Make ObjectStore backend generic over any obspec store - #3698
kylebarron wants to merge 4 commits into
Conversation
|
+1 Xarray could make use of this. (When reading non-icechunk stores - icechunk already has a caching layer) |
|
I'm not sure where the other locations are where I need to define obspec as a dependency |
d-v-b
left a comment
There was a problem hiding this comment.
looks good, just need a release note in changes
|
As discussed in zarr-developers/VirtualiZarr#1092, this would also enable users to use the |
|
sounds good, do you want me to resolve the conflicts? |
|
with the conflicts resolved there are some blockers, which I will summarize here:
i'm happy to deal with these |
|
If you'd like to make updates, I can review. I have a lot going on/catching up on at the moment |
Conflicts in pyproject.toml, .pre-commit-config.yaml and src/zarr/storage/_obstore.py resolved by taking main's versions; the obspec changes are re-applied on top in the following commit.
ObjectStore now accepts any object implementing the async obspec protocols instead of only obstore store classes: - obspec is imported only for type checking, plus a lazy import for exception mapping, so importing zarr.storage no longer requires it. Constructing an ObjectStore fails fast with an ImportError if obspec is missing. - The constructor check is structural: it verifies the eight obspec methods are present rather than requiring an "obstore" module name. - Every catch site (get, exists, set_if_not_exists, delete, getsize, and the suffix-range fallback) matches errors through obspec's well-known names, and unhandled errors are re-raised unchanged instead of as obspec copies. getsize translates a store's own NotFoundError into the FileNotFoundError the Store contract promises. - The suffix-range workaround for stores without suffix support is shared between get and get_partial_values. - Tests run the full StoreTests suite against a pure-Python in-memory obspec store whose exceptions do not derive from the builtins, plus tests for wrapping an obstore store, the structural check, the missing obspec error, and error pass-through. obstore's minimum version rises to 0.7.0 (buffer_async) and the remote extra also installs obspec. Assisted-by: ClaudeCode:claude-fable-5-1 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #3698 +/- ##
==========================================
+ Coverage 94.34% 94.44% +0.10%
==========================================
Files 92 92
Lines 12935 12941 +6
==========================================
+ Hits 12203 12222 +19
+ Misses 732 719 -13
🚀 New features to boost your workflow:
|
|
@kylebarron have a look, i fixed the conflicts and the issues I mentioned |
In #1661 we merged an Obstore-based backend. It sounds like the Obstore backend has become very popular, and @maxrjones found that, at least in some situations, the Obstore backend can be significantly faster.
One problem with the Obstore backend, however, is that it's strictly tied to Obstore. The type hinting and runtime behavior all require exact instances from the Obstore package.
But very often people might want to insert some middleware.
The goal of Obspec is to define generic protocols to cleanly enable this. As described in the initial release post from last summer, Obspec should allow downstream libraries to depend on a protocol-based API that works with any implementation that provides the given signature.
E.g. if you think of the simplest pseudocode example of a cache:
Then if a function expects an object implementing
GetRange:Then now you can pass in either the raw obstore backend or the backend wrapped by the cache:
This architecture is much more tractable for end users than if Obstore implemented its own caching natively. Since users have full access to the cache (i.e. it isn't hidden away inside Rust), users can check methods of
SimpleCacheto track how much memory the cache is using and to manually evict cache items if they wanted.@maxrjones has been starting to collect utilities around obspec in https://github.com/virtual-zarr/obspec-utils.
This is backwards-compatible (at least if you ignore the obstore version bump from 0.5.1 — released March 2025 — to 0.7.0 — released June 2025).
Implementation notes
Exceptionsin the obspec docs, the workaround I chose is to use well-defined names, andmap_exceptionwill convert any external exceptions to exceptions subclassing fromobspec.exceptions.TODO:
docs/user-guide/*.mdchanges/