Skip self-copy when reading uncopied lazy-loaded field#264
Merged
Conversation
When a file-backed NXfield is deep-copied (via NXdata.__init__, NXdata.weighted_data, etc.), `__deepcopy__` records `_uncopied_data = (file, source_path)` so the data can be lazily transferred to its new destination at next read. If the deep-copied field ends up at the same on-disk path as the source -- e.g. the nexpy PlotDialog wraps a field selected from inside an NXdata in a new in-memory NXdata named after the original group and reparents to the same grandparent -- then `_get_uncopied_data` asks HDF5 to copy the field onto itself and h5py raises `RuntimeError: Unable to synchronously copy object (destination object already exists)`. Guard `_get_uncopied_data` against this case: when the source and destination paths agree, the data is already where it needs to be, so skip the copy and fall through to the normal readvalue. Tests: new tests/test_files.py::test_read_lazy_field_after_same_path_rewrap reproduces the original crash (the field has to stay lazy-loaded, hence the 2k x 2k mask) and asserts a clean read after the fix. Full suite still passes (104 tests). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.
Summary
When a file-backed
NXfieldis deep-copied (viaNXdata.__init__,NXdata.weighted_data, etc.),NXfield.__deepcopy__records_uncopied_data = (file, source_path)so the data can be lazily transferred to its new destination at next read. The transfer is done inNXfield._get_uncopied_dataviaf.copy(source_path, self.nxpath).If the deep-copied field ends up at the same on-disk path as the source, h5py refuses the self-copy and raises
RuntimeError: Unable to synchronously copy object (destination object already exists).The case that surfaced this is the nexpy
PlotDialog: when the user selects a field that already lives inside anNXdata(e.g. thesummed_data_maskcompanion field), the dialog wraps it in a fresh in-memoryNXdatanamed after the original group and reparents to the original grandparent. The deep-copied wrapper'snxpathcollides with the source on disk, so the read crashes.The fix is a one-line guard in
_get_uncopied_data: when source and destination paths agree, the data is already where it needs to be — skip the copy and fall through to the normalreadvalue. It addresses the symptom directly and also closes the door on any other code path that wraps a file-backed lazy field into a same-on-disk-path container, not justPlotDialog.Test plan
tests/test_files.py::test_read_lazy_field_after_same_path_rewrapreproduces the originalRuntimeError(uses a 2k × 2k mask so the field stays lazy-loaded — small fields skip the bug because their_valueis already in memory and__deepcopy__clears_uncopied_data). The test reads cleanly after the fix and asserts the_uncopied_datareference is cleared after the read.pytest tests/— 104 tests).NXdata) via thePlotDialogno longer raisesUnable to synchronously copy object.