From 2c90480e1a102ab0fac57737d2ba5143d82abed7 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Tue, 30 Aug 2022 20:46:46 -0700 Subject: [PATCH 1/1] Use strict mypy checking (#3222) Co-authored-by: Richard Si <63936253+ichard26@users.noreply.github.com> --- .pre-commit-config.yaml | 2 ++ mypy.ini | 45 ++++++++++++++++++++++----------------- scripts/fuzz.py | 5 ++++- src/blackd/middlewares.py | 4 ++-- src/blib2to3/pytree.py | 18 ++++++++-------- tests/optional.py | 2 +- tests/test_blackd.py | 24 +++++++++++++-------- 7 files changed, 59 insertions(+), 41 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 87bb6e6..0be8dc4 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -49,6 +49,8 @@ repos: - types-typed-ast >= 1.4.1 - click >= 8.1.0 - platformdirs >= 2.1.0 + - pytest + - hypothesis - repo: https://github.com/pre-commit/mirrors-prettier rev: v2.7.1 diff --git a/mypy.ini b/mypy.ini index 244e8ae..4811cc0 100644 --- a/mypy.ini +++ b/mypy.ini @@ -7,33 +7,40 @@ python_version=3.6 mypy_path=src show_column_numbers=True - -# show error messages from unrelated files -follow_imports=normal - -# suppress errors about unsatisfied imports -ignore_missing_imports=True +show_error_codes=True # be strict -disallow_untyped_calls=True -warn_return_any=True -strict_optional=True -warn_no_return=True -warn_redundant_casts=True -warn_unused_ignores=True -disallow_any_generics=True -no_implicit_optional=True +strict=True + +# except for... +no_implicit_reexport = False # Unreachable blocks have been an issue when compiling mypyc, let's try # to avoid 'em in the first place. warn_unreachable=True -# The following are off by default. Flip them on if you feel -# adventurous. -disallow_untyped_defs=True -check_untyped_defs=True - [mypy-black] # The following is because of `patch_click()`. Remove when # we drop Python 3.6 support. warn_unused_ignores=False + +[mypy-blib2to3.driver.*] +ignore_missing_imports = True + +[mypy-IPython.*] +ignore_missing_imports = True + +[mypy-colorama.*] +ignore_missing_imports = True + +[mypy-pathspec.*] +ignore_missing_imports = True + +[mypy-tokenize_rt.*] +ignore_missing_imports = True + +[mypy-uvloop.*] +ignore_missing_imports = True + +[mypy-_black_version.*] +ignore_missing_imports = True diff --git a/scripts/fuzz.py b/scripts/fuzz.py index 83e02f4..25362c9 100644 --- a/scripts/fuzz.py +++ b/scripts/fuzz.py @@ -85,5 +85,8 @@ if __name__ == "__main__": pass else: test = test_idempotent_any_syntatically_valid_python - atheris.Setup(sys.argv, test.hypothesis.fuzz_one_input) + atheris.Setup( + sys.argv, + test.hypothesis.fuzz_one_input, # type: ignore[attr-defined] + ) atheris.Fuzz() diff --git a/src/blackd/middlewares.py b/src/blackd/middlewares.py index 7abde52..e71f508 100644 --- a/src/blackd/middlewares.py +++ b/src/blackd/middlewares.py @@ -9,7 +9,7 @@ Middleware = Callable[[Request, Handler], Awaitable[StreamResponse]] def cors(allow_headers: Iterable[str]) -> Middleware: - @middleware + @middleware # type: ignore[misc] async def impl(request: Request, handler: Handler) -> StreamResponse: is_options = request.method == "OPTIONS" is_preflight = is_options and "Access-Control-Request-Method" in request.headers @@ -32,4 +32,4 @@ def cors(allow_headers: Iterable[str]) -> Middleware: return resp - return impl # type: ignore + return impl # type: ignore[no-any-return] diff --git a/src/blib2to3/pytree.py b/src/blib2to3/pytree.py index 10b4690..15a1420 100644 --- a/src/blib2to3/pytree.py +++ b/src/blib2to3/pytree.py @@ -10,7 +10,7 @@ even the comments and whitespace between tokens. There's also a pattern matching implementation here. """ -# mypy: allow-untyped-defs +# mypy: allow-untyped-defs, allow-incomplete-defs from typing import ( Any, @@ -291,7 +291,7 @@ class Node(Base): """ return "".join(map(str, self.children)) - def _eq(self, other) -> bool: + def _eq(self, other: Base) -> bool: """Compare two nodes for equality.""" return (self.type, self.children) == (other.type, other.children) @@ -326,7 +326,7 @@ class Node(Base): return self.children[0].prefix @prefix.setter - def prefix(self, prefix) -> None: + def prefix(self, prefix: Text) -> None: if self.children: self.children[0].prefix = prefix @@ -439,7 +439,7 @@ class Leaf(Base): """ return self._prefix + str(self.value) - def _eq(self, other) -> bool: + def _eq(self, other: "Leaf") -> bool: """Compare two nodes for equality.""" return (self.type, self.value) == (other.type, other.value) @@ -472,7 +472,7 @@ class Leaf(Base): return self._prefix @prefix.setter - def prefix(self, prefix) -> None: + def prefix(self, prefix: Text) -> None: self.changed() self._prefix = prefix @@ -618,7 +618,7 @@ class LeafPattern(BasePattern): self.content = content self.name = name - def match(self, node: NL, results=None): + def match(self, node: NL, results=None) -> bool: """Override match() to insist on a leaf node.""" if not isinstance(node, Leaf): return False @@ -678,7 +678,7 @@ class NodePattern(BasePattern): if isinstance(item, WildcardPattern): # type: ignore[unreachable] self.wildcards = True # type: ignore[unreachable] self.type = type - self.content = newcontent + self.content = newcontent # TODO: this is unbound when content is None self.name = name def _submatch(self, node, results=None) -> bool: @@ -920,7 +920,7 @@ class WildcardPattern(BasePattern): class NegatedPattern(BasePattern): - def __init__(self, content: Optional[Any] = None) -> None: + def __init__(self, content: Optional[BasePattern] = None) -> None: """ Initializer. @@ -941,7 +941,7 @@ class NegatedPattern(BasePattern): # We only match an empty sequence of nodes in its entirety return len(nodes) == 0 - def generate_matches(self, nodes) -> Iterator[Tuple[int, _Results]]: + def generate_matches(self, nodes: List[NL]) -> Iterator[Tuple[int, _Results]]: if self.content is None: # Return a match if there is an empty sequence if len(nodes) == 0: diff --git a/tests/optional.py b/tests/optional.py index 853ecaa..8a39cc4 100644 --- a/tests/optional.py +++ b/tests/optional.py @@ -26,7 +26,7 @@ try: from pytest import StashKey except ImportError: # pytest < 7 - from _pytest.store import StoreKey as StashKey + from _pytest.store import StoreKey as StashKey # type: ignore[no-redef] log = logging.getLogger(__name__) diff --git a/tests/test_blackd.py b/tests/test_blackd.py index 8e73906..1da4ab7 100644 --- a/tests/test_blackd.py +++ b/tests/test_blackd.py @@ -1,6 +1,6 @@ import re import sys -from typing import Any +from typing import TYPE_CHECKING, Any, Callable, TypeVar from unittest.mock import patch import pytest @@ -19,16 +19,22 @@ if LESS_THAN_311: # noqa: C901 except ImportError as e: raise RuntimeError("Please install Black with the 'd' extra") from e - try: - from aiohttp.test_utils import unittest_run_loop - except ImportError: - # unittest_run_loop is unnecessary and a no-op since aiohttp 3.8, and aiohttp 4 - # removed it. To maintain compatibility we can make our own no-op decorator. - def unittest_run_loop(func: Any, *args: Any, **kwargs: Any) -> Any: - return func + if TYPE_CHECKING: + F = TypeVar("F", bound=Callable[..., Any]) + + unittest_run_loop: Callable[[F], F] = lambda x: x + else: + try: + from aiohttp.test_utils import unittest_run_loop + except ImportError: + # unittest_run_loop is unnecessary and a no-op since aiohttp 3.8, and + # aiohttp 4 removed it. To maintain compatibility we can make our own + # no-op decorator. + def unittest_run_loop(func, *args, **kwargs): + return func @pytest.mark.blackd - class BlackDTestCase(AioHTTPTestCase): + class BlackDTestCase(AioHTTPTestCase): # type: ignore[misc] def test_blackd_main(self) -> None: with patch("blackd.web.run_app"): result = CliRunner().invoke(blackd.main, []) -- 2.39.5