Uh oh!
There was an error while loading. Please reload this page.
feat(datasets): reusable dataset registry + downloader - #40
Conversation
Add a `datasets` subpackage (behind the `datasets` extra) that packages can share instead of each reimplementing pooch-based dataset downloading: - DatasetRegistry / DatasetEntry / FileEntry: declarative YAML registry, supporting both full URLs (e.g. Zenodo) and base_url + s3_key. - Fetcher: pooch download with SHA-256 verification, URL fallback, caching, archive extraction (via FetchContext helpers). - register_loader: pluggable loader registry keyed by the free-form dataset `type` string, so domain loaders (image, spatialdata, visium, ...) are registered by the consuming package. Ships a built-in `anndata` loader. Tests cover registry parsing, URL building, loader dispatch and extraction (no network). Verified end-to-end against a real S3-hosted SpatialData zip. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
for more information, see https://pre-commit.ci
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@## main #40 +/- ##
==========================================
+ Coverage 91.36% 93.60% +2.24%
==========================================
Files 8 11 +3 Lines 440 532 +92 ==========================================
+ Hits 402 498 +96 + Misses 38 34 -4
🚀 New features to boost your workflow:
|
- type register_loader via overloads so the decorator preserves loader types - narrow file() suffix matching for mypy - positional-only ctx in the built-in anndata loader to match the Loader protocol - ignore_missing_imports for stubless optional deps (pooch, anndata, yaml) via a single mypy override Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Multi-file datasets (e.g. a per-sample Visium layout) need to place files in a subdirectory rather than the shared type cache dir. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- add a built-in spatialdata loader (zip -> .zarr -> read_zarr), behind the new 'spatialdata' extra - promote anndata to a core dependency so the anndata loader works out of the box - [datasets] extra is now just the download machinery (pooch, pyyaml, tqdm) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Matches the Loader protocol; the pre-commit mypy hook type-checks tests too (local runs over src/ alone missed it). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…r, retries) - replace the hand-rolled url-fallback loop with pooch.create(...).fetch(), gaining retry_if_failed for free - replace manual shutil.unpack_archive with pooch's Unzip/Untar processors (passed via FetchContext.download(processor=...)) - drop the now-redundant extract_archive helper and module logger - Fetcher gains a 'retries' arg (pooch retry_if_failed, default 3) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- FileEntry.urls() returned a candidate list but only [0] was ever used and pooch.create takes one url per key -> collapse to resolve_url() -> str - remove unused FetchContext.download_all Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Address review feedback (over-engineered): remove DatasetRegistry, Fetcher and FetchContext. Keep the typed FileEntry/DatasetEntry dataclasses; the registry is now a plain dict[str, DatasetEntry] from parse_registry(), and downloading is a fetch() function. Loaders are (entry, target, download, **kwargs) callables. parse_registry folds every YAML key except type/files into entry.metadata, so it no longer hardcodes domain fields (shape/library_id). ~216 -> ~96 lines, 6 classes -> 2 (both pure-data). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Co-authored-by: Philipp A. <flying-sheep@web.de>
Uh oh!
There was an error while loading. Please reload this page.
flying-sheep
left a comment
There was a problem hiding this comment.
test coverage seems quite poor, can you improve that?
flying-sheep
left a comment
There was a problem hiding this comment.
Apart from the above, also needs param docs.
- sphinx_ext: read __scverse_misc_canonical_instance_name__ (the attr the
namespace decorator sets); the old __scverse_misc_namespace_name__ never
existed, so namespace-decorator docstrings were silently never rendered
- datasets: parse_registry drops unknown per-file YAML keys so extras
(e.g. `description`) no longer crash FileEntry(**fd)
- datasets: _load_spatialdata extracts into a per-dataset dir and finds the
store by glob("*.zarr") instead of hardcoding <name>.zarr — decouples from
zip layout, avoids collisions in the shared target
- tests: cover the download closure, both built-in loaders, file(name=...),
extra-key tolerance, and the new glob/error paths (datasets module now 100%)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
flying-sheep
left a comment
There was a problem hiding this comment.
looks great! one small docs problem:
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Co-authored-by: Philipp A. <flying-sheep@web.de>
- parse_registry now warns on (and still drops) unrecognised per-file keys so typos surface, via a small `_file_entry` helper - remove unused `calls["processor"]` capture in test_download_drives_pooch (the FakePup.fetch method itself backs the real pup.fetch call and stays) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Uh oh!
There was an error while loading. Please reload this page.
Replace squidpy's internal pooch-based registry/downloader with the shared scverse_misc.datasets system (scverse-misc[datasets]): - _registry.py: build a scverse_misc DatasetRegistry from datasets.yaml, folding squidpy-specific shape/library_id into the generic metadata mapping. Drops squidpy's duplicated FileEntry/DatasetEntry/DatasetRegistry/DatasetType. - _downloader.py: register squidpy's domain loaders (image -> ImageContainer, visium_10x -> read.visium, spatialdata -> read_zarr) via register_loader and override the built-in anndata loader for the shape warning. The pooch download/verify/extract machinery now lives in scverse-misc. - _datasets.py: public API unchanged; type dispatch uses plain strings. - pyproject: drop direct pooch dep (now via scverse-misc[datasets]). Net ~750 lines deleted. Public API (sq.datasets.*) is unchanged. Depends on scverse/scverse-misc#40. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…wnloader (#1213) * refactor(datasets): use scverse-misc dataset registry + downloader Replace squidpy's internal pooch-based registry/downloader with the shared scverse_misc.datasets system (scverse-misc[datasets]): - _registry.py: build a scverse_misc DatasetRegistry from datasets.yaml, folding squidpy-specific shape/library_id into the generic metadata mapping. Drops squidpy's duplicated FileEntry/DatasetEntry/DatasetRegistry/DatasetType. - _downloader.py: register squidpy's domain loaders (image -> ImageContainer, visium_10x -> read.visium, spatialdata -> read_zarr) via register_loader and override the built-in anndata loader for the shape warning. The pooch download/verify/extract machinery now lives in scverse-misc. - _datasets.py: public API unchanged; type dispatch uses plain strings. - pyproject: drop direct pooch dep (now via scverse-misc[datasets]). Net ~750 lines deleted. Public API (sq.datasets.*) is unchanged. Depends on scverse/scverse-misc#40. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(datasets): use scverse-misc's built-in spatialdata loader scverse-misc now ships a generic spatialdata loader, so squidpy no longer needs its own; it registers only its domain loaders (image, visium_10x) plus the anndata shape-warning override. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(datasets): consistent <datasetdir>/<type>/ cache layout for visium Drop the redundant 'visium' prefix in visium() so downloads land in <datasetdir>/visium_10x/<sample>/ like every other type (was doubly nested under visium/visium_10x). Update the hires-image path assertion accordingly. Verified: all @internet datasets tests pass (8 passed). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(ci): update prefetch script for the scverse-misc registry registry.{anndata,image,spatialdata}_datasets were squidpy's old registry properties, removed in the migration. Use dataset_names(type) instead. Visium samples now cache to <datasetdir>/visium_10x/<sample>/ via the public API. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(datasets): track scverse-misc slim API (parse_registry + fetch) scverse-misc dropped its DatasetRegistry/Fetcher/FetchContext classes for a typed data model + functions. Adapt: - _registry: parse_registry() -> (base_url, dict[str, DatasetEntry]); get_registry() returns the dict, get_base_url() the base. shape/library_id/doc_header now live in entry.metadata. - _downloader: loaders are (entry, target, download, **kwargs); DatasetDownloader wraps fetch(); visium uses pooch.Untar instead of a manual tarfile loop. - _datasets/tests updated to the dict + metadata shape. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * refactor(datasets): collapse downloader wrapper, fix pooch/pyyaml deps - replace DatasetDownloader class + get_downloader singleton + module wrapper with a single download() over scverse_misc.datasets.fetch - restore pooch as a direct dependency (used via pooch.Untar in the visium loader); drop now-unused direct pyyaml dependency - drop redundant visium() name validation, keeping its specific message * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix(datasets): address review feedback on scverse-misc migration - visium(): guard against the visium_10x names specifically so a valid-but- wrong-type name (e.g. "imc") fails with a clear error instead of dying deep in the anndata loader with an "unexpected keyword argument" TypeError - get_registry(): return a read-only MappingProxyType so the lru_cached singleton can't be mutated by callers; rename _parsed() -> _load() - cap scverse-misc[datasets] to <0.2 (0.1.x API still churning) - document the <datasetdir>/<type>/ cache-path change (visium/->visium_10x/, images/->image/) in the release notes - restore the include_hires_tiff metadata-toggle test; add offline regression tests for the visium guard; comment the intentional global anndata override * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * chore(datasets): drop notes-dev.md changelog entry (no longer used) * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Selman Özleyen <32667648+selmanozleyen@users.noreply.github.com>
flying-sheep
commented
Aug 21, 2026
Because I was interested: the ❯ pipdeptree --python=(^hatch env find hatch-test.stable | path join bin/python) -r -p pyyaml,scanpy[dask]PyYAML==6.0.3┣━━ dask==2026.7.1 [requires: PyYAML>=5.4.1]┃ ┗━━ scanpy==1.14.0.dev57+g41165bfc9 [requires: dask>=2024.10, extra: dask]┗━━ donfig==0.8.1.post1 [requires: PyYAML] ┗━━ zarr==3.2.1 [requires: donfig>=0.8] ┣━━ anndata==0.13.2 [requires: zarr>=3.1] ┃ ┗━━ scanpy==1.14.0.dev57+g41165bfc9 [requires: anndata>=0.12.14, extra: dask] ┗━━ scanpy==1.14.0.dev57+g41165bfc9 [requires: zarr>=3.2] |
Motivation
Several scverse packages each reimplement the same thing: a registry of downloadable datasets + a pooch-based downloader with hash verification. squidpy, scanpy and pertpy all roll their own; there is no shared, reusable building block. This PR adds one to
scverse-miscso packages can drop their bespoke infra and register only their domain-specific loaders.