Open
Description
Zarr version
v3.0.8
Numcodecs version
v0.15.1
Python Version
3.11.11
Operating System
Linux
Installation
uv within devcontainer based on 'mcr.microsoft.com/devcontainers/python:3.12'
Description
Issue: When opening multiple group instances that point to the same store path, their attributes are not properly shared, leading to inconsistent behavior. I tried this specifically for multiple paths that should correspond to the root path ("", "/", ..., see in code).
Expected behavior: All group instances (root1
, root2
, root3
, root4
) should share the same attributes since they reference the same store path.
Actual behavior:
root1.attrs.asdict()
returns{'root1_attrs': '1'}
root2.attrs.asdict()
returns{'root2_attrs': '2'}
root3.attrs.asdict()
returns{}
(empty)root4.attrs.asdict()
returns{'root2_attrs': '2'}
The issue appears to be that group instances maintain separate attribute objects even when they reference the same underlying store path.
Steps to reproduce
# /// script
# requires-python = "==3.11.11"
# dependencies = [
# "zarr==3.0.8",
# "numcodecs==0.15.1",
# ]
# ///
import asyncio
import zarr
import zarr.storage
# utility for dealing with async
async def get_all_keys(async_group):
keys = []
async for key in async_group.list():
keys.append(key)
return keys
def get_items(store):
return asyncio.run(get_all_keys(store))
############################################
store = zarr.storage.MemoryStore()
root1 = zarr.create_group(store, path="")
root2 = zarr.open_group(store, path="/")
root3 = zarr.open_group(store, path="\\")
root1_subarray = root1.create_array("subarray1", shape=(1, 1), dtype=float)
root2_subarray = root2.create_array("subarray2", shape=(1, 1), dtype=float)
root1.attrs["root1_attrs"] = "1"
root2.attrs["root2_attrs"] = "2"
root4 = zarr.open_group(store, path="")
# this passes
assert root1._async_group.store_path == root2._async_group.store_path == root3._async_group.store_path
assert root1.name == root2.name == root3.name
assert root1.path == root2.path == root3.path
# this passes, but should not
assert root1.attrs.asdict() != root2.attrs.asdict()
assert root1.attrs.asdict() != root3.attrs.asdict()
# overview:
print(get_items(store))
print(f"{root1=}")
print(f"{root2=}")
print(f"{root3=}")
print(f"{root4=}")
print("")
print(f"{list(root1.arrays())=}")
print(f"{list(root2.arrays())=}")
print(f"{list(root3.arrays())=}")
print(f"{list(root4.arrays())=}")
print("")
print(f"{root1.attrs=}")
print(f"{root2.attrs=}")
print(f"{root3.attrs=}")
print(f"{root4.attrs=}")
print("")
print("")
print(f"{root1.attrs.asdict()=}")
print(f"{root2.attrs.asdict()=}")
print(f"{root3.attrs.asdict()=}")
print(f"{root4.attrs.asdict()=}")
print("")
Additional output
Output
['zarr.json', 'subarray1/zarr.json', 'subarray2/zarr.json']
root1=<Group memory://140310329361152>
root2=<Group memory://140310329361152>
root3=<Group memory://140310329361152>
root4=<Group memory://140310329361152>
list(root1.arrays())=[('subarray2', <Array memory://140310329361152/subarray2 shape=(1, 1) dtype=float64>), ('subarray1', <Array memory://140310329361152/subarray1 shape=(1, 1) dtype=float64>)]
list(root2.arrays())=[('subarray2', <Array memory://140310329361152/subarray2 shape=(1, 1) dtype=float64>), ('subarray1', <Array memory://140310329361152/subarray1 shape=(1, 1) dtype=float64>)]
list(root3.arrays())=[('subarray2', <Array memory://140310329361152/subarray2 shape=(1, 1) dtype=float64>), ('subarray1', <Array memory://140310329361152/subarray1 shape=(1, 1) dtype=float64>)]
list(root4.arrays())=[('subarray2', <Array memory://140310329361152/subarray2 shape=(1, 1) dtype=float64>), ('subarray1', <Array memory://140310329361152/subarray1 shape=(1, 1) dtype=float64>)]
root1.attrs=<zarr.core.attributes.Attributes object at 0x7f9c8b414f10>
root2.attrs=<zarr.core.attributes.Attributes object at 0x7f9c8b414f10>
root3.attrs=<zarr.core.attributes.Attributes object at 0x7f9c8b414f10>
root4.attrs=<zarr.core.attributes.Attributes object at 0x7f9c8b414f10>
root1.attrs.asdict()={'root1_attrs': '1'}
root2.attrs.asdict()={'root2_attrs': '2'}
root3.attrs.asdict()={}
root4.attrs.asdict()={'root2_attrs': '2'}