From 552274af8778efb04c792d424d94531eb76666c4 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 27 Sep 2026 19:10:08 +0000 Subject: [PATCH] Drop pynwb < 3.1 compatibility code from pynwb_utils The declared floor is pynwb >= 3.1.0 (and nwbinspector >= 0.7.0 requires pynwb >= 3.1 anyway), which makes several version guards unreachable: - validate(): only the `pynwb.validate(path=...)` branch (pynwb >= 3.0) can run; the `paths=[...]` tuple-returning form (2.2 to 2.x) and the `io=` fallback for even older releases are gone. - copy_nwb_file(): `cache_spec` has been accepted by `export()` since pynwb 2.8.2, so pass it unconditionally. - _get_external_images(): `ExternalImage` exists in every supported pynwb, so import it at module level instead of guarding an ImportError. The now-unused `packaging.version.Version` import is removed as well. The NWB *schema* version checks (e.g. the < 2.1.0 error filter) are about the file being validated, not about pynwb, and are unchanged. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01HFhMPs6vF5zyeHDvPTvRcA --- dandi/pynwb_utils.py | 30 +++++++----------------------- 1 file changed, 7 insertions(+), 23 deletions(-) diff --git a/dandi/pynwb_utils.py b/dandi/pynwb_utils.py index 80fba2a20..297df05f3 100644 --- a/dandi/pynwb_utils.py +++ b/dandi/pynwb_utils.py @@ -27,9 +27,9 @@ import h5py import hdmf import numpy as np -from packaging.version import Version import pynwb from pynwb import NWBHDF5IO +from pynwb.base import ExternalImage import semantic_version from . import __version__, get_logger @@ -450,12 +450,6 @@ def _get_external_images(nwb: pynwb.NWBFile) -> list[dict]: list of dicts : [{id: , name: , external_files=[ExternalImage.data], field: "data"}] """ - try: - from pynwb.base import ExternalImage - except ImportError: - # `ExternalImage` was added in pynwb 3.1.0; nothing to collect on an older one. - return [] - out = [] for ob in nwb.objects.values(): if not isinstance(ob, ExternalImage) or ob.data is None: @@ -685,16 +679,9 @@ def validate(path: str | Path, devel_debug: bool = False) -> list[ValidationResu ) try: - if Version(pynwb.__version__) >= Version("3.0.0"): - error_outputs = pynwb.validate(path=path) - elif Version(pynwb.__version__) >= Version( - "2.2.0" - ): # Use cached namespace feature - # argument get_cached_namespaces is True by default - error_outputs, _ = pynwb.validate(paths=[path]) - else: # Fallback if an older version - with pynwb.NWBHDF5IO(path=path, mode="r", load_namespaces=True) as reader: - error_outputs = pynwb.validate(io=reader) + # Validates against the namespaces cached in the file; pynwb falls back + # to its own namespaces if the file has none cached + error_outputs = pynwb.validate(path=path) except Exception as exc: if devel_debug: raise @@ -808,18 +795,15 @@ def copy_nwb_file(src: str | Path, dest: str | Path) -> str: dest = op.join(dest, op.basename(src)) else: os.makedirs(op.dirname(dest), exist_ok=True) - kws = {} - if Version(pynwb.__version__) >= Version("2.8.2"): - # we might make it leaner by not caching the spec if original - # file did not have it. Possible only since 2.8.2.dev11 - kws["cache_spec"] = bool(pynwb.NWBHDF5IO.get_namespaces(src)) with pynwb.NWBHDF5IO(src, "r") as ior, pynwb.NWBHDF5IO(dest, "w") as iow: data = ior.read() data.generate_new_id() iow.export( ior, nwbfile=data, - **kws, + # keep the copy leaner by not caching the spec if the original + # file did not have it cached either + cache_spec=bool(pynwb.NWBHDF5IO.get_namespaces(src)), ) return str(dest)