From 3e28dfb50971dc7b35a39de96fb2e31459afa699 Mon Sep 17 00:00:00 2001 From: Ray Osborn Date: Thu, 4 Jun 2026 12:08:35 -0500 Subject: [PATCH 1/2] Include the offending key in NXgroup path-error messages NeXusError messages for invalid indices and invalid paths in NXgroup now interpolate the key or resolved path, so the caller can see which lookup failed instead of just "Invalid path" / "Invalid index". Co-Authored-By: Claude Opus 4.7 (1M context) --- src/nexusformat/nexus/tree.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/nexusformat/nexus/tree.py b/src/nexusformat/nexus/tree.py index 6d86c8e..98604cb 100644 --- a/src/nexusformat/nexus/tree.py +++ b/src/nexusformat/nexus/tree.py @@ -4886,7 +4886,7 @@ def __getitem__(self, key): try: path = PurePath(str(key)) except TypeError: - raise NeXusError("Invalid index") + raise NeXusError(f"'{key}' is an invalid index") if path.is_absolute(): node = self.nxroot path = path.relative_to('/') @@ -4896,7 +4896,7 @@ def __getitem__(self, key): try: node = node.entries[name] except KeyError: - raise NeXusError("Invalid path") + raise NeXusError(f"'{path}' is an invalid path") return node def __setitem__(self, key, value): @@ -5007,7 +5007,7 @@ def __delitem__(self, key): if name in group: group = group[name] else: - raise NeXusError("Invalid path") + raise NeXusError(f"'{key}' is an invalid path") if key not in group: raise NeXusError("'"+key+"' not in "+group.nxpath) elif group[key].is_linked(): From 212cbbb1a0d4cad961afaf8e624f69e869f43746 Mon Sep 17 00:00:00 2001 From: Ray Osborn Date: Thu, 4 Jun 2026 12:09:05 -0500 Subject: [PATCH 2/2] Release file lock even if h5py close raises NXFile.close() called release_lock() unconditionally only if the preceding self._file.close() did not raise; a flush error during close would therefore strand the .lock file on disk and block every subsequent acquire until the 8-hour expiry kicked in. NXFile.__exit__ had the same shape: a raising close() left _with_count stuck at 1, so later with-blocks on the same NXFile would skip open() entirely and never retry the close. Wrap both in try/finally so the lock is always released and the counter always decrements. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/nexusformat/nexus/tree.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/nexusformat/nexus/tree.py b/src/nexusformat/nexus/tree.py index 98604cb..2876ba3 100644 --- a/src/nexusformat/nexus/tree.py +++ b/src/nexusformat/nexus/tree.py @@ -542,9 +542,11 @@ def __enter__(self): def __exit__(self, *args): """Close the NeXus file and, if necessary, release the lock.""" - if self._with_count == 1: - self.close() - self._with_count -= 1 + try: + if self._with_count == 1: + self.close() + finally: + self._with_count -= 1 def __del__(self): """Close the file and delete the NXFile instance.""" @@ -721,9 +723,11 @@ def close(self): ----- The file modification time of the root object is updated. """ - if self.is_open(): - self._file.close() - self.release_lock() + try: + if self.is_open(): + self._file.close() + finally: + self.release_lock() try: self._root._mtime = self.mtime except Exception: