77
88from zarr .abc .store import Store
99from zarr .buffer import Buffer
10- from zarr .common import concurrent_map , to_thread
10+ from zarr .common import OpenMode , concurrent_map , to_thread
1111
1212
13- def _get (path : Path , byte_range : tuple [int , int | None ] | None ) -> Buffer :
13+ def _get (path : Path , byte_range : tuple [int | None , int | None ] | None ) -> Buffer :
1414 """
1515 Fetch a contiguous region of bytes from a file.
1616
@@ -51,10 +51,8 @@ def _put(
5151 path : Path ,
5252 value : Buffer ,
5353 start : int | None = None ,
54- auto_mkdir : bool = True ,
5554) -> int | None :
56- if auto_mkdir :
57- path .parent .mkdir (parents = True , exist_ok = True )
55+ path .parent .mkdir (parents = True , exist_ok = True )
5856 if start is not None :
5957 with path .open ("r+b" ) as f :
6058 f .seek (start )
@@ -70,15 +68,14 @@ class LocalStore(Store):
7068 supports_listing : bool = True
7169
7270 root : Path
73- auto_mkdir : bool
7471
75- def __init__ (self , root : Path | str , auto_mkdir : bool = True ):
72+ def __init__ (self , root : Path | str , * , mode : OpenMode = "r" ):
73+ super ().__init__ (mode = mode )
7674 if isinstance (root , str ):
7775 root = Path (root )
7876 assert isinstance (root , Path )
7977
8078 self .root = root
81- self .auto_mkdir = auto_mkdir
8279
8380 def __str__ (self ) -> str :
8481 return f"file://{ self .root } "
@@ -90,7 +87,7 @@ def __eq__(self, other: object) -> bool:
9087 return isinstance (other , type (self )) and self .root == other .root
9188
9289 async def get (
93- self , key : str , byte_range : tuple [int , int | None ] | None = None
90+ self , key : str , byte_range : tuple [int | None , int | None ] | None = None
9491 ) -> Buffer | None :
9592 assert isinstance (key , str )
9693 path = self .root / key
@@ -101,7 +98,7 @@ async def get(
10198 return None
10299
103100 async def get_partial_values (
104- self , key_ranges : list [tuple [str , tuple [int , int ]]]
101+ self , key_ranges : list [tuple [str , tuple [int | None , int | None ]]]
105102 ) -> list [Buffer | None ]:
106103 """
107104 Read byte ranges from multiple keys.
@@ -121,16 +118,18 @@ async def get_partial_values(
121118 return await concurrent_map (args , to_thread , limit = None ) # TODO: fix limit
122119
123120 async def set (self , key : str , value : Buffer ) -> None :
121+ self ._check_writable ()
124122 assert isinstance (key , str )
125123 if isinstance (value , bytes | bytearray ):
126124 # TODO: to support the v2 tests, we convert bytes to Buffer here
127125 value = Buffer .from_bytes (value )
128126 if not isinstance (value , Buffer ):
129127 raise TypeError ("LocalStore.set(): `value` must a Buffer instance" )
130128 path = self .root / key
131- await to_thread (_put , path , value , auto_mkdir = self . auto_mkdir )
129+ await to_thread (_put , path , value )
132130
133131 async def set_partial_values (self , key_start_values : list [tuple [str , int , bytes ]]) -> None :
132+ self ._check_writable ()
134133 args = []
135134 for key , start , value in key_start_values :
136135 assert isinstance (key , str )
@@ -142,6 +141,7 @@ async def set_partial_values(self, key_start_values: list[tuple[str, int, bytes]
142141 await concurrent_map (args , to_thread , limit = None ) # TODO: fix limit
143142
144143 async def delete (self , key : str ) -> None :
144+ self ._check_writable ()
145145 path = self .root / key
146146 if path .is_dir (): # TODO: support deleting directories? shutil.rmtree?
147147 shutil .rmtree (path )
0 commit comments