ursa.store.backends._obstore¶
obstore-backed ObjectStore implementation.
One class wraps any obstore handle (S3Store for R2, LocalStore, future GCSStore/AzureStore). Responsibilities:
Translate obstore types into Ursa types (
ObjectMeta).Translate obstore exceptions into Ursa errors.
Validate
extra_metadataagainst S3/R2 user-metadata constraints before any network call so callers see typed errors rather than opaque obstore tracebacks.Emit OTel spans on each public op (low-cardinality attrs only — never the caller-supplied
key).
The wrapper does no path composition: prefix scoping lives in the
obstore handle (S3Store(prefix=...), LocalStore(prefix=...)). This
keeps raw_obstore() correct for Lance/Zarr backends that bypass the
wrapper.
Module Contents¶
Classes¶
|
|
Forward-only streaming reader returned by |
|
Adapt an obstore byte-chunk iterator into a forward-only |
Functions¶
Wrap a public method with an OTel span. |
|
Validate |
|
Compose obstore |
|
Translate obstore’s ObjectMeta TypedDict into ours. |
|
Map obstore exceptions to Ursa errors. |
Data¶
API¶
- ursa.store.backends._obstore.__all__¶
[‘ObstoreBackend’]
- ursa.store.backends._obstore._TRACER¶
‘get_tracer(…)’
- ursa.store.backends._obstore._F¶
‘TypeVar(…)’
- ursa.store.backends._obstore._METADATA_KEY_RE: Final¶
‘compile(…)’
- ursa.store.backends._obstore._METADATA_TOTAL_BYTES_LIMIT: Final¶
2048
- ursa.store.backends._obstore._RESERVED_ATTRIBUTES: Final¶
‘frozenset(…)’
- ursa.store.backends._obstore._trace(op: str) Callable[[ursa.store.backends._obstore._F], ursa.store.backends._obstore._F][source]¶
Wrap a public method with an OTel span.
Span attributes are intentionally low-cardinality:
ursa.store.role,ursa.store.backend,ursa.store.op. Keys are not included — they’re unbounded-cardinality and don’t belong in span tags.
- ursa.store.backends._obstore._validate_extra_metadata(extra: collections.abc.Mapping[str, str] | None) None[source]¶
Validate
extra_metadataagainst S3/R2 user-metadata constraints.Raises
InvalidMetadataError(with a specific reason) if any key fails the charset rule or if the combined header size exceeds 2 KiB.
- ursa.store.backends._obstore._build_attributes(*, content_type: str | None, sha256: str | None, extra_metadata: collections.abc.Mapping[str, str] | None) dict[str, str] | None[source]¶
Compose obstore
Attributes(a dict) from the wrapper’s split args.
- ursa.store.backends._obstore._meta_from_obstore(raw: collections.abc.Mapping[str, Any], *, strip_prefix: str = '', sha256: str | None = None) ursa.store.base.ObjectMeta[source]¶
Translate obstore’s ObjectMeta TypedDict into ours.
path(obstore) ->key(ours, prefix-stripped).last_modifiedis normalized to UTC.sha256defaults toNonebecause obstore’s list/head do not surface user metadata; callers that want sha256 from a read must usegetand inspect the result’s attributes.
- ursa.store.backends._obstore._translate_errors() collections.abc.Iterator[None][source]¶
Map obstore exceptions to Ursa errors.
- class ursa.store.backends._obstore.ObstoreBackend(inner: obstore.store.ObjectStore, *, role: str, backend: str, prefix: str = '', supports_attributes: bool = True, supports_etag_match: bool = True, lance_uri: str = '', lance_storage_options: collections.abc.Mapping[str, str] | None = None)[source]¶
ObjectStoreimplementation over an obstore handle.The handle is constructed already-prefix-scoped by the per-backend factories (
backends/r2.py,backends/local.py); this class does no path composition.Initialization
supports_attributes/supports_etag_matchare backend capability flags. obstore’sLocalStoreraisesNotImplementedErrorwhen attributes orUpdateVersionmodes are passed; those backends opt out by setting these toFalse, andputraises a typedInvalidMetadataErrorwith a clear message rather than letting the obstore error leak through.prefixis the same value pushed into the obstore handle. The wrapper records it to strip from result paths because obstore’s S3Store returns prefixed paths fromhead(while LocalStore returns them relative). Stripping here makes the contract consistent.- put(key: str, data: bytes | typing.BinaryIO, *, content_type: str | None = None, sha256: str | None = None, extra_metadata: collections.abc.Mapping[str, str] | None = None, if_none_match: typing.Literal[*] | None = None, if_match: str | None = None) ursa.store.base.ObjectMeta[source]¶
- copy(src: str, dst: str, *, overwrite: bool = False) ursa.store.base.ObjectMeta[source]¶
- head(key: str) ursa.store.base.ObjectMeta[source]¶
- list(prefix: str = '') collections.abc.Iterator[ursa.store.base.ObjectMeta][source]¶
- class ursa.store.backends._obstore._OpenReader(inner: obstore.store.ObjectStore, key: str)[source]¶
Bases:
contextlib.AbstractContextManager[typing.BinaryIO]Forward-only streaming reader returned by
ObjectStore.open().Implemented over
obstore.get(...).stream()rather thanobstore.open_readerbecause the latter has a known prefix-handling bug on S3Store: when the store is constructed withprefix=, the reader double-applies the prefix at request time.get().stream()is consistent across backends.Initialization
- class ursa.store.backends._obstore._NoSeekStreamReader(chunks: Any)[source]¶
Bases:
io.RawIOBaseAdapt an obstore byte-chunk iterator into a forward-only
BinaryIO.Explicitly
seekable() == False;seek()raises so callers can’t silently get wrong bytes.Initialization
Initialize self. See help(type(self)) for accurate signature.