Repository navigation
[dev] Use datalad-fuse's remfile backend and all other dev branches - #1957
Closed
CodyCBakerPhD wants to merge 41 commits into
Closed
CodyCBakerPhD wants to merge 41 commits into
CodyCBakerPhD wants to merge 41 commits into
Conversation
Add `dandi.support.datalad_fuse.AnnexedReadableFile`, a `Readable` for a locked annexed file whose content is not present locally, streamed with the adapter of datalad-fuse (no FUSE mount), which asks git-annex where the content is. It is available with the new `datalad` extra. Cache NWB metadata of locked annexed files, streamed or local, under their git-annex key paired with their path, with fscacher 0.5.0's `annex_key_fingerprint`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qn5WBSiQgoZoL4fytF6nEr
Add a `stream` policy to `--missing-file-content`: the content of annexed files that is not present locally (as in a DataLad clone of a Dandiset) is streamed with datalad-fuse's adapter, so that pynwb and nwbinspector run on it without it being downloaded. pynwb validation results are cached under the files' git-annex keys. This is the validation integration of #1933, on top of the datalad-fuse readable rather than a git-only reader of the git-annex branch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qn5WBSiQgoZoL4fytF6nEr
annex_fingerprint is not specific to NWB files, and living in dandi.support.datalad_fuse it needs no deferred import. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qn5WBSiQgoZoL4fytF6nEr
…am-via-datalad-fuse Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qn5WBSiQgoZoL4fytF6nEr
As documented in datalad/datalad-fuse#138. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qn5WBSiQgoZoL4fytF6nEr
…am-via-datalad-fuse
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qn5WBSiQgoZoL4fytF6nEr
With the development version of datalad-fuse from datalad/datalad-fuse#131, use its RemoteFilesystemAdapter, which reads HDF5-based files such as NWB with remfile if installed (fsspec otherwise), and look up S3 exports for content without a registered URL. The released FsspecAdapter is still used with datalad-fuse 0.5.1. Test it in a new datalad-dev CI job. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qn5WBSiQgoZoL4fytF6nEr
fscacher imports joblib, which imports numpy, so importing it with the module made the CLI (via dandi.validate in the stacked PR) import numpy, failing test_no_heavy_imports. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qn5WBSiQgoZoL4fytF6nEr
…am-via-datalad-fuse Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qn5WBSiQgoZoL4fytF6nEr
…talad-fuse-remfile Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qn5WBSiQgoZoL4fytF6nEr
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## claude/validate-stream-via-datalad-fuse #1957 +/- ##
============================================================================
+ Coverage 66.82% 91.14% +24.32%
============================================================================
Files 94 94
Lines 14835 14866 +31
============================================================================
+ Hits 9913 13550 +3637
+ Misses 4922 1316 -3606
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qn5WBSiQgoZoL4fytF6nEr
…am-via-datalad-fuse Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qn5WBSiQgoZoL4fytF6nEr
…talad-fuse-remfile Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qn5WBSiQgoZoL4fytF6nEr
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qn5WBSiQgoZoL4fytF6nEr
h5py holds its global lock while reading from a Python file object, and the read waits for another thread to fetch the data over HTTP (fsspec's I/O thread, or an HTTP server in the same process as in the tests). A garbage collection in that thread could then run the finalizer of an unreferenced h5py-backed object (e.g., hdmf's HDF5IO.__del__), which waits for the same lock: a deadlock, as seen in CI in test_validate_stream, ended by pytest-timeout with a segfault. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qn5WBSiQgoZoL4fytF6nEr
This reverts commit d18843d. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qn5WBSiQgoZoL4fytF6nEr
…to claude/validate-stream-via-datalad-fuse
…talad-fuse-remfile Resolve the import conflict, and check the type of the file object wrapped by the one that pauses garbage collection in the remfile test. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qn5WBSiQgoZoL4fytF6nEr
CodyCBakerPhD
added this pull request to stack #1959
October 12, 2026 21:30
Add `dandi.support.datalad_fuse.AnnexedReadableFile`, a `Readable` for a locked annexed file whose content is not present locally, streamed with the adapter of datalad-fuse (no FUSE mount), which asks git-annex where the content is. It is available with the new `datalad` extra. Cache NWB metadata of locked annexed files, streamed or local, under their git-annex key paired with their path, with fscacher 0.5.0's `annex_key_fingerprint`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qn5WBSiQgoZoL4fytF6nEr
annex_fingerprint is not specific to NWB files, and living in dandi.support.datalad_fuse it needs no deferred import. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qn5WBSiQgoZoL4fytF6nEr
As documented in datalad/datalad-fuse#138. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qn5WBSiQgoZoL4fytF6nEr
fscacher imports joblib, which imports numpy, so importing it with the module made the CLI (via dandi.validate in the stacked PR) import numpy, failing test_no_heavy_imports. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qn5WBSiQgoZoL4fytF6nEr
h5py holds its global lock while reading from a Python file object, and the read waits for another thread to fetch the data over HTTP (fsspec's I/O thread, or an HTTP server in the same process as in the tests). A garbage collection in that thread could then run the finalizer of an unreferenced h5py-backed object (e.g., hdmf's HDF5IO.__del__), which waits for the same lock: a deadlock, as seen in CI in test_validate_stream, ended by pytest-timeout with a segfault. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qn5WBSiQgoZoL4fytF6nEr
Add a `stream` policy to `--missing-file-content`: the content of annexed files that is not present locally (as in a DataLad clone of a Dandiset) is streamed with datalad-fuse's adapter, so that pynwb and nwbinspector run on it without it being downloaded. pynwb validation results are cached under the files' git-annex keys. This is the validation integration of #1933, on top of the datalad-fuse readable rather than a git-only reader of the git-annex branch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qn5WBSiQgoZoL4fytF6nEr
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qn5WBSiQgoZoL4fytF6nEr
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qn5WBSiQgoZoL4fytF6nEr
This reverts commit d18843d. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qn5WBSiQgoZoL4fytF6nEr
CodyCBakerPhD
force-pushed
the
claude/validate-stream-via-datalad-fuse
branch
from
October 12, 2026 21:32
0be1c28 to
106f661
Compare
…talad-fuse-remfile Bring in the rebased base branch. The conflicting files are identical in the old and the rebased base, so they keep this branch's versions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qn5WBSiQgoZoL4fytF6nEr
datalad-fuse opens the files with fsspec's defaults: 5 MiB of read-ahead, discarded whenever a read jumps elsewhere. h5py reads many small pieces scattered over an NWB file, so each open downloaded about as much as the whole file (64 MB for the neurodata types of a 16 MB file in the new test), and dandi opens each file several times during validation. Read in cached blocks of 256 KiB instead (1.3 MB for the same file). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qn5WBSiQgoZoL4fytF6nEr
…to claude/validate-stream-via-datalad-fuse
…ad-fuse' into claude/datalad-fuse-remfile
With datalad/datalad-fuse#131 and remfile installed, remfile reads HDF5 files, with its own read-ahead (chunks growing up to 100 MiB) that datalad-fuse does not let us configure. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qn5WBSiQgoZoL4fytF6nEr
fsspec's "blockcache" fetches each block of a read with its own request, so a large read (e.g., of a dataset's data, by NWB Inspector) took one round trip per 256 KiB, i.e., a couple of MB/s however fast the connection. Use instead a block cache that fetches each run of consecutive missing blocks with a single request, and keeps up to 512 blocks (128 MiB) per open file rather than 32, which h5py's scattered reads of metadata kept evicting and fetching again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qn5WBSiQgoZoL4fytF6nEr
…to claude/validate-stream-via-datalad-fuse
The results of inspecting an NWB file are cached in the validation cache, under the file (by stat() for a local file, or by its git-annex key for streamed content; see `annex_fingerprint`) and the version of nwbinspector, so that validating again does not inspect (and for streamed content, download) unchanged files again. A failure to read streamed content, which might be transient, is not cached. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qn5WBSiQgoZoL4fytF6nEr
Validating a streamed file mostly waits for its content to be downloaded, one request at a time (h5py serializes all reading of HDF5 files in a process), so validate NWB files (other than those of BIDS datasets, which are validated together) in a pool of processes, started with "spawn" since this process may run threads (e.g., fsspec's). The results are yielded in the same order as without the pool; the workers' log records are handled by the handlers of the main process. Streamed content is set up again in the worker, as its source cannot be sent to another process. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qn5WBSiQgoZoL4fytF6nEr
…ad-fuse' into claude/datalad-fuse-remfile
Validating streamed content can take a long time per file, during which
nothing was shown. Find all the files to validate first, log their
number, and log for each file when it is done ("[3/120] sub-01/...nwb:
2 issues"), including for the files validated in parallel with --jobs,
as they finish.
The `dataset_description.json` files of BIDS datasets, to which the
assets found refer only weakly, are kept alive while their assets are
validated.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qn5WBSiQgoZoL4fytF6nEr
…ad-fuse' into claude/datalad-fuse-remfile
CodyCBakerPhD
force-pushed
the
claude/validate-stream-via-datalad-fuse
branch
from
October 15, 2026 17:58
a65f6ee to
336ec0b
Compare
…ad-fuse' into claude/datalad-fuse-remfile # Conflicts: # .github/workflows/run-tests.yml # dandi/support/datalad_fuse.py # dandi/support/tests/test_datalad_fuse.py # docs/source/cmdline/validate.rst
CodyCBakerPhD
removed this pull request from stack #1959
October 15, 2026 18:34
This was referenced Oct 15, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #1956. This tries the development version of datalad-fuse from datalad/datalad-fuse#131, which adds pluggable backends, including remfile for HDF5-based files.
Changes
get_adapter()usesdatalad_fuse.adapter.RemoteFilesystemAdapter(dandiset.yaml: should become READONLY locally, with a comment, no metadata uploaded from it #131) when it is available, and falls back to the releaseddatalad_fuse.fsspec.FsspecAdapter(0.5.1) otherwise.remfileinstalled, NWB and other HDF5 files are read through remfile; other files still go through fsspec.datalad.fusefs.backendssetting.get_annexed_readable()also tries the S3 exporttree URLs from dandiset.yaml: should become READONLY locally, with a comment, no metadata uploaded from it #131 when git-annex lists no URL, for older datasets that lack versioned S3 URLs.validatedocs.test_annexed_readable_remfilechecks that an NWB file is opened through datalad-fuse'sRemfileWrapperand read with h5py over the local HTTP range server. It is skipped unless both dandiset.yaml: should become READONLY locally, with a comment, no metadata uploaded from it #131 andremfileare installed.datalad-devjob installs git-annex,remfileand datalad-fuse fromrefs/pull/131/head. The existingdataladjob keeps testing the PyPI release.Locally, the datalad-fuse and
validatestream tests pass with both datalad-fuse 0.5.1 (where the remfile test is skipped) and #131 plus remfile.Once #131 is released, the CI install can switch to that release and the
dataladextra's lower bound can be raised.Follow-up branch: remfile instead of the workarounds
Branch
claude/use-dev-datalad-fuse-remfile(c9b2d02) builds on this PR. It has no PR of its own yet. It reads NWB and other HDF5-based files with datalad-fuse's remfile backend instead of fsspec with a custom cache. It uses theremfile-devbranch of CodyCBakerPhD/datalad-fuse, which is datalad/datalad-fuse#131 plusinit_annexfrom CodyCBakerPhD/datalad-fuse#4.dandi/support/datalad_fuse.py_coalescing_block_cache()and_read_in_small_blocks()annex_initialized()init_annex=Falseandannex_initializedin datalad-fuse_GCPausedFilefor every streamed file_GCPausedFileonly for files read with fsspecWhy
_GCPausedFilestays for fsspec, and why the h5py fork is not used:defer-dealloc-when-locked) still deadlocks. In fsspec's I/O thread, a garbage collection runsHDMFIO.__del__(hdmf 6.2.0). That callsis_open(), i.e.bool(h5py.File), which waits for h5py's lock, held by the reading thread.CI on the follow-up branch:
dataladextra.datalad-devjob installs theremfile-devbranch.dataladjob keeps the released datalad-fuse, where streaming is skipped.Results so far (local only):
Tests: the datalad-fuse,
validateandcmd_validatetests pass:Real data: the 53 NWB files of 000006 with
-J 8and stock h5py 3.16 gave the same results as the fsspec path. Nothing was downloaded into the clone.For HDF5 files, remfile trades some bandwidth for not needing a custom cache, a garbage collection pause or an h5py patch.
🤖 Generated with Claude Code
https://claude.ai/code/session_01Qn5WBSiQgoZoL4fytF6nEr
Generated by Claude Code
Generated by Claude Code