33from  abc  import  ABC , abstractmethod 
44from  asyncio  import  gather 
55from  itertools  import  starmap 
6- from  typing  import  TYPE_CHECKING , NamedTuple ,  Protocol , runtime_checkable 
6+ from  typing  import  TYPE_CHECKING , Protocol , runtime_checkable 
77
88if  TYPE_CHECKING :
9-     from  collections .abc  import  AsyncGenerator , Iterable 
9+     from  collections .abc  import  AsyncGenerator , AsyncIterator ,  Iterable 
1010    from  types  import  TracebackType 
1111    from  typing  import  Any , Self , TypeAlias 
1212
1313    from  zarr .core .buffer  import  Buffer , BufferPrototype 
14-     from  zarr .core .common  import  AccessModeLiteral ,  BytesLike 
14+     from  zarr .core .common  import  BytesLike 
1515
16- __all__  =  ["AccessMode"  ,  " ByteGetter" , "ByteSetter" , "Store" , "set_or_delete" ]
16+ __all__  =  ["ByteGetter" , "ByteSetter" , "Store" , "set_or_delete" ]
1717
1818ByteRangeRequest : TypeAlias  =  tuple [int  |  None , int  |  None ]
1919
2020
21- class  AccessMode (NamedTuple ):
22-     """Access mode flags.""" 
23- 
24-     str : AccessModeLiteral 
25-     readonly : bool 
26-     overwrite : bool 
27-     create : bool 
28-     update : bool 
29- 
30-     @classmethod  
31-     def  from_literal (cls , mode : AccessModeLiteral ) ->  Self :
32-         """ 
33-         Create an AccessMode instance from a literal. 
34- 
35-         Parameters 
36-         ---------- 
37-         mode : AccessModeLiteral 
38-             One of 'r', 'r+', 'w', 'w-', 'a'. 
39- 
40-         Returns 
41-         ------- 
42-         AccessMode 
43-             The created instance. 
44- 
45-         Raises 
46-         ------ 
47-         ValueError 
48-             If mode is not one of 'r', 'r+', 'w', 'w-', 'a'. 
49-         """ 
50-         if  mode  in  ("r" , "r+" , "a" , "w" , "w-" ):
51-             return  cls (
52-                 str = mode ,
53-                 readonly = mode  ==  "r" ,
54-                 overwrite = mode  ==  "w" ,
55-                 create = mode  in  ("a" , "w" , "w-" ),
56-                 update = mode  in  ("r+" , "a" ),
57-             )
58-         raise  ValueError ("mode must be one of 'r', 'r+', 'w', 'w-', 'a'" )
59- 
60- 
6121class  Store (ABC ):
6222    """ 
6323    Abstract base class for Zarr stores. 
6424    """ 
6525
66-     _mode :  AccessMode 
26+     _read_only :  bool 
6727    _is_open : bool 
6828
69-     def  __init__ (self , * args :  Any ,  mode :  AccessModeLiteral  =  "r" ,  ** kwargs :  Any ) ->  None :
29+     def  __init__ (self , * ,  read_only :  bool  =  False ) ->  None :
7030        self ._is_open  =  False 
71-         self ._mode  =  AccessMode . from_literal ( mode ) 
31+         self ._read_only  =  read_only 
7232
7333    @classmethod  
7434    async  def  open (cls , * args : Any , ** kwargs : Any ) ->  Self :
@@ -112,81 +72,60 @@ async def _open(self) -> None:
11272        ------ 
11373        ValueError 
11474            If the store is already open. 
115-         FileExistsError 
116-             If ``mode='w-'`` and the store already exists. 
117- 
118-         Notes 
119-         ----- 
120-         * When ``mode='w'`` and the store already exists, it will be cleared. 
12175        """ 
12276        if  self ._is_open :
12377            raise  ValueError ("store is already open" )
124-         if  self .mode .str  ==  "w" :
125-             await  self .clear ()
126-         elif  self .mode .str  ==  "w-"  and  not  await  self .empty ():
127-             raise  FileExistsError ("Store already exists" )
12878        self ._is_open  =  True 
12979
13080    async  def  _ensure_open (self ) ->  None :
13181        """Open the store if it is not already open.""" 
13282        if  not  self ._is_open :
13383            await  self ._open ()
13484
135-     @abstractmethod  
136-     async  def  empty (self ) ->  bool :
85+     async  def  is_empty (self , prefix : str ) ->  bool :
13786        """ 
138-         Check if the store is empty. 
87+         Check if the directory is empty. 
88+ 
89+         Parameters 
90+         ---------- 
91+         prefix : str 
92+             Prefix of keys to check. 
13993
14094        Returns 
14195        ------- 
14296        bool 
14397            True if the store is empty, False otherwise. 
14498        """ 
145-         ...
99+         if  not  self .supports_listing :
100+             raise  NotImplementedError 
101+         if  prefix  !=  ""  and  not  prefix .endswith ("/" ):
102+             prefix  +=  "/" 
103+         async  for  _  in  self .list_prefix (prefix ):
104+             return  False 
105+         return  True 
146106
147-     @abstractmethod  
148107    async  def  clear (self ) ->  None :
149108        """ 
150109        Clear the store. 
151110
152111        Remove all keys and values from the store. 
153112        """ 
154-         ...
155- 
156-     @abstractmethod  
157-     def  with_mode (self , mode : AccessModeLiteral ) ->  Self :
158-         """ 
159-         Return a new store of the same type pointing to the same location with a new mode. 
160- 
161-         The returned Store is not automatically opened. Call :meth:`Store.open` before 
162-         using. 
163- 
164-         Parameters 
165-         ---------- 
166-         mode : AccessModeLiteral 
167-             The new mode to use. 
168- 
169-         Returns 
170-         ------- 
171-         store 
172-             A new store of the same type with the new mode. 
173- 
174-         Examples 
175-         -------- 
176-         >>> writer = zarr.store.MemoryStore(mode="w") 
177-         >>> reader = writer.with_mode("r") 
178-         """ 
179-         ...
113+         if  not  self .supports_deletes :
114+             raise  NotImplementedError 
115+         if  not  self .supports_listing :
116+             raise  NotImplementedError 
117+         self ._check_writable ()
118+         await  self .delete_dir ("" )
180119
181120    @property  
182-     def  mode (self ) ->  AccessMode :
183-         """Access mode of  the store. """ 
184-         return  self ._mode 
121+     def  read_only (self ) ->  bool :
122+         """Is  the store read-only? """ 
123+         return  self ._read_only 
185124
186125    def  _check_writable (self ) ->  None :
187126        """Raise an exception if the store is not writable.""" 
188-         if  self .mode . readonly :
189-             raise  ValueError ("store mode does not support writing" )
127+         if  self .read_only :
128+             raise  ValueError ("store was opened in read-only  mode and  does not support writing" )
190129
191130    @abstractmethod  
192131    def  __eq__ (self , value : object ) ->  bool :
@@ -329,16 +268,19 @@ def supports_listing(self) -> bool:
329268        ...
330269
331270    @abstractmethod  
332-     def  list (self ) ->  AsyncGenerator [str ]:
271+     def  list (self ) ->  AsyncIterator [str ]:
333272        """Retrieve all keys in the store. 
334273
335274        Returns 
336275        ------- 
337-         AsyncGenerator [str, None ] 
276+         AsyncIterator [str] 
338277        """ 
278+         # This method should be async, like overridden methods in child classes. 
279+         # However, that's not straightforward: 
280+         # https://stackoverflow.com/questions/68905848 
339281
340282    @abstractmethod  
341-     def  list_prefix (self , prefix : str ) ->  AsyncGenerator [str ]:
283+     def  list_prefix (self , prefix : str ) ->  AsyncIterator [str ]:
342284        """ 
343285        Retrieve all keys in the store that begin with a given prefix. Keys are returned relative 
344286        to the root of the store. 
@@ -349,11 +291,14 @@ def list_prefix(self, prefix: str) -> AsyncGenerator[str]:
349291
350292        Returns 
351293        ------- 
352-         AsyncGenerator [str, None ] 
294+         AsyncIterator [str] 
353295        """ 
296+         # This method should be async, like overridden methods in child classes. 
297+         # However, that's not straightforward: 
298+         # https://stackoverflow.com/questions/68905848 
354299
355300    @abstractmethod  
356-     def  list_dir (self , prefix : str ) ->  AsyncGenerator [str ]:
301+     def  list_dir (self , prefix : str ) ->  AsyncIterator [str ]:
357302        """ 
358303        Retrieve all keys and prefixes with a given prefix and which do not contain the character 
359304        “/” after the given prefix. 
@@ -364,8 +309,11 @@ def list_dir(self, prefix: str) -> AsyncGenerator[str]:
364309
365310        Returns 
366311        ------- 
367-         AsyncGenerator [str, None ] 
312+         AsyncIterator [str] 
368313        """ 
314+         # This method should be async, like overridden methods in child classes. 
315+         # However, that's not straightforward: 
316+         # https://stackoverflow.com/questions/68905848 
369317
370318    async  def  delete_dir (self , prefix : str ) ->  None :
371319        """ 
@@ -376,7 +324,7 @@ async def delete_dir(self, prefix: str) -> None:
376324        if  not  self .supports_listing :
377325            raise  NotImplementedError 
378326        self ._check_writable ()
379-         if  not  prefix .endswith ("/" ):
327+         if  prefix   !=   ""   and   not  prefix .endswith ("/" ):
380328            prefix  +=  "/" 
381329        async  for  key  in  self .list_prefix (prefix ):
382330            await  self .delete (key )
0 commit comments