generated from coulomb/repo-seed
WP-0001-T003: storage adapter SPI and local filesystem backend
src/artifactstore/storage/:
- spi.py: StorageBackend Protocol (backend_id, put, get, head, delete,
health) and result dataclasses (StorageReceipt, StorageObjectMetadata,
DeletionResult, BackendStatus). ObjectNotFoundError exception type.
- registry.py: backend lookup by string ID (register/get/list_backends/
clear) per ADR-0004.
- backends/local.py: LocalBackend implementation.
* Object layout <root>/<algorithm>/<hex[0:2]>/<hex[2:4]>/<hex>.
* Atomic writes: tmpfile + fsync + rename (idempotent re-puts drain the
stream without rewriting).
* Defence in depth: resolves the final path and asserts it remains under
the configured root.
* Range reads honour HTTP-style inclusive (start, end) tuples.
* health() returns disk usage via shutil.disk_usage and surfaces an
unhealthy status when the root has disappeared.
* delete() cleans up emptied shard directories opportunistically.
tests/unit/test_storage_local.py (14 cases): put/get round-trip; object
key layout matches blueprint; head returns metadata; head/get missing
raise ObjectNotFoundError; put is idempotent; delete returns True then
False; range read returns subrange; range read rejects invalid range;
health reports disk usage; health reports unhealthy when root vanished;
ContentAddress validation blocks path-traversal-flavoured inputs;
registry register/get/list/clear round-trip; idempotent re-put leaves
bytes intact.
Gates: ruff clean, mypy --strict clean on 41 files, 59 tests pass.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,42 @@
|
||||
"""Storage adapter SPI and backend registry.
|
||||
|
||||
The SPI and local filesystem backend land in ARTIFACT-STORE-WP-0001-T003.
|
||||
The S3-compatible backend lands in workplan WP-0004.
|
||||
Backends address bytes by content address (ADR-0001). The SPI is small
|
||||
(``put`` / ``get`` / ``head`` / ``delete`` / ``health``) so swapping or
|
||||
adding adapters never touches the registry or API layers.
|
||||
"""
|
||||
|
||||
from artifactstore.storage.backends.local import LocalBackend
|
||||
from artifactstore.storage.registry import (
|
||||
clear as clear_backends,
|
||||
)
|
||||
from artifactstore.storage.registry import (
|
||||
get as get_backend,
|
||||
)
|
||||
from artifactstore.storage.registry import (
|
||||
list_backends,
|
||||
)
|
||||
from artifactstore.storage.registry import (
|
||||
register as register_backend,
|
||||
)
|
||||
from artifactstore.storage.spi import (
|
||||
BackendStatus,
|
||||
DeletionResult,
|
||||
ObjectNotFoundError,
|
||||
StorageBackend,
|
||||
StorageObjectMetadata,
|
||||
StorageReceipt,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"BackendStatus",
|
||||
"DeletionResult",
|
||||
"LocalBackend",
|
||||
"ObjectNotFoundError",
|
||||
"StorageBackend",
|
||||
"StorageObjectMetadata",
|
||||
"StorageReceipt",
|
||||
"clear_backends",
|
||||
"get_backend",
|
||||
"list_backends",
|
||||
"register_backend",
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user