Enable check_untyped_defs mypy option for src/

This option checks even functions which are not annotated. It's a good
step to ensure that existing type annotation are correct.

In a Pareto fashion, the last few holdouts are always the ugliest,
beware.
This commit is contained in:
Ran Benita 2020-05-01 14:40:16 +03:00
parent 848ab00663
commit 71dfdca4df
9 changed files with 65 additions and 31 deletions

View File

@ -98,3 +98,6 @@ strict_equality = True
warn_redundant_casts = True warn_redundant_casts = True
warn_return_any = True warn_return_any = True
warn_unused_configs = True warn_unused_configs = True
[mypy-_pytest.*]
check_untyped_defs = True

View File

@ -519,10 +519,11 @@ class MultiCapture:
def pop_outerr_to_orig(self): def pop_outerr_to_orig(self):
""" pop current snapshot out/err capture and flush to orig streams. """ """ pop current snapshot out/err capture and flush to orig streams. """
out, err = self.readouterr() out, err = self.readouterr()
# TODO: Fix type ignores.
if out: if out:
self.out.writeorg(out) self.out.writeorg(out) # type: ignore[union-attr] # noqa: F821
if err: if err:
self.err.writeorg(err) self.err.writeorg(err) # type: ignore[union-attr] # noqa: F821
return out, err return out, err
def suspend_capturing(self, in_: bool = False) -> None: def suspend_capturing(self, in_: bool = False) -> None:
@ -542,7 +543,8 @@ class MultiCapture:
if self.err: if self.err:
self.err.resume() self.err.resume()
if self._in_suspended: if self._in_suspended:
self.in_.resume() # TODO: Fix type ignore.
self.in_.resume() # type: ignore[union-attr] # noqa: F821
self._in_suspended = False self._in_suspended = False
def stop_capturing(self) -> None: def stop_capturing(self) -> None:

View File

@ -974,7 +974,7 @@ class Config:
self._mark_plugins_for_rewrite(hook) self._mark_plugins_for_rewrite(hook)
_warn_about_missing_assertion(mode) _warn_about_missing_assertion(mode)
def _mark_plugins_for_rewrite(self, hook): def _mark_plugins_for_rewrite(self, hook) -> None:
""" """
Given an importhook, mark for rewrite any top-level Given an importhook, mark for rewrite any top-level
modules or packages in the distribution package for modules or packages in the distribution package for
@ -989,7 +989,9 @@ class Config:
package_files = ( package_files = (
str(file) str(file)
for dist in importlib_metadata.distributions() for dist in importlib_metadata.distributions()
if any(ep.group == "pytest11" for ep in dist.entry_points) # Type ignored due to missing stub:
# https://github.com/python/typeshed/pull/3795
if any(ep.group == "pytest11" for ep in dist.entry_points) # type: ignore
for file in dist.files or [] for file in dist.files or []
) )

View File

@ -721,7 +721,9 @@ class FixtureRequest:
# this might also be a non-function Item despite its attribute name # this might also be a non-function Item despite its attribute name
return self._pyfuncitem return self._pyfuncitem
if scope == "package": if scope == "package":
node = get_scope_package(self._pyfuncitem, self._fixturedef) # FIXME: _fixturedef is not defined on FixtureRequest (this class),
# but on FixtureRequest (a subclass).
node = get_scope_package(self._pyfuncitem, self._fixturedef) # type: ignore[attr-defined] # noqa: F821
else: else:
node = get_scope_node(self._pyfuncitem, scope) node = get_scope_node(self._pyfuncitem, scope)
if node is None and scope == "class": if node is None and scope == "class":
@ -1158,7 +1160,7 @@ def wrap_function_to_error_out_if_called_directly(function, fixture_marker):
# keep reference to the original function in our own custom attribute so we don't unwrap # keep reference to the original function in our own custom attribute so we don't unwrap
# further than this point and lose useful wrappings like @mock.patch (#3774) # further than this point and lose useful wrappings like @mock.patch (#3774)
result.__pytest_wrapped__ = _PytestWrapper(function) result.__pytest_wrapped__ = _PytestWrapper(function) # type: ignore[attr-defined] # noqa: F821
return result return result

View File

@ -42,6 +42,8 @@ if TYPE_CHECKING:
# Imported here due to circular import. # Imported here due to circular import.
from _pytest.main import Session from _pytest.main import Session
from _pytest.warning_types import PytestWarning
SEP = "/" SEP = "/"
@ -118,9 +120,9 @@ class Node(metaclass=NodeMeta):
def __init__( def __init__(
self, self,
name: str, name: str,
parent: Optional["Node"] = None, parent: "Optional[Node]" = None,
config: Optional[Config] = None, config: Optional[Config] = None,
session: Optional["Session"] = None, session: "Optional[Session]" = None,
fspath: Optional[py.path.local] = None, fspath: Optional[py.path.local] = None,
nodeid: Optional[str] = None, nodeid: Optional[str] = None,
) -> None: ) -> None:
@ -201,7 +203,7 @@ class Node(metaclass=NodeMeta):
def __repr__(self) -> str: def __repr__(self) -> str:
return "<{} {}>".format(self.__class__.__name__, getattr(self, "name", None)) return "<{} {}>".format(self.__class__.__name__, getattr(self, "name", None))
def warn(self, warning): def warn(self, warning: "PytestWarning") -> None:
"""Issue a warning for this item. """Issue a warning for this item.
Warnings will be displayed after the test session, unless explicitly suppressed Warnings will be displayed after the test session, unless explicitly suppressed
@ -226,11 +228,9 @@ class Node(metaclass=NodeMeta):
) )
) )
path, lineno = get_fslocation_from_item(self) path, lineno = get_fslocation_from_item(self)
assert lineno is not None
warnings.warn_explicit( warnings.warn_explicit(
warning, warning, category=None, filename=str(path), lineno=lineno + 1,
category=None,
filename=str(path),
lineno=lineno + 1 if lineno is not None else None,
) )
# methods for ordering nodes # methods for ordering nodes
@ -417,24 +417,26 @@ class Node(metaclass=NodeMeta):
def get_fslocation_from_item( def get_fslocation_from_item(
item: "Item", node: "Node",
) -> Tuple[Union[str, py.path.local], Optional[int]]: ) -> Tuple[Union[str, py.path.local], Optional[int]]:
"""Tries to extract the actual location from an item, depending on available attributes: """Tries to extract the actual location from a node, depending on available attributes:
* "fslocation": a pair (path, lineno) * "location": a pair (path, lineno)
* "obj": a Python object that the item wraps. * "obj": a Python object that the node wraps.
* "fspath": just a path * "fspath": just a path
:rtype: a tuple of (str|LocalPath, int) with filename and line number. :rtype: a tuple of (str|LocalPath, int) with filename and line number.
""" """
try: # See Item.location.
return item.location[:2] location = getattr(
except AttributeError: node, "location", None
pass ) # type: Optional[Tuple[str, Optional[int], str]]
obj = getattr(item, "obj", None) if location is not None:
return location[:2]
obj = getattr(node, "obj", None)
if obj is not None: if obj is not None:
return getfslineno(obj) return getfslineno(obj)
return getattr(item, "fspath", "unknown location"), -1 return getattr(node, "fspath", "unknown location"), -1
class Collector(Node): class Collector(Node):

View File

@ -1169,8 +1169,10 @@ class Testdir:
popen = subprocess.Popen(cmdargs, stdout=stdout, stderr=stderr, **kw) popen = subprocess.Popen(cmdargs, stdout=stdout, stderr=stderr, **kw)
if stdin is Testdir.CLOSE_STDIN: if stdin is Testdir.CLOSE_STDIN:
assert popen.stdin is not None
popen.stdin.close() popen.stdin.close()
elif isinstance(stdin, bytes): elif isinstance(stdin, bytes):
assert popen.stdin is not None
popen.stdin.write(stdin) popen.stdin.write(stdin)
return popen return popen

View File

@ -64,6 +64,7 @@ from _pytest.warning_types import PytestCollectionWarning
from _pytest.warning_types import PytestUnhandledCoroutineWarning from _pytest.warning_types import PytestUnhandledCoroutineWarning
if TYPE_CHECKING: if TYPE_CHECKING:
from typing import Type
from typing_extensions import Literal from typing_extensions import Literal
from _pytest.fixtures import _Scope from _pytest.fixtures import _Scope
@ -256,6 +257,18 @@ def pytest_pycollect_makeitem(collector: "PyCollector", name: str, obj):
class PyobjMixin: class PyobjMixin:
_ALLOW_MARKERS = True _ALLOW_MARKERS = True
# Function and attributes that the mixin needs (for type-checking only).
if TYPE_CHECKING:
name = "" # type: str
parent = None # type: Optional[nodes.Node]
own_markers = [] # type: List[Mark]
def getparent(self, cls: Type[nodes._NodeType]) -> Optional[nodes._NodeType]:
...
def listchain(self) -> List[nodes.Node]:
...
@property @property
def module(self): def module(self):
"""Python module object this node was collected from (can be None).""" """Python module object this node was collected from (can be None)."""
@ -292,7 +305,10 @@ class PyobjMixin:
def _getobj(self): def _getobj(self):
"""Gets the underlying Python object. May be overwritten by subclasses.""" """Gets the underlying Python object. May be overwritten by subclasses."""
return getattr(self.parent.obj, self.name) # TODO: Improve the type of `parent` such that assert/ignore aren't needed.
assert self.parent is not None
obj = self.parent.obj # type: ignore[attr-defined] # noqa: F821
return getattr(obj, self.name)
def getmodpath(self, stopatmodule=True, includemodule=False): def getmodpath(self, stopatmodule=True, includemodule=False):
""" return python path relative to the containing module. """ """ return python path relative to the containing module. """
@ -772,7 +788,10 @@ class Instance(PyCollector):
# can be removed at node structure reorganization time # can be removed at node structure reorganization time
def _getobj(self): def _getobj(self):
return self.parent.obj() # TODO: Improve the type of `parent` such that assert/ignore aren't needed.
assert self.parent is not None
obj = self.parent.obj # type: ignore[attr-defined] # noqa: F821
return obj()
def collect(self) -> Iterable[Union[nodes.Item, nodes.Collector]]: def collect(self) -> Iterable[Union[nodes.Item, nodes.Collector]]:
self.session._fixturemanager.parsefactories(self) self.session._fixturemanager.parsefactories(self)
@ -1527,7 +1546,8 @@ class Function(PyobjMixin, nodes.Item):
return getimfunc(self.obj) return getimfunc(self.obj)
def _getobj(self): def _getobj(self):
return getattr(self.parent.obj, self.originalname) assert self.parent is not None
return getattr(self.parent.obj, self.originalname) # type: ignore[attr-defined]
@property @property
def _pyfuncitem(self): def _pyfuncitem(self):

View File

@ -508,7 +508,7 @@ def approx(expected, rel=None, abs=None, nan_ok=False):
__tracebackhide__ = True __tracebackhide__ = True
if isinstance(expected, Decimal): if isinstance(expected, Decimal):
cls = ApproxDecimal cls = ApproxDecimal # type: Type[ApproxBase]
elif isinstance(expected, Number): elif isinstance(expected, Number):
cls = ApproxScalar cls = ApproxScalar
elif isinstance(expected, Mapping): elif isinstance(expected, Mapping):
@ -534,7 +534,7 @@ def _is_numpy_array(obj):
""" """
import sys import sys
np = sys.modules.get("numpy") np = sys.modules.get("numpy") # type: Any
if np is not None: if np is not None:
return isinstance(obj, np.ndarray) return isinstance(obj, np.ndarray)
return False return False

View File

@ -136,8 +136,9 @@ class WarningsRecorder(warnings.catch_warnings):
Adapted from `warnings.catch_warnings`. Adapted from `warnings.catch_warnings`.
""" """
def __init__(self): def __init__(self) -> None:
super().__init__(record=True) # Type ignored due to the way typeshed handles warnings.catch_warnings.
super().__init__(record=True) # type: ignore[call-arg] # noqa: F821
self._entered = False self._entered = False
self._list = [] # type: List[warnings.WarningMessage] self._list = [] # type: List[warnings.WarningMessage]