Skip to main content
The FileHandler class provides thread-safe, atomic file operations for markdown files using POSIX file locks (via portalocker). It implements shared locks for reads and exclusive locks for writes with atomic file replacement.

Class Definition

Constructor

Path
required
Absolute or relative path to the markdown file. The path will be expanded and resolved automatically.

Example

Properties

filepath

Exposes the resolved markdown file path served by this handler.
Path
The resolved absolute path to the markdown file for read and write operations.

Example

Methods

read()

Read markdown content from disk using a shared lock (LOCK_SH). Multiple readers can hold shared locks simultaneously.
str
UTF-8 decoded markdown content currently stored on disk.
This method acquires a shared lock, allowing multiple concurrent reads but blocking if an exclusive write lock is held.
Raises:
  • FileReadError - If the file does not exist, is not valid UTF-8, or cannot be read

Example

write()

Persist markdown content atomically using an exclusive lock (LOCK_EX). Writes to a temporary file, syncs to disk, then atomically replaces the original.
str
required
Full markdown document content to save. This will replace the entire file contents.
bool
Always returns True when content is written and moved into place successfully.
This method replaces the entire file content. The write is atomic - either all content is written or none is (no partial writes).
Raises:
  • FileWriteError - If the temporary file cannot be created, written to, or replaced
Implementation Details:
  1. Acquires exclusive lock (blocks other readers and writers)
  2. Writes content to temporary file in same directory: .{filename}.tmp
  3. Calls fsync() to ensure data is on disk
  4. Atomically replaces original file with os.replace()
  5. Releases lock

Example

get_metadata()

Return current file metadata used by API responses.
dict[str, Any]
File metadata dictionary containing:
  • path (str): Absolute file path as string
  • size_bytes (int): File size in bytes
  • modified_at (float): Last modification timestamp (Unix time)
  • created_at (float): Creation timestamp (Unix time, platform-dependent)
Raises:
  • FileReadError - If the file does not exist or cannot be inspected

Example

Sample Output:

cleanup()

Remove the lock file created by this handler instance. Safe to call multiple times.
This method has best-effort semantics and never raises exceptions. It’s recommended to call this when done with a handler, typically in server shutdown hooks.
Lock File Location: {filename}.md.lock in the same directory as the markdown file.

Example

Exceptions

FileReadError

Raised when reading markdown content fails.
Inherits from RuntimeError. Common scenarios:
  • File does not exist
  • File is not valid UTF-8 text
  • File cannot be read due to permissions or I/O errors

FileWriteError

Raised when writing markdown content fails.
Inherits from RuntimeError. Common scenarios:
  • Failed to acquire exclusive lock
  • Failed to create or write temporary file
  • Failed to replace original file atomically

Complete Usage Example

Lock Behavior

The FileHandler uses POSIX file locks via the portalocker library: Lock File: {filename}.md.lock in the same directory as the target file.
Lock files are created automatically and should be cleaned up with cleanup(). In server mode, the DirectoryHandler calls cleanup() on all cached handlers during shutdown.

Thread Safety

The FileHandler is thread-safe for concurrent reads and writes:
  • Multiple threads can read simultaneously (shared locks)
  • Write operations are serialized (exclusive locks)
  • Atomic writes prevent partial file corruption

Source Reference

See the complete implementation in markdown_os/file_handler.py.