Skip to content

Commit

Permalink
Prevent update_attributes from erasing all prior attributes
Browse files Browse the repository at this point in the history
  • Loading branch information
moradology committed Feb 26, 2025
1 parent 64b9a37 commit 0962049
Show file tree
Hide file tree
Showing 5 changed files with 42 additions and 6 deletions.
2 changes: 0 additions & 2 deletions src/zarr/core/array.py
Original file line number Diff line number Diff line change
Expand Up @@ -1609,8 +1609,6 @@ async def update_attributes(self, new_attributes: dict[str, JSON]) -> Self:
- The updated attributes will be merged with existing attributes, and any conflicts will be
overwritten by the new values.
"""
# metadata.attributes is "frozen" so we simply clear and update the dict
self.metadata.attributes.clear()
self.metadata.attributes.update(new_attributes)

# Write new metadata
Expand Down
1 change: 1 addition & 0 deletions src/zarr/core/attributes.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ def put(self, d: dict[str, JSON]) -> None:
>>> attrs
{'a': 3, 'c': 4}
"""
self._obj.metadata.attributes.clear()

Check warning on line 53 in src/zarr/core/attributes.py

View check run for this annotation

Codecov / codecov/patch

src/zarr/core/attributes.py#L53

Added line #L53 was not covered by tests
self._obj = self._obj.update_attributes(d)

def asdict(self) -> dict[str, JSON]:
Expand Down
4 changes: 2 additions & 2 deletions src/zarr/core/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import functools
import operator
import warnings
from collections.abc import Iterable, Mapping
from collections.abc import Iterable, Mapping, Sequence
from enum import Enum
from itertools import starmap
from typing import (
Expand Down Expand Up @@ -37,7 +37,7 @@
ChunkCoordsLike = Iterable[int]
ZarrFormat = Literal[2, 3]
NodeType = Literal["array", "group"]
JSON = str | int | float | Mapping[str, "JSON"] | tuple["JSON", ...] | None
JSON = str | int | float | Mapping[str, "JSON"] | Sequence["JSON"] | None
MemoryOrder = Literal["C", "F"]
AccessModeLiteral = Literal["r", "r+", "a", "w", "w-"]

Expand Down
2 changes: 0 additions & 2 deletions src/zarr/core/group.py
Original file line number Diff line number Diff line change
Expand Up @@ -1254,8 +1254,6 @@ async def update_attributes(self, new_attributes: dict[str, Any]) -> AsyncGroup:
-------
self : AsyncGroup
"""
# metadata.attributes is "frozen" so we simply clear and update the dict
self.metadata.attributes.clear()
self.metadata.attributes.update(new_attributes)

# Write new metadata
Expand Down
39 changes: 39 additions & 0 deletions tests/test_attributes.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,42 @@ def test_asdict() -> None:
)
result = attrs.asdict()
assert result == {"a": 1, "b": 2}


def test_update_attributes_preserves_existing() -> None:
"""
Test that `update_attributes` only updates the specified attributes
and preserves existing ones.
"""
store = zarr.storage.MemoryStore()
z = zarr.create(10, store=store, overwrite=True)
z.attrs["a"] = []
z.attrs["b"] = 3
assert dict(z.attrs) == {"a": [], "b": 3}

z.update_attributes({"a": [3, 4], "c": 4})
assert dict(z.attrs) == {"a": [3, 4], "b": 3, "c": 4}


def test_update_empty_attributes() -> None:
"""
Ensure updating when initial attributes are empty works.
"""
store = zarr.storage.MemoryStore()
z = zarr.create(10, store=store, overwrite=True)
assert dict(z.attrs) == {}
z.update_attributes({"a": [3, 4], "c": 4})
assert dict(z.attrs) == {"a": [3, 4], "c": 4}


def test_update_no_changes() -> None:
"""
Ensure updating when no new or modified attributes does not alter existing ones.
"""
store = zarr.storage.MemoryStore()
z = zarr.create(10, store=store, overwrite=True)
z.attrs["a"] = []
z.attrs["b"] = 3

z.update_attributes({})
assert dict(z.attrs) == {"a": [], "b": 3}

0 comments on commit 0962049

Please sign in to comment.