3434# ### amended from s3fs ### # 
3535test_bucket_name  =  "test" 
3636secure_bucket_name  =  "test-secure" 
37- port  =  5555 
38- endpoint_url  =  f"http://127.0.0.1:{ port }  /" 
3937
4038
4139async  def  parse_store (
42-     store : Literal [ "local" ,  "memory" ,  "remote" ,  "zip" ],  path : str 
40+     store : str ,  path :  str ,  s3_base : str 
4341) ->  LocalStore  |  MemoryStore  |  RemoteStore  |  ZipStore :
44-     if  store  ==  "local" :
45-         return  await  LocalStore .open (path , mode = "a" )
46-     if  store  ==  "memory" :
47-         return  await  MemoryStore .open (mode = "a" )
48-     if  store  ==  "remote" :
49-         return  RemoteStore .from_url (
50-             f"s3://{ test_bucket_name }  /foo/spam/" ,
51-             mode = "a" ,
52-             storage_options = {"endpoint_url" : endpoint_url , "anon" : False },
53-         )
54-     if  store  ==  "zip" :
55-         return  await  ZipStore .open (path  +  "/zarr.zip" , mode = "a" )
42+     """ 
43+     Take a string representation of a store + access mode, e.g. 'local_a', which would encode 
44+     LocalStore + access mode `a`, and convert that string representation into the appropriate store object, 
45+     which is then returned. 
46+     """ 
47+     store_parsed  =  store .split ("_" )
48+ 
49+     if  len (store_parsed ) ==  1 :
50+         store_type  =  store_parsed [0 ]
51+         # the default mode for testing is a 
52+         mode  =  "a" 
53+     elif  len (store_parsed ) ==  2 :
54+         store_type , mode  =  store_parsed 
55+     else :
56+         raise  ValueError (f"Invalid store specification: { store }  " )
57+ 
58+     match  store_type :
59+         case  "local" :
60+             return  await  LocalStore .open (path , mode = mode )
61+         case  "memory" :
62+             return  await  MemoryStore .open (mode = mode )
63+         case  "remote" :
64+             return  RemoteStore .from_url (
65+                 f"s3://{ test_bucket_name }  /foo/spam/" ,
66+                 mode = mode ,
67+                 storage_options = {"endpoint_url" : s3_base , "anon" : False },
68+             )
69+         case  "zip" :
70+             return  await  ZipStore .open (path  +  "/zarr.zip" , mode = mode )
5671    raise  AssertionError 
5772
5873
@@ -69,14 +84,14 @@ async def store_path(tmpdir: LEGACY_PATH) -> StorePath:
6984
7085
7186@pytest .fixture  
72- async  def  store (request : pytest .FixtureRequest , tmpdir : LEGACY_PATH ) ->  Store :
87+ async  def  store (request : pytest .FixtureRequest , tmpdir : LEGACY_PATH ,  s3_base :  str ) ->  Store :
7388    param  =  request .param 
74-     return  await  parse_store (param , str (tmpdir ))
89+     return  await  parse_store (param , str (tmpdir ),  s3_base )
7590
7691
7792@pytest .fixture (params = ["local" , "memory" , "zip" ]) 
78- def  sync_store (request : pytest .FixtureRequest , tmp_path : LEGACY_PATH ) ->  Store :
79-     result  =  sync (parse_store (request .param , str (tmp_path )))
93+ def  sync_store (request : pytest .FixtureRequest , tmp_path : LEGACY_PATH ,  s3_base :  str ) ->  Store :
94+     result  =  sync (parse_store (request .param , str (tmp_path ),  s3_base ))
8095    if  not  isinstance (result , Store ):
8196        raise  TypeError ("Wrong store class returned by test fixture! got "  +  result  +  " instead" )
8297    return  result 
@@ -90,10 +105,12 @@ class AsyncGroupRequest:
90105
91106
92107@pytest .fixture  
93- async  def  async_group (request : pytest .FixtureRequest , tmpdir : LEGACY_PATH ) ->  AsyncGroup :
108+ async  def  async_group (
109+     request : pytest .FixtureRequest , tmpdir : LEGACY_PATH , s3_base : str 
110+ ) ->  AsyncGroup :
94111    param : AsyncGroupRequest  =  request .param 
95112
96-     store  =  await  parse_store (param .store , str (tmpdir ))
113+     store  =  await  parse_store (param .store , str (tmpdir ),  s3_base )
97114    return  await  AsyncGroup .from_store (
98115        store ,
99116        attributes = param .attributes ,
@@ -147,29 +164,31 @@ def zarr_format(request: pytest.FixtureRequest) -> ZarrFormat:
147164
148165
149166@pytest .fixture (scope = "module" ) 
150- def  s3_base () ->  Generator [None , None , None ]:
167+ def  s3_base () ->  Generator [str , None , None ]:
151168    # writable local S3 system 
169+     from  moto .server  import  ThreadedMotoServer 
152170
153-     # This fixture is module-scoped, meaning that we can reuse the MotoServer across all tests 
154-     server  =  moto_server .ThreadedMotoServer (ip_address = "127.0.0.1" , port = port )
155-     server .start ()
156171    if  "AWS_SECRET_ACCESS_KEY"  not  in   os .environ :
157172        os .environ ["AWS_SECRET_ACCESS_KEY" ] =  "foo" 
158173    if  "AWS_ACCESS_KEY_ID"  not  in   os .environ :
159174        os .environ ["AWS_ACCESS_KEY_ID" ] =  "foo" 
175+     server  =  ThreadedMotoServer (ip_address = "127.0.0.1" , port = 0 )
176+     server .start ()
177+     host , port  =  server ._server .server_address 
178+     endpoint_url  =  f"http://{ host }  :{ port }  " 
160179
161-     yield 
180+     yield   endpoint_url 
162181    server .stop ()
163182
164183
165- def  get_boto3_client () ->  botocore .client .BaseClient :
184+ def  get_boto3_client (endpoint_url :  str ) ->  botocore .client .BaseClient :
166185    # NB: we use the sync botocore client for setup 
167186    session  =  Session ()
168187    return  session .create_client ("s3" , endpoint_url = endpoint_url , region_name = "us-east-1" )
169188
170189
171190@pytest .fixture (autouse = True ) 
172- def  s3 (s3_base : None ) ->  Generator [s3fs .S3FileSystem , None , None ]:  # type: ignore[name-defined] 
191+ def  s3 (s3_base : str ) ->  Generator [s3fs .S3FileSystem , None , None ]:  # type: ignore[name-defined] 
173192    """ 
174193    Quoting Martin Durant: 
175194    pytest-asyncio creates a new event loop for each async test. 
@@ -182,14 +201,14 @@ def s3(s3_base: None) -> Generator[s3fs.S3FileSystem, None, None]:  # type: igno
182201
183202    https://github.com/zarr-developers/zarr-python/pull/1785#discussion_r1634856207 
184203    """ 
185-     client  =  get_boto3_client ()
204+     client  =  get_boto3_client (s3_base )
186205    client .create_bucket (Bucket = test_bucket_name , ACL = "public-read" )
187206    s3fs .S3FileSystem .clear_instance_cache ()
188-     s3  =  s3fs .S3FileSystem (anon = False , client_kwargs = {"endpoint_url" : endpoint_url })
207+     s3  =  s3fs .S3FileSystem (anon = False , client_kwargs = {"endpoint_url" : s3_base })
189208    session  =  sync (s3 .set_session ())
190209    s3 .invalidate_cache ()
191210    yield  s3 
192-     requests .post (f"{ endpoint_url }  /moto-api/reset" )
211+     requests .post (f"{ s3_base }  /moto-api/reset" )
193212    client .close ()
194213    sync (session .close ())
195214
0 commit comments