diff --git a/src/nexusformat/nexus/tree.py b/src/nexusformat/nexus/tree.py index 0dca125..6d86c8e 100644 --- a/src/nexusformat/nexus/tree.py +++ b/src/nexusformat/nexus/tree.py @@ -3286,7 +3286,16 @@ def _get_uncopied_data(self, idx=None): return f.readvalue(_path, idx=idx) else: if self.nxfilemode == 'rw': - f.copy(_path, self.nxpath) + # Skip the copy if the field already lives at the + # destination path. This happens when a file-backed + # field is deep-copied into a new container that + # ends up at the same on-disk location (e.g. the + # PlotDialog wraps a field in an in-memory NXdata + # whose nxpath collides with the source). HDF5 + # otherwise raises 'destination object already + # exists' here. + if _path != self.nxpath: + f.copy(_path, self.nxpath) else: self._create_memfile() f.copy(_path, self._memfile, name='data') diff --git a/tests/test_files.py b/tests/test_files.py index d4dc8dc..fe0f529 100644 --- a/tests/test_files.py +++ b/tests/test_files.py @@ -1,8 +1,9 @@ import os +import numpy as np import pytest -from nexusformat.nexus.tree import (NXdata, NXentry, NXFile, NXroot, nxload, - nxopen) +from nexusformat.nexus.tree import (NXdata, NXentry, NXfield, NXFile, NXroot, + nxload, nxopen) def test_file_creation(tmpdir): @@ -80,3 +81,29 @@ def test_file_context_manager(tmpdir, field1, field2): assert "entry/data/f2" in w2 assert "signal" in w2["entry/data"].attrs assert "axes" in w2["entry/data"].attrs + + +def test_read_lazy_field_on_copy(tmpdir): + + filename = os.path.join(tmpdir, "file.nxs") + shape = (2000, 2000) + root = NXroot(NXentry(NXdata( + NXfield(np.zeros(shape, dtype=np.int64), name="signal"), + name="data"))) + root["entry/data/signal_mask"] = NXfield(np.zeros(shape, dtype=bool)) + root["entry/data/signal"].attrs["mask"] = "signal_mask" + root.save(filename, mode="w") + del root + + root = nxload(filename, "rw") + src = root["entry/data"] + wrapper = NXdata(src["signal_mask"], name=src.nxname) + wrapper.nxgroup = src.nxgroup + field = wrapper.nxsignal + assert field._uncopied_data is not None + assert field._uncopied_data[1] == field.nxpath + + arr = field[()] + assert arr.shape == shape + assert arr.dtype == bool + assert field._uncopied_data is None