File tree Expand file tree Collapse file tree
Expand file tree Collapse file tree Original file line number Diff line number Diff line change 1+ # 0.74.3 (Sat Feb 14 2026)
2+
3+ #### 🐛 Bug Fix
4+
5+ - bf: pass aiohttp timeouts to fsspec to fix test hang [ #1795 ] ( https://github.com/dandi/dandi-cli/pull/1795 ) ([ @yarikoptic ] ( https://github.com/yarikoptic ) )
6+ - Enhance dandiset metadata error messages [ #1790 ] ( https://github.com/dandi/dandi-cli/pull/1790 ) ([ @yarikoptic ] ( https://github.com/yarikoptic ) [ @yarikoptic-gitmate ] ( https://github.com/yarikoptic-gitmate ) )
7+ - Fix type annotation in upload sync path prefix calculation [ #1794 ] ( https://github.com/dandi/dandi-cli/pull/1794 ) ([ @yarikoptic ] ( https://github.com/yarikoptic ) [ @yarikoptic-gitmate ] ( https://github.com/yarikoptic-gitmate ) )
8+ - Fix macOS-15-intel CI failures: h5py and opencv-python regressions [ #1783 ] ( https://github.com/dandi/dandi-cli/pull/1783 ) ([ @yarikoptic ] ( https://github.com/yarikoptic ) )
9+
10+ #### 📝 Documentation
11+
12+ - Add module docstrings to validation and NWB utilities [ #1789 ] ( https://github.com/dandi/dandi-cli/pull/1789 ) ([ @yarikoptic ] ( https://github.com/yarikoptic ) [ @yarikoptic-gitmate ] ( https://github.com/yarikoptic-gitmate ) )
13+
14+ #### Authors: 2
15+
16+ - GitMate for @yarikoptic ([ @yarikoptic-gitmate ] ( https://github.com/yarikoptic-gitmate ) )
17+ - Yaroslav Halchenko ([ @yarikoptic ] ( https://github.com/yarikoptic ) )
18+
19+ ---
20+
121# 0.74.2 (Fri Jan 30 2026)
222
323#### 🐛 Bug Fix
Original file line number Diff line number Diff line change @@ -42,7 +42,11 @@ def __init__(
4242 if not allow_empty and not os .path .lexists (
4343 self .path_obj / dandiset_metadata_file
4444 ):
45- raise ValueError (f"No dandiset at { path } " )
45+ raise ValueError (
46+ f"No dandiset at { path } . "
47+ f"The directory does not contain a '{ dandiset_metadata_file } ' file. "
48+ "Use 'dandi download' to download a dandiset or check the path."
49+ )
4650 self .metadata : dict | None = None
4751 self ._metadata_file_obj = self .path_obj / dandiset_metadata_file
4852 self ._load_metadata ()
@@ -139,11 +143,17 @@ def _get_identifier(metadata: dict) -> str | None:
139143 @property
140144 def identifier (self ) -> str :
141145 if self .metadata is None :
142- raise ValueError ("No metadata record found in Dandiset" )
146+ raise ValueError (
147+ f"No metadata record found in Dandiset at { self .path } . "
148+ f"The '{ dandiset_metadata_file } ' file may be empty or corrupted. "
149+ "Use 'dandi download' to re-download the dandiset metadata."
150+ )
143151 id_ = self ._get_identifier (self .metadata )
144152 if not id_ :
145153 raise ValueError (
146- f"Found no dandiset.identifier in metadata record: { self .metadata } "
154+ f"Found no dandiset.identifier in metadata record. "
155+ f"The '{ dandiset_metadata_file } ' file must contain an 'identifier' field. "
156+ f"Metadata: { self .metadata } "
147157 )
148158 return id_
149159
Original file line number Diff line number Diff line change 1+ """Delete assets and dandisets from DANDI Archive.
2+
3+ This module provides functionality for deleting assets and entire dandisets
4+ from DANDI Archive instances. It supports:
5+ - Single and batch asset deletion
6+ - Dandiset deletion with confirmation
7+ - URL-based and path-based deletion
8+ - Skip-missing option for non-existent resources
9+ """
10+
111from __future__ import annotations
212
313from collections .abc import Iterable , Iterator
Original file line number Diff line number Diff line change 1+ """Download assets from DANDI Archive.
2+
3+ This module provides functionality for downloading files and Zarr archives
4+ from DANDI Archive instances. It supports:
5+ - Individual file downloads with integrity verification
6+ - Zarr archive downloads with parallel entry handling
7+ - Resume capability for interrupted downloads
8+ - Progress tracking and error recovery
9+ """
10+
111from __future__ import annotations
212
313from collections import Counter , deque
Original file line number Diff line number Diff line change @@ -345,10 +345,27 @@ def open(self) -> IO[bytes]:
345345 # Optional dependency:
346346 import fsspec
347347
348+ from aiohttp import ClientTimeout
349+
348350 # We need to call open() on the return value of fsspec.open() because
349351 # otherwise the filehandle will only be opened when used to enter a
350352 # context manager.
351- return cast (IO [bytes ], fsspec .open (self .url , mode = "rb" ).open ())
353+ #
354+ # Pass explicit timeouts to aiohttp to prevent indefinite hangs in
355+ # fsspec's sync() wrapper. Without these, a stalled connection to S3
356+ # (or minio in tests) causes fsspec's background IO thread to block
357+ # forever, which in turn blocks the calling thread in
358+ # threading.Event.wait() — see https://github.com/fsspec/filesystem_spec/issues/1666
359+ return cast (
360+ IO [bytes ],
361+ fsspec .open (
362+ self .url ,
363+ mode = "rb" ,
364+ client_kwargs = {
365+ "timeout" : ClientTimeout (total = 120 , sock_read = 60 , sock_connect = 30 )
366+ },
367+ ).open (),
368+ )
352369
353370 def get_size (self ) -> int :
354371 return self .size
Original file line number Diff line number Diff line change 1+ """Move and rename assets in DANDI Archive.
2+
3+ This module provides functionality for moving and renaming assets both
4+ locally and remotely in DANDI Archive instances. Features include:
5+ - Local file reorganization
6+ - Remote asset path changes
7+ - Combined local and remote moves
8+ - Conflict resolution (skip, overwrite, error)
9+ - Validation of move operations
10+ """
11+
112from __future__ import annotations
213
314from abc import ABC , abstractmethod
Original file line number Diff line number Diff line change 1- """
2- ATM primarily a sandbox for some functionality for dandi organize
1+ """Organize and structure NWB files according to DANDI conventions.
2+
3+ This module provides functionality for organizing neuroscience data files
4+ according to DANDI's file organization schema. Features include:
5+ - Automatic path generation from metadata
6+ - BIDS-like subject/session organization
7+ - Metadata-driven file naming
8+ - Validation of organized paths
9+ - Support for videos and generic files
310"""
411
512from __future__ import annotations
Original file line number Diff line number Diff line change 1+ """Utilities for working with NWB (Neurodata Without Borders) files.
2+
3+ This module provides helper functions for reading, validating, and extracting
4+ metadata from NWB files using PyNWB. Features include:
5+ - NWB file I/O with caching
6+ - Metadata extraction for DANDI schema
7+ - Version compatibility checking
8+ - External link detection
9+ - Validation against NWB standards
10+ """
11+
112from __future__ import annotations
213
314from collections import Counter
Original file line number Diff line number Diff line change @@ -1145,6 +1145,7 @@ def test_nwb2asset(simple2_nwb: Path) -> None:
11451145 )
11461146
11471147
1148+ @pytest .mark .timeout (120 )
11481149@pytest .mark .xfail (reason = "https://github.com/dandi/dandi-cli/issues/1450" )
11491150def test_nwb2asset_remote_asset (nwb_dandiset : SampleDandiset ) -> None :
11501151 pytest .importorskip ("fsspec" )
Original file line number Diff line number Diff line change 1+ """Upload assets to DANDI Archive.
2+
3+ This module handles uploading NWB files and other assets to DANDI Archive
4+ instances. Features include:
5+ - Validation of files before upload
6+ - Progress tracking with resume capability
7+ - Metadata extraction and assignment
8+ - BIDS validation integration
9+ - Concurrent uploads with thread pool
10+ """
11+
112from __future__ import annotations
213
314from collections import defaultdict
415from collections .abc import Iterator , Sequence
516from contextlib import ExitStack
617from enum import Enum
7- from functools import reduce
818import io
919import os .path
1020from pathlib import Path
@@ -493,7 +503,7 @@ def upload_agg(*ignored: Any) -> str:
493503 for p in paths :
494504 rp = os .path .relpath (p , dandiset .path )
495505 relpaths .append ("" if rp == "." else rp )
496- path_prefix = reduce ( os .path .commonprefix , relpaths ) # type: ignore[arg-type]
506+ path_prefix = os .path .commonprefix ( relpaths )
497507 to_delete = []
498508 for asset in remote_dandiset .get_assets_with_path_prefix (path_prefix ):
499509 if any (
You can’t perform that action at this time.
0 commit comments