2424from zarr .core .group import (
2525 ConsolidatedMetadata ,
2626 GroupMetadata ,
27+ _build_metadata_v3 ,
2728 _create_rooted_hierarchy ,
2829 _create_rooted_hierarchy_sync ,
2930 _join_paths ,
3435 create_hierarchy ,
3536 create_nodes ,
3637)
38+ from zarr .core .metadata .v3 import ArrayV3Metadata
3739from zarr .core .sync import _collect_aiterator , sync
38- from zarr .errors import ContainsArrayError , ContainsGroupError
40+ from zarr .errors import ContainsArrayError , ContainsGroupError , MetadataValidationError
3941from zarr .storage import LocalStore , MemoryStore , StorePath , ZipStore
4042from zarr .storage ._common import make_store_path
4143from zarr .storage ._utils import normalize_path
@@ -1516,9 +1518,53 @@ async def test_create_hierarchy(store: Store, overwrite: bool, zarr_format: Zarr
15161518 assert expected_meta == {k : v .metadata for k , v in observed_nodes .items ()}
15171519
15181520
1521+ @pytest .mark .parametrize ("store" , ["memory" ], indirect = True )
1522+ @pytest .mark .parametrize ("extant_node" , ["array" , "group" ])
1523+ async def test_create_hierarchy_existing_nodes (
1524+ store : Store , extant_node : Literal ["array" , "group" ], zarr_format : ZarrFormat
1525+ ) -> None :
1526+ """
1527+ Test that create_hierarchy with overwrite = False will not overwrite an existing array or group,
1528+ and raises an exception instead.
1529+ """
1530+ spath = await make_store_path (store , path = "path" )
1531+ extant_node_path = "node"
1532+
1533+ if extant_node == "array" :
1534+ extant_metadata = meta_from_array (
1535+ np .zeros (4 ), zarr_format = zarr_format , attributes = {"extant" : True }
1536+ )
1537+ new_metadata = meta_from_array (np .zeros (4 ), zarr_format = zarr_format )
1538+ err_cls = ContainsArrayError
1539+ else :
1540+ extant_metadata = GroupMetadata (zarr_format = zarr_format , attributes = {"extant" : True })
1541+ new_metadata = GroupMetadata (zarr_format = zarr_format )
1542+ err_cls = ContainsGroupError
1543+
1544+ # write the extant metadata
1545+ sync (
1546+ _collect_aiterator (
1547+ create_nodes (store_path = spath , nodes = {extant_node_path : extant_metadata })
1548+ )
1549+ )
1550+
1551+ msg = f"{ extant_node } exists in store { store !r} at path { extant_node_path !r} ."
1552+ # ensure that we cannot invoke create_hierarchy with overwrite=False here
1553+ with pytest .raises (err_cls , match = re .escape (msg )):
1554+ sync (
1555+ _collect_aiterator (
1556+ create_hierarchy (store_path = spath , nodes = {"node" : new_metadata }, overwrite = False )
1557+ )
1558+ )
1559+ # ensure that the extant metadata was not overwritten
1560+ assert (
1561+ await _read_node (spath / extant_node_path , zarr_format = zarr_format )
1562+ ).metadata .attributes == {"extant" : True }
1563+
1564+
15191565@pytest .mark .parametrize ("store" , ["memory" ], indirect = True )
15201566@pytest .mark .parametrize ("overwrite" , [True , False ])
1521- def test_group_create_hierarchy (store : Store , zarr_format : ZarrFormat , overwrite : bool ):
1567+ def test_group_create_hierarchy (store : Store , zarr_format : ZarrFormat , overwrite : bool ) -> None :
15221568 """
15231569 Test that the Group.create_hierarchy method creates specified nodes and returns them in a dict.
15241570 """
@@ -1537,7 +1583,9 @@ def test_group_create_hierarchy(store: Store, zarr_format: ZarrFormat, overwrite
15371583
15381584@pytest .mark .parametrize ("store" , ["memory" ], indirect = True )
15391585@pytest .mark .parametrize ("overwrite" , [True , False ])
1540- def test_group_create_hierarchy_no_root (store : Store , zarr_format : ZarrFormat , overwrite : bool ):
1586+ def test_group_create_hierarchy_no_root (
1587+ store : Store , zarr_format : ZarrFormat , overwrite : bool
1588+ ) -> None :
15411589 """
15421590 Test that the Group.create_hierarchy method will error if the dict provided contains a root.
15431591 """
@@ -1552,7 +1600,9 @@ def test_group_create_hierarchy_no_root(store: Store, zarr_format: ZarrFormat, o
15521600
15531601
15541602@pytest .mark .parametrize ("store" , ["memory" ], indirect = True )
1555- def test_group_create_hierarchy_invalid_mixed_zarr_format (store : Store , zarr_format : ZarrFormat ):
1603+ def test_group_create_hierarchy_invalid_mixed_zarr_format (
1604+ store : Store , zarr_format : ZarrFormat
1605+ ) -> None :
15561606 """
15571607 Test that ``Group.create_hierarchy`` will raise an error if the zarr_format of the nodes is
15581608 different from the parent group.
@@ -1597,7 +1647,7 @@ async def test_create_hierarchy_invalid_nested(
15971647
15981648
15991649@pytest .mark .parametrize ("store" , ["memory" ], indirect = True )
1600- async def test_create_hierarchy_invalid_mixed_format (store : Store ):
1650+ async def test_create_hierarchy_invalid_mixed_format (store : Store ) -> None :
16011651 """
16021652 Test that create_hierarchy will not create a Zarr group that contains a both Zarr v2 and
16031653 Zarr v3 nodes.
@@ -1625,7 +1675,9 @@ async def test_create_hierarchy_invalid_mixed_format(store: Store):
16251675@pytest .mark .parametrize ("zarr_format" , [2 , 3 ])
16261676@pytest .mark .parametrize ("root_key" , ["" , "root" ])
16271677@pytest .mark .parametrize ("path" , ["" , "foo" ])
1628- async def test_create_rooted_hierarchy_group (store : Store , zarr_format , path : str , root_key : str ):
1678+ async def test_create_rooted_hierarchy_group (
1679+ store : Store , zarr_format , path : str , root_key : str
1680+ ) -> None :
16291681 """
16301682 Test that the _create_rooted_hierarchy can create a group.
16311683 """
@@ -1663,8 +1715,8 @@ async def test_create_rooted_hierarchy_group(store: Store, zarr_format, path: st
16631715 assert members_observed_meta == members_expected_meta_relative
16641716
16651717
1666- @pytest .mark .parametrize ("store" , ["memory" , "local" ], indirect = True )
1667- def test_create_hierarchy_implicit_groups (store : Store ):
1718+ @pytest .mark .parametrize ("store" , ["memory" ], indirect = True )
1719+ def test_create_hierarchy_implicit_groups (store : Store ) -> None :
16681720 spath = sync (make_store_path (store , path = "" ))
16691721 nodes = {
16701722 "" : GroupMetadata (zarr_format = 3 , attributes = {"implicit" : False }),
@@ -1682,7 +1734,9 @@ def test_create_hierarchy_implicit_groups(store: Store):
16821734@pytest .mark .parametrize ("zarr_format" , [2 , 3 ])
16831735@pytest .mark .parametrize ("root_key" , ["" , "root" ])
16841736@pytest .mark .parametrize ("path" , ["" , "foo" ])
1685- def test_create_rooted_hierarchy_sync_group (store : Store , zarr_format , path : str , root_key : str ):
1737+ def test_create_rooted_hierarchy_sync_group (
1738+ store : Store , zarr_format , path : str , root_key : str
1739+ ) -> None :
16861740 """
16871741 Test that the _create_rooted_hierarchy_sync can create a group.
16881742 """
@@ -1723,7 +1777,9 @@ def test_create_rooted_hierarchy_sync_group(store: Store, zarr_format, path: str
17231777@pytest .mark .parametrize ("zarr_format" , [2 , 3 ])
17241778@pytest .mark .parametrize ("root_key" , ["" , "root" ])
17251779@pytest .mark .parametrize ("path" , ["" , "foo" ])
1726- async def test_create_rooted_hierarchy_array (store : Store , zarr_format , path : str , root_key : str ):
1780+ async def test_create_rooted_hierarchy_array (
1781+ store : Store , zarr_format , path : str , root_key : str
1782+ ) -> None :
17271783 """
17281784 Test that the _create_rooted_hierarchy can create an array.
17291785 """
@@ -1747,7 +1803,7 @@ async def test_create_rooted_hierarchy_array(store: Store, zarr_format, path: st
17471803@pytest .mark .parametrize ("path" , ["" , "foo" ])
17481804async def test_create_rooted_hierarchy_sync_array (
17491805 store : Store , zarr_format , path : str , root_key : str
1750- ):
1806+ ) -> None :
17511807 """
17521808 Test that _create_rooted_hierarchy_sync can create an array.
17531809 """
@@ -1765,7 +1821,7 @@ async def test_create_rooted_hierarchy_sync_array(
17651821 assert a .metadata .attributes == {"path" : root_key }
17661822
17671823
1768- async def test_create_rooted_hierarchy_invalid ():
1824+ async def test_create_rooted_hierarchy_invalid () -> None :
17691825 """
17701826 Ensure _create_rooted_hierarchy will raise a ValueError if the input does not contain
17711827 a root node.
@@ -1781,7 +1837,7 @@ async def test_create_rooted_hierarchy_invalid():
17811837
17821838
17831839@pytest .mark .parametrize ("paths" , [("a" , "/a" ), ("" , "/" ), ("b/" , "b" )])
1784- def test_normalize_paths_invalid (paths : tuple [str , str ]):
1840+ def test_normalize_paths_invalid (paths : tuple [str , str ]) -> None :
17851841 """
17861842 Ensure that calling _normalize_paths on values that will normalize to the same value
17871843 will generate a ValueError.
@@ -1795,7 +1851,7 @@ def test_normalize_paths_invalid(paths: tuple[str, str]):
17951851@pytest .mark .parametrize (
17961852 "paths" , [("/a" , "a/b" ), ("a" , "a/b" ), ("a/" , "a///b" ), ("/a/" , "//a/b///" )]
17971853)
1798- def test_normalize_paths_valid (paths : tuple [str , str ]):
1854+ def test_normalize_paths_valid (paths : tuple [str , str ]) -> None :
17991855 """
18001856 Ensure that calling _normalize_paths on values that normalize to distinct values
18011857 returns a tuple of those normalized values.
@@ -1804,7 +1860,10 @@ def test_normalize_paths_valid(paths: tuple[str, str]):
18041860 assert _normalize_paths (paths ) == expected
18051861
18061862
1807- def test_normalize_path_keys ():
1863+ def test_normalize_path_keys () -> None :
1864+ """
1865+ Test that normalize_path_keys returns a dict where each key has been normalized.
1866+ """
18081867 data = {"" : 10 , "a" : "hello" , "a/b" : None , "/a/b/c/d" : None }
18091868 observed = _normalize_path_keys (data )
18101869 expected = {normalize_path (k ): v for k , v in data .items ()}
@@ -1874,3 +1933,24 @@ def test_group_members_concurrency_limit(store: MemoryStore) -> None:
18741933 elapsed = time .time () - start
18751934
18761935 assert elapsed > num_groups * get_latency
1936+
1937+
1938+ @pytest .mark .parametrize ("option" , ["array" , "group" , "invalid" ])
1939+ def test_build_metadata_v3 (option : Literal ["array" , "group" , "invalid" ]) -> None :
1940+ """
1941+ Test that _build_metadata_v3 returns the correct metadata for a v3 array or group
1942+ """
1943+ match option :
1944+ case "array" :
1945+ metadata_dict = meta_from_array (np .arange (10 ), zarr_format = 3 ).to_dict ()
1946+ assert _build_metadata_v3 (metadata_dict ) == ArrayV3Metadata .from_dict (metadata_dict )
1947+ case "group" :
1948+ metadata_dict = GroupMetadata (attributes = {"foo" : 10 }, zarr_format = 3 ).to_dict ()
1949+ assert _build_metadata_v3 (metadata_dict ) == GroupMetadata .from_dict (metadata_dict )
1950+ case "invalid" :
1951+ metadata_dict = GroupMetadata (zarr_format = 3 ).to_dict ()
1952+ metadata_dict .pop ("node_type" )
1953+ # TODO: fix the error message
1954+ msg = "Invalid value for 'node_type'. Expected 'array or group'. Got 'nothing (the key is missing)'."
1955+ with pytest .raises (MetadataValidationError , match = re .escape (msg )):
1956+ _build_metadata_v3 (metadata_dict )
0 commit comments