Skip to content

Group attributes not shared across different group instances with identical store_path, path and name #3178

@FabricioArendTorres

Description

@FabricioArendTorres

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'}

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugPotential issues with the zarr-python library

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      pFad - Phonifier reborn

      Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

      Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


      Alternative Proxies:

      Alternative Proxy

      pFad Proxy

      pFad v3 Proxy

      pFad v4 Proxy