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 } { store !r} { 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