Skip to main content
The base content-store contract. Storage is “where content is stored” — distinct from tai42_contract.template (“what we do with it”, the render mixins). The store exposes the text surface load / list / upload / delete / delete_dir plus the binary/media surface load_bytes / upload_bytes / stat, with the module-level assert_not_root guarding deletes against the store root. The binary methods ship text-bridging defaults so a text-only backend satisfies the whole contract with no new code; binary-native backends override.

ObjectStat

tai42_contract.storage.ObjectStat
Metadata for a stored object, returned by Storage.stat. Carries only content_type — the MIME type consumed here for image/audio media gating (a backend that stores no metadata infers it from the path, a metadata-bearing backend returns the stored type). Frozen: a stat snapshot is a value, not a mutable handle. Attributes

Storage

tai42_contract.storage.Storage
The abstract object-store contract: load, list, upload and delete objects by path. Files and directories share one id space; a backend maps missing objects to FileNotFoundError and colliding paths to StoragePathConflictError.

Members

load

tai42_contract.storage.Storage.load
Return the content at path. Must raise FileNotFoundError when the item does not exist — the manager relies on that exact type to map a missing {% include %} / {% extends %} dependency to Jinja’s TemplateNotFound (so {% include ... ignore missing %} works). Any other failure (auth, network) must propagate as its own error. Parameters

list

tai42_contract.storage.Storage.list
Return every stored object path, in no guaranteed order.

upload

tai42_contract.storage.Storage.upload
Store content at path (create or overwrite). Files and directories share one id space, so an upload whose path collides with the store’s existing shape — an id that still names a non-empty directory, or an id nested beneath an id already held as a file — raises StoragePathConflictError. An id naming an empty leftover directory is free to hold a file. Parameters

delete

tai42_contract.storage.Storage.delete
Delete the object at path, raising FileNotFoundError when it does not exist. A caller wanting idempotent semantics maps that FileNotFoundError to a no-op. Parameters

delete_dir

tai42_contract.storage.Storage.delete_dir
Delete every object under path. Path and existence validation (ValueError / FileNotFoundError) must complete before any deletion begins — once destruction starts, failures surface as other exception types, so callers can treat those two as pre-mutation. Parameters

load_bytes

tai42_contract.storage.Storage.load_bytes
Return the raw bytes at path, with the same FileNotFoundError contract as load. Text-bridge default: reads the text via load and UTF-8 encodes it, so a text-only backend serves bytes for free. A binary-native backend (e.g. S3) overrides to return the stored bytes unaltered. Parameters

upload_bytes

tai42_contract.storage.Storage.upload_bytes
Store raw data at path, optionally tagging content_type. Text-bridge default: STRICT-decodes data as UTF-8 and stores it via upload; content_type is ignored (a text backend keeps no MIME metadata). The decode never passes errors= — non-UTF-8 bytes raise UnicodeDecodeError loudly rather than corrupting the stored content. A binary-native backend overrides to store data and content_type as-is. Parameters

stat

tai42_contract.storage.Storage.stat
Return metadata for the object at path. Path-inference default: guesses content_type from the path suffix via mimetypes.guess_type — it does NOT verify existence (it answers from the path string), so a standalone stat on a metadata-less backend is best-effort metadata; existence for a real read is enforced by the paired load_bytes. A metadata-bearing backend (e.g. S3) overrides to return the stored content-type and maps a missing object to FileNotFoundError. Parameters

StoragePathConflictError

tai42_contract.storage.StoragePathConflictError
An upload id collides with the store’s existing shape. A single id space is shared by files and directories, so one id can name only one of them: an id that still names a directory holding objects cannot also become a file, and an object cannot be stored beneath an id already held as a file. A backend raises this instead of leaking a backend-specific error, so a surface maps it to a 409 conflict. path is the rejected id; conflicts names the stored ids it collides with. Attributes

Members

conflicts_summary

tai42_contract.storage.StoragePathConflictError.conflicts_summary
The conflicting ids as a human string, capped at _MESSAGE_CONFLICT_CAP. Beyond the cap a … and N more tail is appended; the full list stays on conflicts.

assert_not_root

tai42_contract.storage.assert_not_root
Reject a directory path that resolves to the storage root. A blank path, one consisting only of slashes and dots ("", ".", "/", " "), or one whose normalized form escapes to or above the root ("a/..", "./x/../..") would target every stored item, so deleting it is refused. Shared by all storage backends to keep the guard identical. Parameters