Skip to content

Commit 03e01a7

Browse files
committed
Improve types around repr_failure()
1 parent fa77992 commit 03e01a7

File tree

6 files changed

+31
-16
lines changed

6 files changed

+31
-16
lines changed

src/_pytest/_code/code.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444

4545
from _pytest._code import Source
4646

47-
_TracebackStyle = Literal["long", "short", "line", "no", "native"]
47+
_TracebackStyle = Literal["long", "short", "line", "no", "native", "auto"]
4848

4949

5050
class Code:

src/_pytest/doctest.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,10 @@ def _disable_output_capturing_for_darwin(self) -> None:
300300
sys.stdout.write(out)
301301
sys.stderr.write(err)
302302

303-
def repr_failure(self, excinfo):
303+
# TODO: Type ignored -- breaks Liskov Substitution.
304+
def repr_failure( # type: ignore[override] # noqa: F821
305+
self, excinfo: ExceptionInfo[BaseException],
306+
) -> Union[str, TerminalRepr]:
304307
import doctest
305308

306309
failures = (

src/_pytest/nodes.py

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,8 @@
1616
import py
1717

1818
import _pytest._code
19-
from _pytest._code.code import ExceptionChainRepr
2019
from _pytest._code.code import ExceptionInfo
21-
from _pytest._code.code import ReprExceptionInfo
20+
from _pytest._code.code import TerminalRepr
2221
from _pytest._code.source import getfslineno
2322
from _pytest.compat import cached_property
2423
from _pytest.compat import overload
@@ -28,19 +27,18 @@
2827
from _pytest.deprecated import NODE_USE_FROM_PARENT
2928
from _pytest.fixtures import FixtureDef
3029
from _pytest.fixtures import FixtureLookupError
31-
from _pytest.fixtures import FixtureLookupErrorRepr
3230
from _pytest.mark.structures import Mark
3331
from _pytest.mark.structures import MarkDecorator
3432
from _pytest.mark.structures import NodeKeywords
3533
from _pytest.outcomes import fail
36-
from _pytest.outcomes import Failed
3734
from _pytest.store import Store
3835

3936
if TYPE_CHECKING:
4037
from typing import Type
4138

4239
from _pytest.main import Session
4340
from _pytest.warning_types import PytestWarning
41+
from _pytest._code.code import _TracebackStyle
4442

4543
SEP = "/"
4644

@@ -339,8 +337,10 @@ def _prunetraceback(self, excinfo):
339337
pass
340338

341339
def _repr_failure_py(
342-
self, excinfo: ExceptionInfo[Union[Failed, FixtureLookupError]], style=None
343-
) -> Union[str, ReprExceptionInfo, ExceptionChainRepr, FixtureLookupErrorRepr]:
340+
self,
341+
excinfo: ExceptionInfo[BaseException],
342+
style: "Optional[_TracebackStyle]" = None,
343+
) -> Union[str, TerminalRepr]:
344344
if isinstance(excinfo.value, fail.Exception):
345345
if not excinfo.value.pytrace:
346346
return str(excinfo.value)
@@ -383,8 +383,10 @@ def _repr_failure_py(
383383
)
384384

385385
def repr_failure(
386-
self, excinfo, style=None
387-
) -> Union[str, ReprExceptionInfo, ExceptionChainRepr, FixtureLookupErrorRepr]:
386+
self,
387+
excinfo: ExceptionInfo[BaseException],
388+
style: "Optional[_TracebackStyle]" = None,
389+
) -> Union[str, TerminalRepr]:
388390
"""
389391
Return a representation of a collection or test failure.
390392
@@ -430,13 +432,16 @@ def collect(self) -> Iterable[Union["Item", "Collector"]]:
430432
"""
431433
raise NotImplementedError("abstract")
432434

433-
def repr_failure(self, excinfo):
435+
# TODO: This omits the style= parameter which breaks Liskov Substitution.
436+
def repr_failure( # type: ignore[override] # noqa: F821
437+
self, excinfo: ExceptionInfo[BaseException]
438+
) -> Union[str, TerminalRepr]:
434439
"""
435440
Return a representation of a collection failure.
436441
437442
:param excinfo: Exception information for the failure.
438443
"""
439-
if excinfo.errisinstance(self.CollectError) and not self.config.getoption(
444+
if isinstance(excinfo.value, self.CollectError) and not self.config.getoption(
440445
"fulltrace", False
441446
):
442447
exc = excinfo.value

src/_pytest/python.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
from _pytest.outcomes import fail
6161
from _pytest.outcomes import skip
6262
from _pytest.pathlib import parts
63+
from _pytest.reports import TerminalRepr
6364
from _pytest.warning_types import PytestCollectionWarning
6465
from _pytest.warning_types import PytestUnhandledCoroutineWarning
6566

@@ -1629,7 +1630,10 @@ def _prunetraceback(self, excinfo: ExceptionInfo) -> None:
16291630
for entry in excinfo.traceback[1:-1]:
16301631
entry.set_repr_style("short")
16311632

1632-
def repr_failure(self, excinfo, outerr=None):
1633+
# TODO: Type ignored -- breaks Liskov Substitution.
1634+
def repr_failure( # type: ignore[override] # noqa: F821
1635+
self, excinfo: ExceptionInfo[BaseException], outerr: None = None
1636+
) -> Union[str, TerminalRepr]:
16331637
assert outerr is None, "XXX outerr usage is deprecated"
16341638
style = self.config.getoption("tbstyle", "auto")
16351639
if style == "auto":

src/_pytest/runner.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import sys
55
from time import perf_counter # Intentionally not `import time` to avoid being
66
from time import time # affected by tests which monkeypatch `time` (issue #185).
7+
from typing import Any
78
from typing import Callable
89
from typing import cast
910
from typing import Dict
@@ -256,7 +257,7 @@ class CallInfo(Generic[_T]):
256257
"""
257258

258259
_result = attr.ib(type="Optional[_T]")
259-
excinfo = attr.ib(type=Optional[ExceptionInfo])
260+
excinfo = attr.ib(type=Optional[ExceptionInfo[BaseException]])
260261
start = attr.ib(type=float)
261262
stop = attr.ib(type=float)
262263
duration = attr.ib(type=float)
@@ -313,7 +314,8 @@ def pytest_runtest_makereport(item: Item, call: CallInfo[None]) -> TestReport:
313314

314315
def pytest_make_collect_report(collector: Collector) -> CollectReport:
315316
call = CallInfo.from_call(lambda: list(collector.collect()), "collect")
316-
longrepr = None
317+
# TODO: Better typing for longrepr.
318+
longrepr = None # type: Optional[Any]
317319
if not call.excinfo:
318320
outcome = "passed" # type: Literal["passed", "skipped", "failed"]
319321
else:

src/_pytest/skipping.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,8 @@ def pytest_runtest_makereport(item: Item, call: CallInfo[None]):
148148

149149
elif item.config.option.runxfail:
150150
pass # don't interfere
151-
elif call.excinfo and call.excinfo.errisinstance(xfail.Exception):
151+
elif call.excinfo and isinstance(call.excinfo.value, xfail.Exception):
152+
assert call.excinfo.value.msg is not None
152153
rep.wasxfail = "reason: " + call.excinfo.value.msg
153154
rep.outcome = "skipped"
154155
elif evalxfail and not rep.skipped and evalxfail.wasvalid() and evalxfail.istrue():

0 commit comments

Comments
 (0)