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
59 changes: 59 additions & 0 deletions tests/unit/layer/test_precomputed.py
Original file line number Diff line number Diff line change
Expand Up @@ -289,3 +289,62 @@ def test_no_reference_inherit_exc(clear_caches_reset_mocks):
encoding="raw",
inherit_all_params=True,
)


def test_get_info_diff_added_key():
result = precomputed._get_info_diff({"a": 1}, {"a": 1, "b": 2})
assert "+ b: 2" in result


def test_get_info_diff_removed_key():
result = precomputed._get_info_diff({"a": 1, "b": 2}, {"a": 1})
assert "- b: 2" in result


def test_get_info_diff_changed_key():
result = precomputed._get_info_diff({"a": 1}, {"a": 2})
assert "a: 1 -> 2" in result


def test_get_info_diff_no_diff():
result = precomputed._get_info_diff({"a": 1}, {"a": 1})
assert result == ""


def test_no_overwrite_dtype_change_shows_diff(clear_caches_reset_mocks, mocker):
_write_info = mocker.MagicMock()
precomputed._write_info = _write_info
existing_info = precomputed.get_info(LAYER_X0_PATH)
existing_scale = existing_info["scales"][0]
info_spec = PrecomputedInfoSpec(
info_spec_params=InfoSpecParams.from_optional_reference(
reference_path=LAYER_X0_PATH,
chunk_size=existing_scale["chunk_sizes"][0],
scales=[existing_scale["resolution"]],
bbox=BBox3D.from_coords(
existing_scale["voxel_offset"],
[existing_scale["voxel_offset"][i] + existing_scale["size"][i] for i in range(3)],
existing_scale["resolution"],
),
inherit_all_params=True,
data_type="int32",
)
)

with pytest.raises(RuntimeError, match=r"(?s)Differences:.*data_type.*uint8.*int32"):
info_spec.update_info(LAYER_X0_PATH, overwrite=False, keep_existing_scales=False)


def test_change_scale_on_extend_shows_missing_scales(clear_caches_reset_mocks, mocker):
_write_info = mocker.MagicMock()
precomputed._write_info = _write_info
info_spec = PrecomputedInfoSpec(
info_spec_params=InfoSpecParams.from_optional_reference(
reference_path=LAYER_X0_PATH,
chunk_size=[7, 7, 7],
scales=[[2, 2, 1]],
inherit_all_params=True,
)
)
with pytest.raises(RuntimeError, match=r"Missing scales:"):
info_spec.update_info(LAYER_X0_PATH, overwrite=False, keep_existing_scales=False)
24 changes: 22 additions & 2 deletions zetta_utils/layer/precomputed.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,20 @@
_info_hash_key = hashkey


def _get_info_diff(existing: dict, new: dict) -> str:
"""Return a string describing differences between two info dicts."""
diffs = []
all_keys = set(existing.keys()) | set(new.keys())
for key in sorted(all_keys):
if key not in existing:
diffs.append(f" + {key}: {new[key]}")
elif key not in new:
diffs.append(f" - {key}: {existing[key]}")
elif existing[key] != new[key]:
diffs.append(f" {key}: {existing[key]} -> {new[key]}")
return "\n".join(diffs)


# wrapper to cache using absolute paths with '/info'.
# invalidates the cached infofile if the infofile is local and has since been deleted.
def get_info(path: str) -> dict[str, Any]:
Expand Down Expand Up @@ -352,21 +366,27 @@ def update_info(self, path: str, overwrite: bool, keep_existing_scales: bool) ->
not (e in new_info["scales"]) for e in existing_info["scales"]
)
if existing_scales_changed:
missing_scales = [
e for e in existing_info["scales"] if e not in new_info["scales"]
]
raise RuntimeError(
f"New info is not a pure extension of the info existing at '{path}' "
"while `info_overwrite` is set to False. Some scales present "
f"in `{path}` would be overwritten."
f"in `{path}` would be overwritten.\n"
f"Missing scales: {missing_scales}"
)
existing_info_no_scales = copy.deepcopy(existing_info)
del existing_info_no_scales["scales"]
new_info_no_scales = copy.deepcopy(new_info)
del new_info_no_scales["scales"]
non_scales_changed = existing_info_no_scales != new_info_no_scales
if non_scales_changed:
diff = _get_info_diff(existing_info_no_scales, new_info_no_scales)
raise RuntimeError(
f"New info is not a pure extension of the info existing at '{path}' "
"while `info_overwrite` is set to False. Some non-scale keys "
f"in `{path}` would be overwritten."
f"in `{path}` would be overwritten.\n"
f"Differences:\n{diff}"
)

if existing_info != new_info:
Expand Down
Loading