Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion brukerapi/paths.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,14 @@ def read_array(path, dtype, shape, order="F"):
A filesystem path is memory-mapped as before. An archive member cannot be
(a memory map cannot address a compressed member), so it degrades to a full
read.

The map is opened read-only. numpy's default mode is "r+", which asks the
operating system for write access and so fails with EACCES on a read-only file
or mount -- the usual way a shared scanner archive is exposed. Nothing here
writes: the array is copied out on the same line.
"""
if isinstance(path, (str, os.PathLike)):
return np.array(np.memmap(path, dtype=dtype, shape=shape, order=order)[:])
return np.array(np.memmap(path, dtype=dtype, mode="r", shape=shape, order=order)[:])
with path.open("rb") as binary:
buffer = binary.read()
return np.frombuffer(buffer, dtype=dtype).reshape(shape, order=order)
22 changes: 22 additions & 0 deletions test/test_paths.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,28 @@ def test_dataset_reads_from_archive_identically(study_dir, study_zip):
assert from_zip.shape_final == from_dir.shape_final


def test_dataset_reads_from_a_read_only_source(study_dir):
"""A dataset whose files are not writable must still load.

Scanner archives are normally exposed read-only, and nothing here writes: the
array is copied out of the map immediately. numpy's default memmap mode is
"r+", which asks the operating system for write access and fails with EACCES
on such a source, so the mode has to be given explicitly.
"""
for path in sorted(study_dir.rglob("*")):
if path.is_file():
path.chmod(0o444)

try:
dataset = Dataset(study_dir / "1" / "pdata" / "1" / "2dseq", scale=False)
assert np.array_equal(dataset.data, DATA)
finally:
# restore, so pytest can clean the temporary directory up
for path in sorted(study_dir.rglob("*")):
if path.is_file():
path.chmod(0o644)


def test_parameters_resolve_through_relative_paths(study_dir, study_zip):
"""``../../acqp`` resolves inside an archive, where ``..`` is not collapsed."""
dataset = Dataset(study_zip / "1" / "pdata" / "1" / "2dseq", scale=False,
Expand Down