summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRan Benita <ran@unusedvar.com>2020-05-01 14:40:16 +0300
committerRan Benita <ran@unusedvar.com>2020-06-05 11:34:20 +0300
commit71dfdca4df6961460653c265026e194fbcaebef2 (patch)
treec6a88af4e3c29addc5fd95fd01ae36a3ef5fbfb4
parent848ab00663c9daf8cd27ee92dec1005cd9633152 (diff)
downloadpytest-71dfdca4df6961460653c265026e194fbcaebef2.tar.gz
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.
-rw-r--r--setup.cfg3
-rw-r--r--src/_pytest/capture.py8
-rw-r--r--src/_pytest/config/__init__.py6
-rw-r--r--src/_pytest/fixtures.py6
-rw-r--r--src/_pytest/nodes.py36
-rw-r--r--src/_pytest/pytester.py2
-rw-r--r--src/_pytest/python.py26
-rw-r--r--src/_pytest/python_api.py4
-rw-r--r--src/_pytest/recwarn.py5
9 files changed, 65 insertions, 31 deletions
diff --git a/setup.cfg b/setup.cfg
index a7dd6d1c3..a42ae68ae 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -98,3 +98,6 @@ strict_equality = True
warn_redundant_casts = True
warn_return_any = True
warn_unused_configs = True
+
+[mypy-_pytest.*]
+check_untyped_defs = True
diff --git a/src/_pytest/capture.py b/src/_pytest/capture.py
index bcc16ceb6..98ba878b3 100644
--- a/src/_pytest/capture.py
+++ b/src/_pytest/capture.py
@@ -519,10 +519,11 @@ class MultiCapture:
def pop_outerr_to_orig(self):
""" pop current snapshot out/err capture and flush to orig streams. """
out, err = self.readouterr()
+ # TODO: Fix type ignores.
if out:
- self.out.writeorg(out)
+ self.out.writeorg(out) # type: ignore[union-attr] # noqa: F821
if err:
- self.err.writeorg(err)
+ self.err.writeorg(err) # type: ignore[union-attr] # noqa: F821
return out, err
def suspend_capturing(self, in_: bool = False) -> None:
@@ -542,7 +543,8 @@ class MultiCapture:
if self.err:
self.err.resume()
if self._in_suspended:
- self.in_.resume()
+ # TODO: Fix type ignore.
+ self.in_.resume() # type: ignore[union-attr] # noqa: F821
self._in_suspended = False
def stop_capturing(self) -> None:
diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py
index ff6aee744..27083900d 100644
--- a/src/_pytest/config/__init__.py
+++ b/src/_pytest/config/__init__.py
@@ -974,7 +974,7 @@ class Config:
self._mark_plugins_for_rewrite(hook)
_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
modules or packages in the distribution package for
@@ -989,7 +989,9 @@ class Config:
package_files = (
str(file)
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 []
)
diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py
index 8aa5d73a8..fa7e3e1df 100644
--- a/src/_pytest/fixtures.py
+++ b/src/_pytest/fixtures.py
@@ -721,7 +721,9 @@ class FixtureRequest:
# this might also be a non-function Item despite its attribute name
return self._pyfuncitem
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:
node = get_scope_node(self._pyfuncitem, scope)
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
# 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
diff --git a/src/_pytest/nodes.py b/src/_pytest/nodes.py
index eaa48e5de..15f91343f 100644
--- a/src/_pytest/nodes.py
+++ b/src/_pytest/nodes.py
@@ -42,6 +42,8 @@ if TYPE_CHECKING:
# Imported here due to circular import.
from _pytest.main import Session
+ from _pytest.warning_types import PytestWarning
+
SEP = "/"
@@ -118,9 +120,9 @@ class Node(metaclass=NodeMeta):
def __init__(
self,
name: str,
- parent: Optional["Node"] = None,
+ parent: "Optional[Node]" = None,
config: Optional[Config] = None,
- session: Optional["Session"] = None,
+ session: "Optional[Session]" = None,
fspath: Optional[py.path.local] = None,
nodeid: Optional[str] = None,
) -> None:
@@ -201,7 +203,7 @@ class Node(metaclass=NodeMeta):
def __repr__(self) -> str:
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.
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)
+ assert lineno is not None
warnings.warn_explicit(
- warning,
- category=None,
- filename=str(path),
- lineno=lineno + 1 if lineno is not None else None,
+ warning, category=None, filename=str(path), lineno=lineno + 1,
)
# methods for ordering nodes
@@ -417,24 +417,26 @@ class Node(metaclass=NodeMeta):
def get_fslocation_from_item(
- item: "Item",
+ node: "Node",
) -> 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)
- * "obj": a Python object that the item wraps.
+ * "location": a pair (path, lineno)
+ * "obj": a Python object that the node wraps.
* "fspath": just a path
:rtype: a tuple of (str|LocalPath, int) with filename and line number.
"""
- try:
- return item.location[:2]
- except AttributeError:
- pass
- obj = getattr(item, "obj", None)
+ # See Item.location.
+ location = getattr(
+ node, "location", None
+ ) # type: Optional[Tuple[str, Optional[int], str]]
+ if location is not None:
+ return location[:2]
+ obj = getattr(node, "obj", None)
if obj is not None:
return getfslineno(obj)
- return getattr(item, "fspath", "unknown location"), -1
+ return getattr(node, "fspath", "unknown location"), -1
class Collector(Node):
diff --git a/src/_pytest/pytester.py b/src/_pytest/pytester.py
index 60df17b90..754ecc10f 100644
--- a/src/_pytest/pytester.py
+++ b/src/_pytest/pytester.py
@@ -1169,8 +1169,10 @@ class Testdir:
popen = subprocess.Popen(cmdargs, stdout=stdout, stderr=stderr, **kw)
if stdin is Testdir.CLOSE_STDIN:
+ assert popen.stdin is not None
popen.stdin.close()
elif isinstance(stdin, bytes):
+ assert popen.stdin is not None
popen.stdin.write(stdin)
return popen
diff --git a/src/_pytest/python.py b/src/_pytest/python.py
index 55ed2b164..41dd8b292 100644
--- a/src/_pytest/python.py
+++ b/src/_pytest/python.py
@@ -64,6 +64,7 @@ from _pytest.warning_types import PytestCollectionWarning
from _pytest.warning_types import PytestUnhandledCoroutineWarning
if TYPE_CHECKING:
+ from typing import Type
from typing_extensions import Literal
from _pytest.fixtures import _Scope
@@ -256,6 +257,18 @@ def pytest_pycollect_makeitem(collector: "PyCollector", name: str, obj):
class PyobjMixin:
_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
def module(self):
"""Python module object this node was collected from (can be None)."""
@@ -292,7 +305,10 @@ class PyobjMixin:
def _getobj(self):
"""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):
""" return python path relative to the containing module. """
@@ -772,7 +788,10 @@ class Instance(PyCollector):
# can be removed at node structure reorganization time
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]]:
self.session._fixturemanager.parsefactories(self)
@@ -1527,7 +1546,8 @@ class Function(PyobjMixin, nodes.Item):
return getimfunc(self.obj)
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
def _pyfuncitem(self):
diff --git a/src/_pytest/python_api.py b/src/_pytest/python_api.py
index 29c8af7e2..abace3196 100644
--- a/src/_pytest/python_api.py
+++ b/src/_pytest/python_api.py
@@ -508,7 +508,7 @@ def approx(expected, rel=None, abs=None, nan_ok=False):
__tracebackhide__ = True
if isinstance(expected, Decimal):
- cls = ApproxDecimal
+ cls = ApproxDecimal # type: Type[ApproxBase]
elif isinstance(expected, Number):
cls = ApproxScalar
elif isinstance(expected, Mapping):
@@ -534,7 +534,7 @@ def _is_numpy_array(obj):
"""
import sys
- np = sys.modules.get("numpy")
+ np = sys.modules.get("numpy") # type: Any
if np is not None:
return isinstance(obj, np.ndarray)
return False
diff --git a/src/_pytest/recwarn.py b/src/_pytest/recwarn.py
index 58b6fbab9..57034be2a 100644
--- a/src/_pytest/recwarn.py
+++ b/src/_pytest/recwarn.py
@@ -136,8 +136,9 @@ class WarningsRecorder(warnings.catch_warnings):
Adapted from `warnings.catch_warnings`.
"""
- def __init__(self):
- super().__init__(record=True)
+ def __init__(self) -> None:
+ # 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._list = [] # type: List[warnings.WarningMessage]