Skip to content

Commit f248672

Browse files
committed
Add typehints for fsspec.mapping
1 parent ffbdf86 commit f248672

File tree

2 files changed

+66
-24
lines changed

2 files changed

+66
-24
lines changed

Diff for: fsspec/mapping.py

+63-24
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,29 @@
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+
112
import array
213
import posixpath
314
import warnings
4-
from collections.abc import MutableMapping
515
from functools import cached_property
616

717
from .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

206245
def 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

Diff for: setup.cfg

+3
Original file line numberDiff line numberDiff line change
@@ -57,5 +57,8 @@ exclude = (test.*|conftest)\.py$
5757
[mypy-fsspec.caching]
5858
check_untyped_defs = True
5959

60+
[mypy-fsspec.mapping]
61+
check_untyped_defs = True
62+
6063
[mypy-fsspec.utils]
6164
check_untyped_defs = True

0 commit comments

Comments
 (0)