@@ -572,7 +572,10 @@ def _from_bytes_v2(
572
572
573
573
@classmethod
574
574
def _from_bytes_v3 (
575
- cls , store_path : StorePath , zarr_json_bytes : Buffer , use_consolidated : bool | None
575
+ cls ,
576
+ store_path : StorePath ,
577
+ zarr_json_bytes : Buffer ,
578
+ use_consolidated : bool | None ,
576
579
) -> AsyncGroup :
577
580
group_metadata = json .loads (zarr_json_bytes .to_bytes ())
578
581
if use_consolidated and group_metadata .get ("consolidated_metadata" ) is None :
@@ -666,14 +669,33 @@ def _getitem_consolidated(
666
669
# the caller needs to verify this!
667
670
assert self .metadata .consolidated_metadata is not None
668
671
669
- try :
670
- metadata = self .metadata .consolidated_metadata .metadata [key ]
671
- except KeyError as e :
672
- # The Group Metadata has consolidated metadata, but the key
673
- # isn't present. We trust this to mean that the key isn't in
674
- # the hierarchy, and *don't* fall back to checking the store.
675
- msg = f"'{ key } ' not found in consolidated metadata."
676
- raise KeyError (msg ) from e
672
+ # we support nested getitems like group/subgroup/array
673
+ indexers = key .split ("/" )
674
+ indexers .reverse ()
675
+ metadata : ArrayV2Metadata | ArrayV3Metadata | GroupMetadata = self .metadata
676
+
677
+ while indexers :
678
+ indexer = indexers .pop ()
679
+ if isinstance (metadata , ArrayV2Metadata | ArrayV3Metadata ):
680
+ # we've indexed into an array with group["array/subarray"]. Invalid.
681
+ raise KeyError (key )
682
+ if metadata .consolidated_metadata is None :
683
+ # we've indexed into a group without consolidated metadata.
684
+ # This isn't normal; typically, consolidated metadata
685
+ # will include explicit markers for when there are no child
686
+ # nodes as metadata={}.
687
+ # We have some freedom in exactly how we interpret this case.
688
+ # For now, we treat None as the same as {}, i.e. we don't
689
+ # have any children.
690
+ raise KeyError (key )
691
+ try :
692
+ metadata = metadata .consolidated_metadata .metadata [indexer ]
693
+ except KeyError as e :
694
+ # The Group Metadata has consolidated metadata, but the key
695
+ # isn't present. We trust this to mean that the key isn't in
696
+ # the hierarchy, and *don't* fall back to checking the store.
697
+ msg = f"'{ key } ' not found in consolidated metadata."
698
+ raise KeyError (msg ) from e
677
699
678
700
# update store_path to ensure that AsyncArray/Group.name is correct
679
701
if prefix != "/" :
@@ -932,11 +954,7 @@ async def create_array(
932
954
933
955
@deprecated ("Use AsyncGroup.create_array instead." )
934
956
async def create_dataset (
935
- self ,
936
- name : str ,
937
- * ,
938
- shape : ShapeLike ,
939
- ** kwargs : Any ,
957
+ self , name : str , ** kwargs : Any
940
958
) -> AsyncArray [ArrayV2Metadata ] | AsyncArray [ArrayV3Metadata ]:
941
959
"""Create an array.
942
960
@@ -947,8 +965,6 @@ async def create_dataset(
947
965
----------
948
966
name : str
949
967
Array name.
950
- shape : int or tuple of ints
951
- Array shape.
952
968
kwargs : dict
953
969
Additional arguments passed to :func:`zarr.AsyncGroup.create_array`.
954
970
@@ -959,7 +975,7 @@ async def create_dataset(
959
975
.. deprecated:: 3.0.0
960
976
The h5py compatibility methods will be removed in 3.1.0. Use `AsyncGroup.create_array` instead.
961
977
"""
962
- return await self .create_array (name , shape = shape , ** kwargs )
978
+ return await self .create_array (name , ** kwargs )
963
979
964
980
@deprecated ("Use AsyncGroup.require_array instead." )
965
981
async def require_dataset (
@@ -1081,6 +1097,8 @@ async def nmembers(
1081
1097
-------
1082
1098
count : int
1083
1099
"""
1100
+ # check if we can use consolidated metadata, which requires that we have non-None
1101
+ # consolidated metadata at all points in the hierarchy.
1084
1102
if self .metadata .consolidated_metadata is not None :
1085
1103
return len (self .metadata .consolidated_metadata .flattened_metadata )
1086
1104
# TODO: consider using aioitertools.builtins.sum for this
@@ -1094,7 +1112,8 @@ async def members(
1094
1112
self ,
1095
1113
max_depth : int | None = 0 ,
1096
1114
) -> AsyncGenerator [
1097
- tuple [str , AsyncArray [ArrayV2Metadata ] | AsyncArray [ArrayV3Metadata ] | AsyncGroup ], None
1115
+ tuple [str , AsyncArray [ArrayV2Metadata ] | AsyncArray [ArrayV3Metadata ] | AsyncGroup ],
1116
+ None ,
1098
1117
]:
1099
1118
"""
1100
1119
Returns an AsyncGenerator over the arrays and groups contained in this group.
@@ -1125,12 +1144,12 @@ async def members(
1125
1144
async def _members (
1126
1145
self , max_depth : int | None , current_depth : int
1127
1146
) -> AsyncGenerator [
1128
- tuple [str , AsyncArray [ArrayV2Metadata ] | AsyncArray [ArrayV3Metadata ] | AsyncGroup ], None
1147
+ tuple [str , AsyncArray [ArrayV2Metadata ] | AsyncArray [ArrayV3Metadata ] | AsyncGroup ],
1148
+ None ,
1129
1149
]:
1130
1150
if self .metadata .consolidated_metadata is not None :
1131
1151
# we should be able to do members without any additional I/O
1132
1152
members = self ._members_consolidated (max_depth , current_depth )
1133
-
1134
1153
for member in members :
1135
1154
yield member
1136
1155
return
@@ -1186,7 +1205,8 @@ async def _members(
1186
1205
def _members_consolidated (
1187
1206
self , max_depth : int | None , current_depth : int , prefix : str = ""
1188
1207
) -> Generator [
1189
- tuple [str , AsyncArray [ArrayV2Metadata ] | AsyncArray [ArrayV3Metadata ] | AsyncGroup ], None
1208
+ tuple [str , AsyncArray [ArrayV2Metadata ] | AsyncArray [ArrayV3Metadata ] | AsyncGroup ],
1209
+ None ,
1190
1210
]:
1191
1211
consolidated_metadata = self .metadata .consolidated_metadata
1192
1212
@@ -1271,7 +1291,11 @@ async def full(
1271
1291
self , * , name : str , shape : ChunkCoords , fill_value : Any | None , ** kwargs : Any
1272
1292
) -> AsyncArray [ArrayV2Metadata ] | AsyncArray [ArrayV3Metadata ]:
1273
1293
return await async_api .full (
1274
- shape = shape , fill_value = fill_value , store = self .store_path , path = name , ** kwargs
1294
+ shape = shape ,
1295
+ fill_value = fill_value ,
1296
+ store = self .store_path ,
1297
+ path = name ,
1298
+ ** kwargs ,
1275
1299
)
1276
1300
1277
1301
async def empty_like (
@@ -1627,13 +1651,7 @@ def create_dataset(self, name: str, **kwargs: Any) -> Array:
1627
1651
return Array (self ._sync (self ._async_group .create_dataset (name , ** kwargs )))
1628
1652
1629
1653
@deprecated ("Use Group.require_array instead." )
1630
- def require_dataset (
1631
- self ,
1632
- name : str ,
1633
- * ,
1634
- shape : ShapeLike ,
1635
- ** kwargs : Any ,
1636
- ) -> Array :
1654
+ def require_dataset (self , name : str , ** kwargs : Any ) -> Array :
1637
1655
"""Obtain an array, creating if it doesn't exist.
1638
1656
1639
1657
Arrays are known as "datasets" in HDF5 terminology. For compatibility
@@ -1660,15 +1678,9 @@ def require_dataset(
1660
1678
.. deprecated:: 3.0.0
1661
1679
The h5py compatibility methods will be removed in 3.1.0. Use `Group.require_array` instead.
1662
1680
"""
1663
- return Array (self ._sync (self ._async_group .require_array (name , shape = shape , ** kwargs )))
1681
+ return Array (self ._sync (self ._async_group .require_array (name , ** kwargs )))
1664
1682
1665
- def require_array (
1666
- self ,
1667
- name : str ,
1668
- * ,
1669
- shape : ShapeLike ,
1670
- ** kwargs : Any ,
1671
- ) -> Array :
1683
+ def require_array (self , name : str , ** kwargs : Any ) -> Array :
1672
1684
"""Obtain an array, creating if it doesn't exist.
1673
1685
1674
1686
@@ -1690,7 +1702,7 @@ def require_array(
1690
1702
-------
1691
1703
a : Array
1692
1704
"""
1693
- return Array (self ._sync (self ._async_group .require_array (name , shape = shape , ** kwargs )))
1705
+ return Array (self ._sync (self ._async_group .require_array (name , ** kwargs )))
1694
1706
1695
1707
def empty (self , * , name : str , shape : ChunkCoords , ** kwargs : Any ) -> Array :
1696
1708
return Array (self ._sync (self ._async_group .empty (name = name , shape = shape , ** kwargs )))
0 commit comments