Skip to content

Commit a6ef792

Browse files
committed
Added group info
1 parent 435073c commit a6ef792

File tree

6 files changed

+168
-17
lines changed

6 files changed

+168
-17
lines changed

src/zarr/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from zarr._info import GroupInfo
12
from zarr._version import version as __version__
23
from zarr.api.synchronous import (
34
array,
@@ -38,6 +39,7 @@
3839
"AsyncArray",
3940
"AsyncGroup",
4041
"Group",
42+
"GroupInfo",
4143
"__version__",
4244
"array",
4345
"config",

src/zarr/_info.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import dataclasses
2+
import textwrap
3+
from typing import Literal
4+
5+
import zarr.abc.store
6+
7+
# Group
8+
# Name : /
9+
# Type : zarr.hierarchy.Group
10+
# Read-only : False
11+
# Store type : zarr.storage.MemoryStore
12+
# No. members : 0
13+
# No. arrays : 0
14+
# No. groups : 0
15+
16+
17+
# In [19]: z.info
18+
# Out[19]:
19+
# Type : zarr.core.Array
20+
# Data type : int32
21+
# Shape : (1000000,)
22+
# Chunk shape : (100000,)
23+
# Order : C
24+
# Read-only : False
25+
# Compressor : Blosc(cname='lz4', clevel=5, shuffle=SHUFFLE, blocksize=0)
26+
# Store type : zarr.storage.KVStore
27+
# No. bytes : 4000000 (3.8M)
28+
# No. bytes stored : 320
29+
# Storage ratio : 12500.0
30+
# Chunks initialized : 0/10
31+
32+
33+
@dataclasses.dataclass(kw_only=True)
34+
class GroupInfo:
35+
name: str
36+
type: Literal["Group"] = "Group"
37+
read_only: bool
38+
store_type: str
39+
count_members: int | None = None
40+
count_arrays: int | None = None
41+
count_groups: int | None = None
42+
43+
def __repr__(self) -> str:
44+
template = textwrap.dedent("""\
45+
Name : {name}
46+
Type : {type}
47+
Read-only : {read_only}
48+
Store type : {store_type}""")
49+
50+
if self.count_members is not None:
51+
template += ("\nNo. members : {count_members}")
52+
if self.count_arrays is not None:
53+
template += ("\nNo. arrays : {count_arrays}")
54+
if self.count_groups is not None:
55+
template += ("\nNo. groups : {count_groups}")
56+
return template.format(
57+
**dataclasses.asdict(self)
58+
)
59+
60+
# def _repr_html_(self): ...

src/zarr/core/array.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1145,7 +1145,11 @@ async def update_attributes(self, new_attributes: dict[str, JSON]) -> Self:
11451145
def __repr__(self) -> str:
11461146
return f"<AsyncArray {self.store_path} shape={self.shape} dtype={self.dtype}>"
11471147

1148-
async def info(self) -> None:
1148+
@property
1149+
def info(self) -> ...:
1150+
...
1151+
1152+
async def info_full(self) -> None:
11491153
raise NotImplementedError
11501154

11511155

@@ -2818,10 +2822,12 @@ def update_attributes(self, new_attributes: dict[str, JSON]) -> Array:
28182822
def __repr__(self) -> str:
28192823
return f"<Array {self.store_path} shape={self.shape} dtype={self.dtype}>"
28202824

2825+
@property
28212826
def info(self) -> None:
2822-
return sync(
2823-
self._async_array.info(),
2824-
)
2827+
return self._async_array.info
2828+
2829+
def info_full(self) -> None:
2830+
...
28252831

28262832

28272833
def nchunks_initialized(

src/zarr/core/group.py

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import numpy.typing as npt
1414
from typing_extensions import deprecated
1515

16+
from zarr._info import GroupInfo
1617
import zarr.api.asynchronous as async_api
1718
from zarr.abc.metadata import Metadata
1819
from zarr.abc.store import Store, set_or_delete
@@ -792,8 +793,37 @@ def attrs(self) -> dict[str, Any]:
792793
return self.metadata.attributes
793794

794795
@property
795-
def info(self) -> None:
796-
raise NotImplementedError
796+
def info(self) -> GroupInfo:
797+
if self.metadata.consolidated_metadata:
798+
members = list(self.metadata.consolidated_metadata.flattened_metadata.values())
799+
else:
800+
members = None
801+
return self._info(members=members)
802+
803+
async def info_complete(self) -> GroupInfo:
804+
members = [x[1].metadata async for x in self.members(max_depth=None)]
805+
return self._info(members=members)
806+
807+
def _info(self, members: list[ArrayV2Metadata | ArrayV3Metadata | GroupMetadata] | None = None) -> GroupInfo:
808+
kwargs = {}
809+
if members is not None:
810+
kwargs["count_members"] = len(members)
811+
count_arrays = 0
812+
count_groups = 0
813+
for member in members:
814+
if isinstance(member, GroupMetadata):
815+
count_groups += 1
816+
else:
817+
count_arrays += 1
818+
kwargs["count_arrays"] = count_arrays
819+
kwargs["count_groups"] = count_groups
820+
821+
return GroupInfo(
822+
name=self.store_path.path,
823+
read_only=self.store_path.store.mode.readonly,
824+
store_type=type(self.store_path.store).__name__,
825+
**kwargs
826+
)
797827

798828
@property
799829
def store(self) -> Store:
@@ -1438,8 +1468,11 @@ def attrs(self) -> Attributes:
14381468
return Attributes(self)
14391469

14401470
@property
1441-
def info(self) -> None:
1442-
raise NotImplementedError
1471+
def info(self) -> GroupInfo:
1472+
return self._async_group.info
1473+
1474+
def info_complete(self) -> GroupInfo:
1475+
return self._sync(self._async_group.info_complete())
14431476

14441477
@property
14451478
def store(self) -> Store:

tests/v3/test_group.py

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import pytest
1010

1111
import zarr
12+
from zarr._info import GroupInfo
1213
import zarr.api.asynchronous
1314
import zarr.api.synchronous
1415
from zarr import Array, AsyncArray, AsyncGroup, Group
@@ -18,6 +19,7 @@
1819
from zarr.core.sync import sync
1920
from zarr.errors import ContainsArrayError, ContainsGroupError
2021
from zarr.storage import LocalStore, MemoryStore, StorePath, ZipStore
22+
import zarr.storage
2123
from zarr.storage.common import make_store_path
2224

2325
from .conftest import parse_store
@@ -768,15 +770,6 @@ async def test_asyncgroup_attrs(store: Store, zarr_format: ZarrFormat) -> None:
768770
assert agroup.attrs == agroup.metadata.attributes == attributes
769771

770772

771-
async def test_asyncgroup_info(store: Store, zarr_format: ZarrFormat) -> None:
772-
agroup = await AsyncGroup.from_store( # noqa: F841
773-
store,
774-
zarr_format=zarr_format,
775-
)
776-
pytest.xfail("Info is not implemented for metadata yet")
777-
# assert agroup.info == agroup.metadata.info
778-
779-
780773
async def test_asyncgroup_open(
781774
store: Store,
782775
zarr_format: ZarrFormat,
@@ -1322,6 +1315,34 @@ def test_from_dict_extra_fields(self):
13221315
assert result == expected
13231316

13241317

1318+
class TestInfo:
1319+
def test_info(self):
1320+
store = zarr.storage.MemoryStore(mode="w")
1321+
A = zarr.group(store=store, path="A")
1322+
B = A.create_group(name="B")
1323+
1324+
B.create_array(name="x", shape=(1,))
1325+
B.create_array(name="y", shape=(2,))
1326+
1327+
result = A.info
1328+
expected = GroupInfo(
1329+
name="A",
1330+
read_only=False,
1331+
store_type="MemoryStore",
1332+
)
1333+
assert result == expected
1334+
1335+
result = A.info_complete()
1336+
expected = GroupInfo(
1337+
name="A",
1338+
read_only=False,
1339+
store_type="MemoryStore",
1340+
count_members=3,
1341+
count_arrays=2,
1342+
count_groups=1,
1343+
)
1344+
assert result == expected
1345+
13251346
def test_update_attrs() -> None:
13261347
# regression test for https://github.com/zarr-developers/zarr-python/issues/2328
13271348
root = Group.from_store(

tests/v3/test_info.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import textwrap
2+
3+
from zarr._info import GroupInfo
4+
5+
6+
def test_group_info_repr() -> None:
7+
info = GroupInfo(name="a", store_type="MemoryStore", read_only=False)
8+
result = repr(info)
9+
expected = textwrap.dedent("""\
10+
Name : a
11+
Type : Group
12+
Read-only : False
13+
Store type : MemoryStore""")
14+
assert result == expected
15+
16+
17+
def test_group_info_complete() -> None:
18+
info = GroupInfo(name="a", store_type="MemoryStore", read_only=False, count_arrays=10, count_groups=4, count_members=14)
19+
result = repr(info)
20+
expected = textwrap.dedent("""\
21+
Name : a
22+
Type : Group
23+
Read-only : False
24+
Store type : MemoryStore
25+
No. members : 14
26+
No. arrays : 10
27+
No. groups : 4""")
28+
assert result == expected
29+

0 commit comments

Comments
 (0)