Skip to content

Commit da41a0c

Browse files
authored
mypy: strict typings (#221)
* mypy: strict typings * update pre-commit hooks and mypy
1 parent 6633ddb commit da41a0c

14 files changed

+94
-64
lines changed

.pre-commit-config.yaml

+6-6
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ default_language_version:
22
python: python3
33
repos:
44
- repo: https://github.com/psf/black
5-
rev: 23.1.0
5+
rev: 23.7.0
66
hooks:
77
- id: black
88
- repo: https://github.com/pre-commit/pre-commit-hooks
@@ -24,12 +24,12 @@ repos:
2424
- id: sort-simple-yaml
2525
- id: trailing-whitespace
2626
- repo: https://github.com/codespell-project/codespell
27-
rev: v2.2.2
27+
rev: v2.2.5
2828
hooks:
2929
- id: codespell
3030
additional_dependencies: ["tomli"]
3131
- repo: https://github.com/asottile/pyupgrade
32-
rev: v3.3.1
32+
rev: v3.10.1
3333
hooks:
3434
- id: pyupgrade
3535
args: [--py38-plus]
@@ -38,7 +38,7 @@ repos:
3838
hooks:
3939
- id: isort
4040
- repo: https://github.com/pycqa/flake8
41-
rev: 6.0.0
41+
rev: 6.1.0
4242
hooks:
4343
- id: flake8
4444
additional_dependencies:
@@ -47,8 +47,8 @@ repos:
4747
- flake8-debugger==4.1.2
4848
- flake8-string-format==0.3.0
4949
- repo: https://github.com/pycqa/bandit
50-
rev: 1.7.4
50+
rev: 1.7.5
5151
hooks:
5252
- id: bandit
5353
args: [-c, pyproject.toml]
54-
additional_dependencies: ["toml"]
54+
additional_dependencies: [".[toml]"]

pyproject.toml

+11-2
Original file line numberDiff line numberDiff line change
@@ -56,14 +56,23 @@ show_error_codes = true
5656
show_error_context = true
5757
show_traceback = true
5858
pretty = true
59-
check_untyped_defs = false
59+
check_untyped_defs = true
6060
# Warnings
6161
warn_no_return = true
6262
warn_redundant_casts = true
6363
warn_unreachable = true
64-
ignore_missing_imports = true
64+
strict_equality = true
65+
no_implicit_optional = true
66+
warn_unused_configs = true
6567
files = ["src", "tests"]
6668

69+
[[tool.mypy.overrides]]
70+
ignore_missing_imports = true
71+
module = [
72+
"fsspec.*",
73+
"funcy",
74+
]
75+
6776
[tool.pylint.format]
6877
max-line-length = 88
6978

setup.cfg

+2-1
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,11 @@ tests =
3838
pytest-cov==4.0.0
3939
pytest-mock==3.8.2
4040
pylint==2.15.0
41-
mypy==0.971
41+
mypy==1.5.1
4242
pytest-servers[s3]==0.1.3
4343
dev =
4444
%(tests)s
45+
types-tqdm
4546

4647
[options.packages.find]
4748
exclude =

src/dvc_objects/_tqdm.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,11 @@ def format_dict(self):
144144
d = super().format_dict
145145
ncols = d["ncols"] or 80
146146
# assumes `bar_format` has max one of ("ncols_desc" & "ncols_info")
147-
ncols_left = ncols - len(self.format_meter(ncols_desc=1, ncols_info=1, **d)) + 1
147+
148+
meter = self.format_meter( # type: ignore[call-arg]
149+
ncols_desc=1, ncols_info=1, **d
150+
)
151+
ncols_left = ncols - len(meter) + 1
148152
ncols_left = max(ncols_left, 0)
149153
if ncols_left:
150154
d["ncols_desc"] = d["ncols_info"] = ncols_left

src/dvc_objects/db.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -350,12 +350,12 @@ def all(self, jobs=None):
350350
remote_size, remote_oids = self._estimate_remote_size()
351351
return self._list_oids_traverse(remote_size, remote_oids, jobs=jobs)
352352

353-
def list_oids_exists(self, oids: List[str], jobs: Optional[int] = None):
353+
def list_oids_exists(self, oids: Iterable[str], jobs: Optional[int] = None):
354354
"""Return list of the specified oids which exist in this fs.
355355
Hashes will be queried individually.
356356
"""
357-
logger.debug(f"Querying {len(oids)} oids via object_exists")
358357
paths = list(map(self.oid_to_path, oids))
358+
logger.debug(f"Querying {len(paths)} oids via object_exists")
359359
in_remote = self.fs.exists(paths, batch_size=jobs)
360360
yield from itertools.compress(oids, in_remote)
361361

src/dvc_objects/executors.py

+21-21
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ class ThreadPoolExecutor(futures.ThreadPoolExecutor):
2828
_max_workers: int
2929

3030
def __init__(
31-
self, max_workers: int = None, cancel_on_error: bool = False, **kwargs
31+
self, max_workers: Optional[int] = None, cancel_on_error: bool = False, **kwargs
3232
):
3333
super().__init__(max_workers=max_workers, **kwargs)
3434
self._cancel_on_error = cancel_on_error
@@ -62,26 +62,26 @@ def shutdown( # pylint: disable=arguments-differ
6262
if sys.version_info > (3, 9):
6363
# pylint: disable=unexpected-keyword-arg
6464
return super().shutdown(wait=wait, cancel_futures=cancel_futures)
65-
66-
with self._shutdown_lock:
67-
self._shutdown = True
68-
if cancel_futures:
69-
# Drain all work items from the queue, and then cancel their
70-
# associated futures.
71-
while True:
72-
try:
73-
work_item = self._work_queue.get_nowait()
74-
except queue.Empty:
75-
break
76-
if work_item is not None:
77-
work_item.future.cancel()
78-
79-
# Send a wake-up to prevent threads calling
80-
# _work_queue.get(block=True) from permanently blocking.
81-
self._work_queue.put(None)
82-
if wait:
83-
for t in self._threads:
84-
t.join()
65+
else:
66+
with self._shutdown_lock:
67+
self._shutdown = True
68+
if cancel_futures:
69+
# Drain all work items from the queue, and then cancel their
70+
# associated futures.
71+
while True:
72+
try:
73+
work_item = self._work_queue.get_nowait()
74+
except queue.Empty:
75+
break
76+
if work_item is not None:
77+
work_item.future.cancel()
78+
79+
# Send a wake-up to prevent threads calling
80+
# _work_queue.get(block=True) from permanently blocking.
81+
self._work_queue.put(None) # type: ignore[arg-type]
82+
if wait:
83+
for t in self._threads:
84+
t.join()
8585

8686
def __exit__(self, exc_type, exc_val, exc_tb):
8787
if self._cancel_on_error:

src/dvc_objects/fs/base.py

+17-15
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@
2222
)
2323

2424
from fsspec.asyn import get_loop
25-
from funcy import cached_property, once_per_args
25+
from funcy import once_per_args
26+
27+
from dvc_objects.utils import cached_property
2628

2729
from ..executors import ThreadPoolExecutor, batch_coros
2830
from .callbacks import DEFAULT_CALLBACK, Callback
@@ -230,7 +232,7 @@ def read_block(
230232
path: AnyFSPath,
231233
offset: int,
232234
length: int,
233-
delimiter: bytes = None,
235+
delimiter: Optional[bytes] = None,
234236
) -> bytes:
235237
return self.fs.read_block(path, offset, length, delimiter=delimiter)
236238

@@ -248,16 +250,16 @@ def cat_ranges(
248250
paths: List[AnyFSPath],
249251
starts: List[int],
250252
ends: List[int],
251-
max_gap: int = None,
253+
max_gap: Optional[int] = None,
252254
**kwargs,
253255
) -> List[bytes]:
254256
return self.fs.cat_ranges(paths, starts, ends, max_gap=max_gap, **kwargs)
255257

256258
def cat_file(
257259
self,
258260
path: AnyFSPath,
259-
start: int = None,
260-
end: int = None,
261+
start: Optional[int] = None,
262+
end: Optional[int] = None,
261263
**kwargs: Any,
262264
) -> bytes:
263265
return self.fs.cat_file(path, start=start, end=end, **kwargs)
@@ -277,9 +279,9 @@ def pipe_file(self, path: AnyFSPath, value: bytes, **kwargs: Any) -> None:
277279
def read_text(
278280
self,
279281
path: AnyFSPath,
280-
encoding: str = None,
281-
errors: str = None,
282-
newline: str = None,
282+
encoding: Optional[str] = None,
283+
errors: Optional[str] = None,
284+
newline: Optional[str] = None,
283285
**kwargs: Any,
284286
) -> str:
285287
return self.fs.read_text(
@@ -290,9 +292,9 @@ def write_text(
290292
self,
291293
path: AnyFSPath,
292294
value: str,
293-
encoding: str = None,
294-
errors: str = None,
295-
newline: str = None,
295+
encoding: Optional[str] = None,
296+
errors: Optional[str] = None,
297+
newline: Optional[str] = None,
296298
**kwargs: Any,
297299
) -> None:
298300
self.fs.write_text(
@@ -531,7 +533,7 @@ def put_file(
531533
from_file: Union[AnyFSPath, "BinaryIO"],
532534
to_info: AnyFSPath,
533535
callback: Callback = DEFAULT_CALLBACK,
534-
size: int = None,
536+
size: Optional[int] = None,
535537
**kwargs,
536538
) -> None:
537539
if size:
@@ -578,7 +580,7 @@ def du(
578580
self,
579581
path: AnyFSPath,
580582
total: bool = True,
581-
maxdepth: int = None,
583+
maxdepth: Optional[int] = None,
582584
**kwargs: Any,
583585
) -> Union[int, Dict[AnyFSPath, int]]:
584586
return self.fs.du(path, total=total, maxdepth=maxdepth, **kwargs)
@@ -589,7 +591,7 @@ def put(
589591
to_info: Union[AnyFSPath, List[AnyFSPath]],
590592
callback: "Callback" = DEFAULT_CALLBACK,
591593
recursive: bool = False, # pylint: disable=unused-argument
592-
batch_size: int = None,
594+
batch_size: Optional[int] = None,
593595
):
594596
jobs = batch_size or self.jobs
595597
if self.fs.async_impl:
@@ -617,7 +619,7 @@ def get(
617619
to_info: Union[AnyFSPath, List[AnyFSPath]],
618620
callback: "Callback" = DEFAULT_CALLBACK,
619621
recursive: bool = False, # pylint: disable=unused-argument
620-
batch_size: int = None,
622+
batch_size: Optional[int] = None,
621623
) -> None:
622624
# Currently, the implementation is non-recursive if the paths are
623625
# provided as a list, and recursive if it's a single path.

src/dvc_objects/fs/callbacks.py

+6-5
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
from contextlib import ExitStack
22
from functools import wraps
3-
from typing import TYPE_CHECKING, Any, Dict, Optional, TypeVar, overload
3+
from typing import TYPE_CHECKING, Any, Dict, Optional, TypeVar, cast, overload
44

55
import fsspec
6-
from funcy import cached_property
6+
7+
from dvc_objects.utils import cached_property
78

89
if TYPE_CHECKING:
910
from typing import Awaitable, BinaryIO, Callable, TextIO, Union
@@ -33,7 +34,7 @@ def wrap_attr(
3334
from tqdm.utils import CallbackIOWrapper
3435

3536
wrapped = CallbackIOWrapper(self.relative_update, fobj, method)
36-
return wrapped
37+
return cast("Union[TextIO, BinaryIO]", wrapped)
3738

3839
def wrap_fn(self, fn: "Callable[_P, _R]") -> "Callable[_P, _R]":
3940
@wraps(fn)
@@ -121,7 +122,7 @@ def branch( # pylint: disable=arguments-differ
121122
path_1: "Union[str, BinaryIO]",
122123
path_2: str,
123124
kwargs: Dict[str, Any],
124-
child: "Callback" = None,
125+
child: Optional["Callback"] = None,
125126
) -> "Callback":
126127
child = kwargs["callback"] = child or DEFAULT_CALLBACK
127128
return child
@@ -136,7 +137,7 @@ def __init__(
136137
self,
137138
size: Optional[int] = None,
138139
value: int = 0,
139-
progress_bar: "Tqdm" = None,
140+
progress_bar: Optional["Tqdm"] = None,
140141
**tqdm_kwargs,
141142
):
142143
tqdm_kwargs["total"] = size or -1

src/dvc_objects/fs/errors.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import TYPE_CHECKING, List
1+
from typing import TYPE_CHECKING, List, Optional
22

33
if TYPE_CHECKING:
44
from .base import FileSystem
@@ -18,7 +18,7 @@ def __init__(
1818
fs: "FileSystem",
1919
protocol: str,
2020
url: str,
21-
missing: List[str] = None,
21+
missing: Optional[List[str]] = None,
2222
) -> None:
2323
self.protocol = protocol
2424
self.fs = fs

src/dvc_objects/fs/local.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
import threading
55

66
import fsspec
7-
from funcy import cached_property, wrap_prop
7+
from funcy import wrap_prop
8+
9+
from dvc_objects.utils import cached_property
810

911
from . import system
1012
from .base import FileSystem
@@ -183,7 +185,7 @@ class LocalFileSystem(FileSystem):
183185
PARAM_PATH = "path"
184186
TRAVERSE_PREFIX_LEN = 2
185187

186-
@wrap_prop(threading.Lock())
188+
@wrap_prop(threading.Lock()) # type: ignore[misc]
187189
@cached_property
188190
def fs(self):
189191
return FsspecLocalFileSystem(**self.config)

src/dvc_objects/fs/utils.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ def remove(path: "AnyFSPath") -> None:
104104
raise
105105

106106

107-
def makedirs(path, exist_ok: bool = False, mode: int = None) -> None:
107+
def makedirs(path, exist_ok: bool = False, mode: Optional[int] = None) -> None:
108108
if mode is None:
109109
os.makedirs(path, exist_ok=exist_ok)
110110
return
@@ -144,9 +144,9 @@ def makedirs(path, exist_ok: bool = False, mode: int = None) -> None:
144144
def copyfile(
145145
src: "AnyFSPath",
146146
dest: "AnyFSPath",
147-
callback: "Callback" = None,
147+
callback: Optional["Callback"] = None,
148148
no_progress_bar: bool = False,
149-
name: str = None,
149+
name: Optional[str] = None,
150150
) -> None:
151151
"""Copy file with progress bar"""
152152
name = name if name else os.path.basename(dest)

src/dvc_objects/transfer.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
from typing import TYPE_CHECKING, Set
1+
from typing import TYPE_CHECKING, Optional, Set
22

33
if TYPE_CHECKING:
44
from .db import ObjectDB
55

66

77
def transfer(
8-
src: "ObjectDB", dest: "ObjectDB", oids: Set["str"], jobs: int = None
8+
src: "ObjectDB", dest: "ObjectDB", oids: Set["str"], jobs: Optional[int] = None
99
) -> Set["str"]:
1010
src_exists = set(src.oids_exist(oids, jobs=jobs))
1111
src_missing = oids - src_exists

src/dvc_objects/utils.py

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
from typing import TYPE_CHECKING
2+
3+
if TYPE_CHECKING:
4+
from functools import cached_property
5+
else:
6+
from funcy import cached_property # noqa: TID251
7+
8+
__all__ = ["cached_property"]

0 commit comments

Comments
 (0)