1+ from __future__ import annotations
2+ from typing import (
3+ TYPE_CHECKING ,
4+ MutableMapping ,
5+ Literal ,
6+ Iterable ,
7+ Iterator ,
8+ overload ,
9+ TypeVar ,
10+ )
11+
112import array
213import posixpath
314import warnings
4- from collections .abc import MutableMapping
515from functools import cached_property
616
717from .core import url_to_fs
818
19+ if TYPE_CHECKING :
20+ from .spec import AbstractFileSystem
21+ from .implementations .dirfs import DirFileSystem
22+
23+ T = TypeVar ("T" )
24+
925
10- class FSMap (MutableMapping ):
26+ class FSMap (MutableMapping [ str , bytes ] ):
1127 """Wrap a FileSystem instance as a mutable wrapping.
1228
1329 The keys of the mapping become files under the given root, and the
@@ -35,7 +51,14 @@ class FSMap(MutableMapping):
3551 b'Hello World'
3652 """
3753
38- def __init__ (self , root , fs , check = False , create = False , missing_exceptions = None ):
54+ def __init__ (
55+ self ,
56+ root : str ,
57+ fs : AbstractFileSystem ,
58+ check : bool = False ,
59+ create : bool = False ,
60+ missing_exceptions : tuple [type [Exception ], ...] | None = None ,
61+ ):
3962 self .fs = fs
4063 self .root = fs ._strip_protocol (root ).rstrip ("/" )
4164 self ._root_key_to_str = fs ._strip_protocol (posixpath .join (root , "x" ))[:- 1 ]
@@ -61,28 +84,44 @@ def __init__(self, root, fs, check=False, create=False, missing_exceptions=None)
6184 self .fs .rm (root + "/a" )
6285
6386 @cached_property
64- def dirfs (self ):
87+ def dirfs (self ) -> DirFileSystem :
6588 """dirfs instance that can be used with the same keys as the mapper"""
6689 from .implementations .dirfs import DirFileSystem
6790
6891 return DirFileSystem (path = self ._root_key_to_str , fs = self .fs )
6992
70- def clear (self ):
93+ def clear (self ) -> None :
7194 """Remove all keys below root - empties out mapping"""
7295 try :
7396 self .fs .rm (self .root , True )
7497 self .fs .mkdir (self .root )
7598 except : # noqa: E722
7699 pass
77100
78- def getitems (self , keys , on_error = "raise" ):
101+ @overload
102+ def getitems (
103+ self , keys : Iterable [str ], on_error : Literal ["raise" , "omit" ] = ...
104+ ) -> dict [str , bytes ]:
105+ pass
106+
107+ @overload
108+ def getitems (
109+ self , keys : Iterable [str ], on_error : Literal ["return" ]
110+ ) -> dict [str , bytes | Exception ]:
111+ pass
112+
113+ def getitems (
114+ self ,
115+ keys : Iterable [str ],
116+ on_error : Literal ["raise" , "omit" , "return" ] = "raise" ,
117+ ) -> dict [str , bytes | Exception ] | dict [str , bytes ]:
79118 """Fetch multiple items from the store
80119
81120 If the backend is async-able, this might proceed concurrently
82121
83122 Parameters
84123 ----------
85- keys: list (str)
124+ keys: iterable (str)
86125 They keys to be fetched
87126 on_error : "raise", "omit", "return"
88127 If raise, an underlying exception will be raised (converted to KeyError
@@ -113,7 +152,7 @@ def getitems(self, keys, on_error="raise"):
113152 if on_error == "return" or not isinstance (out [k2 ], BaseException )
114153 }
115154
116- def setitems (self , values_dict ) :
155+ def setitems (self , values_dict : dict [ str , bytes ]) -> None :
117156 """Set the values of multiple items in the store
118157
119158 Parameters
@@ -123,11 +162,11 @@ def setitems(self, values_dict):
123162 values = {self ._key_to_str (k ): maybe_convert (v ) for k , v in values_dict .items ()}
124163 self .fs .pipe (values )
125164
126- def delitems (self , keys ) :
165+ def delitems (self , keys : Iterable [ str ]) -> None :
127166 """Remove multiple keys from the store"""
128167 self .fs .rm ([self ._key_to_str (k ) for k in keys ])
129168
130- def _key_to_str (self , key ) :
169+ def _key_to_str (self , key : str ) -> str :
131170 """Generate full path for the key"""
132171 if not isinstance (key , str ):
133172 # raise TypeError("key must be of type `str`, got `{type(key).__name__}`"
@@ -140,11 +179,11 @@ def _key_to_str(self, key):
140179 key = str (key )
141180 return f"{ self ._root_key_to_str } { key } "
142181
143- def _str_to_key (self , s ) :
182+ def _str_to_key (self , s : str ) -> str :
144183 """Strip path of to leave key name"""
145184 return s [len (self .root ) :].lstrip ("/" )
146185
147- def __getitem__ (self , key , default = None ):
186+ def __getitem__ (self , key : str , default : bytes | None = None ) -> bytes :
148187 """Retrieve data"""
149188 k = self ._key_to_str (key )
150189 try :
@@ -155,7 +194,7 @@ def __getitem__(self, key, default=None):
155194 raise KeyError (key )
156195 return result
157196
158- def pop (self , key , default = None ):
197+ def pop (self , key : str , default : bytes | None = None ) -> bytes : # type: ignore[override]
159198 """Pop data"""
160199 result = self .__getitem__ (key , default )
161200 try :
@@ -164,26 +203,26 @@ def pop(self, key, default=None):
164203 pass
165204 return result
166205
167- def __setitem__ (self , key , value ) :
206+ def __setitem__ (self , key : str , value : bytes ) -> None :
168207 """Store value in key"""
169208 key = self ._key_to_str (key )
170209 self .fs .mkdirs (self .fs ._parent (key ), exist_ok = True )
171210 self .fs .pipe_file (key , maybe_convert (value ))
172211
173- def __iter__ (self ):
212+ def __iter__ (self ) -> Iterator [ str ] :
174213 return (self ._str_to_key (x ) for x in self .fs .find (self .root ))
175214
176- def __len__ (self ):
215+ def __len__ (self ) -> int :
177216 return len (self .fs .find (self .root ))
178217
179- def __delitem__ (self , key ) :
218+ def __delitem__ (self , key : str ) -> None :
180219 """Remove key"""
181220 try :
182221 self .fs .rm (self ._key_to_str (key ))
183222 except : # noqa: E722
184223 raise KeyError
185224
186- def __contains__ (self , key ):
225+ def __contains__ (self , key : str ) -> bool : # type: ignore[override]
187226 """Does key exist in mapping?"""
188227 path = self ._key_to_str (key )
189228 return self .fs .exists (path ) and self .fs .isfile (path )
@@ -204,13 +243,13 @@ def maybe_convert(value):
204243
205244
206245def get_mapper (
207- url = "" ,
208- check = False ,
209- create = False ,
210- missing_exceptions = None ,
211- alternate_root = None ,
246+ url : str = "" ,
247+ check : bool = False ,
248+ create : bool = False ,
249+ missing_exceptions : tuple [ type [ Exception ], ...] | None = None ,
250+ alternate_root : str | None = None ,
212251 ** kwargs ,
213- ):
252+ ) -> FSMap :
214253 """Create key-value interface for given URL and options
215254
216255 The URL will be of the form "protocol://location" and point to the root
0 commit comments