Skip to content

Commit

Permalink
Add mypy config, fix type errors in a few remaining places (iterative…
Browse files Browse the repository at this point in the history
…#4951)

* Add mypy config, fix type errors in a few random places

* Add typing_extensions

* Use setup.cfg for mypy's config
  • Loading branch information
skshetry authored Nov 24, 2020
1 parent 7c3c2e8 commit d138bdd
Show file tree
Hide file tree
Showing 7 changed files with 78 additions and 16 deletions.
1 change: 0 additions & 1 deletion dvc/parsing/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ def __init__(self, key, new, into):
)
return

assert isinstance(new, Node) and isinstance(into[key], Node)
preexisting = into[key].meta.source
new_src = new.meta.source
path = new.meta.path()
Expand Down
2 changes: 1 addition & 1 deletion dvc/tree/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ def open(self, path_info, mode: str = "r", encoding: str = None):

raise RemoteActionNotImplemented("open", self.scheme)

def exists(self, path_info, use_dvcignore=True):
def exists(self, path_info, use_dvcignore=True) -> bool:
raise NotImplementedError

# pylint: disable=unused-argument
Expand Down
10 changes: 6 additions & 4 deletions dvc/tree/ssh/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,16 +70,18 @@ def close(self):
self._ssh.close()

def st_mode(self, path):
lstat = None
with suppress(FileNotFoundError):
return self.sftp.lstat(path).st_mode
lstat = self.sftp.lstat(path)

return 0
return lstat.st_mode if lstat else 0

def getsize(self, path):
lstat = None
with suppress(FileNotFoundError):
return self.sftp.lstat(path).st_size
lstat = self.sftp.lstat(path)

return 0
return lstat.st_size if lstat else 0

def exists(self, path):
return bool(self.st_mode(path))
Expand Down
9 changes: 7 additions & 2 deletions dvc/utils/serialize/__init__.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
from collections import defaultdict
from typing import DefaultDict

from ._common import * # noqa, pylint: disable=wildcard-import
from ._json import * # noqa, pylint: disable=wildcard-import
from ._py import * # noqa, pylint: disable=wildcard-import
from ._toml import * # noqa, pylint: disable=wildcard-import
from ._yaml import * # noqa, pylint: disable=wildcard-import

LOADERS = defaultdict(lambda: load_yaml) # noqa: F405
LOADERS: DefaultDict[str, LoaderFn] = defaultdict( # noqa: F405
lambda: load_yaml # noqa: F405
)
LOADERS.update(
{".toml": load_toml, ".json": load_json, ".py": load_py} # noqa: F405
)

MODIFIERS = defaultdict(lambda: modify_yaml) # noqa: F405
MODIFIERS: DefaultDict[str, ModifierFn] = defaultdict( # noqa: F405
lambda: modify_yaml # noqa: F405
)
MODIFIERS.update(
{
".toml": modify_toml, # noqa: F405
Expand Down
52 changes: 44 additions & 8 deletions dvc/utils/serialize/_common.py
Original file line number Diff line number Diff line change
@@ -1,34 +1,70 @@
"""Common utilities for serialize."""
import os
from contextlib import contextmanager
from typing import TYPE_CHECKING, Any, Callable, ContextManager, Dict, Union

from typing_extensions import Protocol

from dvc.exceptions import DvcException
from dvc.utils import relpath

if TYPE_CHECKING:
from dvc.tree.base import BaseTree
from dvc.types import AnyPath


class DumperFn(Protocol):
def __call__(
self, path: "AnyPath", data: Any, tree: "BaseTree" = None
) -> Any:
...


class ModifierFn(Protocol):
def __call__(
self, path: "AnyPath", tree: "BaseTree" = None
) -> ContextManager[Dict]:
...


class LoaderFn(Protocol):
def __call__(self, path: "AnyPath", tree: "BaseTree" = None) -> Any:
...


ReadType = Union[bytes, None, str]
ParserFn = Callable[[ReadType, "AnyPath"], dict]


class ParseError(DvcException):
"""Errors while parsing files"""

def __init__(self, path, message):
def __init__(self, path: "AnyPath", message: str):
path = relpath(path)
super().__init__(f"unable to read: '{path}', {message}")


def _load_data(path, parser, tree=None):
def _load_data(path: "AnyPath", parser: ParserFn, tree: "BaseTree" = None):
open_fn = tree.open if tree else open
with open_fn(path, encoding="utf-8") as fd:
with open_fn(path, encoding="utf-8") as fd: # type: ignore
return parser(fd.read(), path)


def _dump_data(path, data, dumper, tree=None):
def _dump_data(path, data: Any, dumper: DumperFn, tree: "BaseTree" = None):
open_fn = tree.open if tree else open
with open_fn(path, "w+", encoding="utf-8") as fd:
with open_fn(path, "w+", encoding="utf-8") as fd: # type: ignore
dumper(data, fd)


@contextmanager
def _modify_data(path, parser, dumper, tree=None):
exists = tree.exists if tree else os.path.exists
data = _load_data(path, parser=parser, tree=tree) if exists(path) else {}
def _modify_data(
path: "AnyPath",
parser: ParserFn,
dumper: DumperFn,
tree: "BaseTree" = None,
):
exists_fn = tree.exists if tree else os.path.exists
file_exists = exists_fn(path) # type: ignore
data = _load_data(path, parser=parser, tree=tree) if file_exists else {}
yield data
dumper(path, data, tree=tree)
18 changes: 18 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,21 @@ timeout = 600
timeout_method = thread
log_level = debug
addopts = -ra

[mypy]
# Error output
show_column_numbers = True
show_error_codes = True
show_error_context = True
show_traceback = True
pretty = True

# See https://mypy.readthedocs.io/en/latest/running_mypy.html#missing-imports.
ignore_missing_imports = True
check_untyped_defs = False

# Warnings
warn_no_return=True
warn_redundant_casts=True
warn_unused_ignores=True
warn_unreachable = True
2 changes: 2 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ def run(self):
"dictdiffer>=0.8.1",
"python-benedict>=0.21.1",
"pyparsing==2.4.7",
"typing_extensions>=3.7.4",
]


Expand Down Expand Up @@ -152,6 +153,7 @@ def run(self):
"wget",
"filelock",
"black==19.10b0",
"mypy",
"wsgidav",
]

Expand Down

0 comments on commit d138bdd

Please sign in to comment.