Skip to content

Commit 71dfdca

Browse files
committed
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.
1 parent 848ab00 commit 71dfdca

File tree

9 files changed

+65
-31
lines changed

9 files changed

+65
-31
lines changed

setup.cfg

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,3 +98,6 @@ strict_equality = True
9898
warn_redundant_casts = True
9999
warn_return_any = True
100100
warn_unused_configs = True
101+
102+
[mypy-_pytest.*]
103+
check_untyped_defs = True

src/_pytest/capture.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -519,10 +519,11 @@ def start_capturing(self) -> None:
519519
def pop_outerr_to_orig(self):
520520
""" pop current snapshot out/err capture and flush to orig streams. """
521521
out, err = self.readouterr()
522+
# TODO: Fix type ignores.
522523
if out:
523-
self.out.writeorg(out)
524+
self.out.writeorg(out) # type: ignore[union-attr] # noqa: F821
524525
if err:
525-
self.err.writeorg(err)
526+
self.err.writeorg(err) # type: ignore[union-attr] # noqa: F821
526527
return out, err
527528

528529
def suspend_capturing(self, in_: bool = False) -> None:
@@ -542,7 +543,8 @@ def resume_capturing(self) -> None:
542543
if self.err:
543544
self.err.resume()
544545
if self._in_suspended:
545-
self.in_.resume()
546+
# TODO: Fix type ignore.
547+
self.in_.resume() # type: ignore[union-attr] # noqa: F821
546548
self._in_suspended = False
547549

548550
def stop_capturing(self) -> None:

src/_pytest/config/__init__.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -974,7 +974,7 @@ def _consider_importhook(self, args: Sequence[str]) -> None:
974974
self._mark_plugins_for_rewrite(hook)
975975
_warn_about_missing_assertion(mode)
976976

977-
def _mark_plugins_for_rewrite(self, hook):
977+
def _mark_plugins_for_rewrite(self, hook) -> None:
978978
"""
979979
Given an importhook, mark for rewrite any top-level
980980
modules or packages in the distribution package for
@@ -989,7 +989,9 @@ def _mark_plugins_for_rewrite(self, hook):
989989
package_files = (
990990
str(file)
991991
for dist in importlib_metadata.distributions()
992-
if any(ep.group == "pytest11" for ep in dist.entry_points)
992+
# Type ignored due to missing stub:
993+
# https://github.com/python/typeshed/pull/3795
994+
if any(ep.group == "pytest11" for ep in dist.entry_points) # type: ignore
993995
for file in dist.files or []
994996
)
995997

src/_pytest/fixtures.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -721,7 +721,9 @@ def _getscopeitem(self, scope):
721721
# this might also be a non-function Item despite its attribute name
722722
return self._pyfuncitem
723723
if scope == "package":
724-
node = get_scope_package(self._pyfuncitem, self._fixturedef)
724+
# FIXME: _fixturedef is not defined on FixtureRequest (this class),
725+
# but on FixtureRequest (a subclass).
726+
node = get_scope_package(self._pyfuncitem, self._fixturedef) # type: ignore[attr-defined] # noqa: F821
725727
else:
726728
node = get_scope_node(self._pyfuncitem, scope)
727729
if node is None and scope == "class":
@@ -1158,7 +1160,7 @@ def result(*args, **kwargs):
11581160

11591161
# keep reference to the original function in our own custom attribute so we don't unwrap
11601162
# further than this point and lose useful wrappings like @mock.patch (#3774)
1161-
result.__pytest_wrapped__ = _PytestWrapper(function)
1163+
result.__pytest_wrapped__ = _PytestWrapper(function) # type: ignore[attr-defined] # noqa: F821
11621164

11631165
return result
11641166

src/_pytest/nodes.py

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@
4242

4343
# Imported here due to circular import.
4444
from _pytest.main import Session
45+
from _pytest.warning_types import PytestWarning
46+
4547

4648
SEP = "/"
4749

@@ -118,9 +120,9 @@ class Node(metaclass=NodeMeta):
118120
def __init__(
119121
self,
120122
name: str,
121-
parent: Optional["Node"] = None,
123+
parent: "Optional[Node]" = None,
122124
config: Optional[Config] = None,
123-
session: Optional["Session"] = None,
125+
session: "Optional[Session]" = None,
124126
fspath: Optional[py.path.local] = None,
125127
nodeid: Optional[str] = None,
126128
) -> None:
@@ -201,7 +203,7 @@ def ihook(self):
201203
def __repr__(self) -> str:
202204
return "<{} {}>".format(self.__class__.__name__, getattr(self, "name", None))
203205

204-
def warn(self, warning):
206+
def warn(self, warning: "PytestWarning") -> None:
205207
"""Issue a warning for this item.
206208
207209
Warnings will be displayed after the test session, unless explicitly suppressed
@@ -226,11 +228,9 @@ def warn(self, warning):
226228
)
227229
)
228230
path, lineno = get_fslocation_from_item(self)
231+
assert lineno is not None
229232
warnings.warn_explicit(
230-
warning,
231-
category=None,
232-
filename=str(path),
233-
lineno=lineno + 1 if lineno is not None else None,
233+
warning, category=None, filename=str(path), lineno=lineno + 1,
234234
)
235235

236236
# methods for ordering nodes
@@ -417,24 +417,26 @@ def repr_failure(
417417

418418

419419
def get_fslocation_from_item(
420-
item: "Item",
420+
node: "Node",
421421
) -> Tuple[Union[str, py.path.local], Optional[int]]:
422-
"""Tries to extract the actual location from an item, depending on available attributes:
422+
"""Tries to extract the actual location from a node, depending on available attributes:
423423
424-
* "fslocation": a pair (path, lineno)
425-
* "obj": a Python object that the item wraps.
424+
* "location": a pair (path, lineno)
425+
* "obj": a Python object that the node wraps.
426426
* "fspath": just a path
427427
428428
:rtype: a tuple of (str|LocalPath, int) with filename and line number.
429429
"""
430-
try:
431-
return item.location[:2]
432-
except AttributeError:
433-
pass
434-
obj = getattr(item, "obj", None)
430+
# See Item.location.
431+
location = getattr(
432+
node, "location", None
433+
) # type: Optional[Tuple[str, Optional[int], str]]
434+
if location is not None:
435+
return location[:2]
436+
obj = getattr(node, "obj", None)
435437
if obj is not None:
436438
return getfslineno(obj)
437-
return getattr(item, "fspath", "unknown location"), -1
439+
return getattr(node, "fspath", "unknown location"), -1
438440

439441

440442
class Collector(Node):

src/_pytest/pytester.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1169,8 +1169,10 @@ def popen(
11691169

11701170
popen = subprocess.Popen(cmdargs, stdout=stdout, stderr=stderr, **kw)
11711171
if stdin is Testdir.CLOSE_STDIN:
1172+
assert popen.stdin is not None
11721173
popen.stdin.close()
11731174
elif isinstance(stdin, bytes):
1175+
assert popen.stdin is not None
11741176
popen.stdin.write(stdin)
11751177

11761178
return popen

src/_pytest/python.py

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
from _pytest.warning_types import PytestUnhandledCoroutineWarning
6565

6666
if TYPE_CHECKING:
67+
from typing import Type
6768
from typing_extensions import Literal
6869
from _pytest.fixtures import _Scope
6970

@@ -256,6 +257,18 @@ def pytest_pycollect_makeitem(collector: "PyCollector", name: str, obj):
256257
class PyobjMixin:
257258
_ALLOW_MARKERS = True
258259

260+
# Function and attributes that the mixin needs (for type-checking only).
261+
if TYPE_CHECKING:
262+
name = "" # type: str
263+
parent = None # type: Optional[nodes.Node]
264+
own_markers = [] # type: List[Mark]
265+
266+
def getparent(self, cls: Type[nodes._NodeType]) -> Optional[nodes._NodeType]:
267+
...
268+
269+
def listchain(self) -> List[nodes.Node]:
270+
...
271+
259272
@property
260273
def module(self):
261274
"""Python module object this node was collected from (can be None)."""
@@ -292,7 +305,10 @@ def obj(self, value):
292305

293306
def _getobj(self):
294307
"""Gets the underlying Python object. May be overwritten by subclasses."""
295-
return getattr(self.parent.obj, self.name)
308+
# TODO: Improve the type of `parent` such that assert/ignore aren't needed.
309+
assert self.parent is not None
310+
obj = self.parent.obj # type: ignore[attr-defined] # noqa: F821
311+
return getattr(obj, self.name)
296312

297313
def getmodpath(self, stopatmodule=True, includemodule=False):
298314
""" return python path relative to the containing module. """
@@ -772,7 +788,10 @@ class Instance(PyCollector):
772788
# can be removed at node structure reorganization time
773789

774790
def _getobj(self):
775-
return self.parent.obj()
791+
# TODO: Improve the type of `parent` such that assert/ignore aren't needed.
792+
assert self.parent is not None
793+
obj = self.parent.obj # type: ignore[attr-defined] # noqa: F821
794+
return obj()
776795

777796
def collect(self) -> Iterable[Union[nodes.Item, nodes.Collector]]:
778797
self.session._fixturemanager.parsefactories(self)
@@ -1527,7 +1546,8 @@ def function(self):
15271546
return getimfunc(self.obj)
15281547

15291548
def _getobj(self):
1530-
return getattr(self.parent.obj, self.originalname)
1549+
assert self.parent is not None
1550+
return getattr(self.parent.obj, self.originalname) # type: ignore[attr-defined]
15311551

15321552
@property
15331553
def _pyfuncitem(self):

src/_pytest/python_api.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -508,7 +508,7 @@ def approx(expected, rel=None, abs=None, nan_ok=False):
508508
__tracebackhide__ = True
509509

510510
if isinstance(expected, Decimal):
511-
cls = ApproxDecimal
511+
cls = ApproxDecimal # type: Type[ApproxBase]
512512
elif isinstance(expected, Number):
513513
cls = ApproxScalar
514514
elif isinstance(expected, Mapping):
@@ -534,7 +534,7 @@ def _is_numpy_array(obj):
534534
"""
535535
import sys
536536

537-
np = sys.modules.get("numpy")
537+
np = sys.modules.get("numpy") # type: Any
538538
if np is not None:
539539
return isinstance(obj, np.ndarray)
540540
return False

src/_pytest/recwarn.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,8 +136,9 @@ class WarningsRecorder(warnings.catch_warnings):
136136
Adapted from `warnings.catch_warnings`.
137137
"""
138138

139-
def __init__(self):
140-
super().__init__(record=True)
139+
def __init__(self) -> None:
140+
# Type ignored due to the way typeshed handles warnings.catch_warnings.
141+
super().__init__(record=True) # type: ignore[call-arg] # noqa: F821
141142
self._entered = False
142143
self._list = [] # type: List[warnings.WarningMessage]
143144

0 commit comments

Comments
 (0)