Skip to content

Commit e2bb784

Browse files
committed
Improve types around repr_failure()
1 parent 8da889e commit e2bb784

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
@@ -47,7 +47,7 @@
4747
from typing_extensions import Literal
4848
from weakref import ReferenceType
4949

50-
_TracebackStyle = Literal["long", "short", "line", "no", "native"]
50+
_TracebackStyle = Literal["long", "short", "line", "no", "native", "auto"]
5151

5252

5353
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]
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
@@ -17,9 +17,8 @@
1717

1818
import _pytest._code
1919
from _pytest._code import getfslineno
20-
from _pytest._code.code import ExceptionChainRepr
2120
from _pytest._code.code import ExceptionInfo
22-
from _pytest._code.code import ReprExceptionInfo
21+
from _pytest._code.code import TerminalRepr
2322
from _pytest.compat import cached_property
2423
from _pytest.compat import overload
2524
from _pytest.compat import TYPE_CHECKING
@@ -28,12 +27,10 @@
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:
@@ -42,6 +39,7 @@
4239
# Imported here due to circular import.
4340
from _pytest.main import Session
4441
from _pytest.warning_types import PytestWarning
42+
from _pytest._code.code import _TracebackStyle
4543

4644

4745
SEP = "/"
@@ -354,8 +352,10 @@ def _prunetraceback(self, excinfo):
354352
pass
355353

356354
def _repr_failure_py(
357-
self, excinfo: ExceptionInfo[Union[Failed, FixtureLookupError]], style=None
358-
) -> Union[str, ReprExceptionInfo, ExceptionChainRepr, FixtureLookupErrorRepr]:
355+
self,
356+
excinfo: ExceptionInfo[BaseException],
357+
style: "Optional[_TracebackStyle]" = None,
358+
) -> Union[str, TerminalRepr]:
359359
if isinstance(excinfo.value, fail.Exception):
360360
if not excinfo.value.pytrace:
361361
return str(excinfo.value)
@@ -398,8 +398,10 @@ def _repr_failure_py(
398398
)
399399

400400
def repr_failure(
401-
self, excinfo, style=None
402-
) -> Union[str, ReprExceptionInfo, ExceptionChainRepr, FixtureLookupErrorRepr]:
401+
self,
402+
excinfo: ExceptionInfo[BaseException],
403+
style: "Optional[_TracebackStyle]" = None,
404+
) -> Union[str, TerminalRepr]:
403405
"""
404406
Return a representation of a collection or test failure.
405407
@@ -445,13 +447,16 @@ def collect(self) -> Iterable[Union["Item", "Collector"]]:
445447
"""
446448
raise NotImplementedError("abstract")
447449

448-
def repr_failure(self, excinfo):
450+
# TODO: This omits the style= parameter which breaks Liskov Substitution.
451+
def repr_failure( # type: ignore[override]
452+
self, excinfo: ExceptionInfo[BaseException]
453+
) -> Union[str, TerminalRepr]:
449454
"""
450455
Return a representation of a collection failure.
451456
452457
:param excinfo: Exception information for the failure.
453458
"""
454-
if excinfo.errisinstance(self.CollectError) and not self.config.getoption(
459+
if isinstance(excinfo.value, self.CollectError) and not self.config.getoption(
455460
"fulltrace", False
456461
):
457462
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

@@ -1591,7 +1592,10 @@ def _prunetraceback(self, excinfo: ExceptionInfo) -> None:
15911592
for entry in excinfo.traceback[1:-1]:
15921593
entry.set_repr_style("short")
15931594

1594-
def repr_failure(self, excinfo, outerr=None):
1595+
# TODO: Type ignored -- breaks Liskov Substitution.
1596+
def repr_failure( # type: ignore[override]
1597+
self, excinfo: ExceptionInfo[BaseException], outerr: None = None
1598+
) -> Union[str, TerminalRepr]:
15951599
assert outerr is None, "XXX outerr usage is deprecated"
15961600
style = self.config.getoption("tbstyle", "auto")
15971601
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
@@ -257,7 +258,7 @@ class CallInfo(Generic[_T]):
257258
"""
258259

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

315316
def pytest_make_collect_report(collector: Collector) -> CollectReport:
316317
call = CallInfo.from_call(lambda: list(collector.collect()), "collect")
317-
longrepr = None
318+
# TODO: Better typing for longrepr.
319+
longrepr = None # type: Optional[Any]
318320
if not call.excinfo:
319321
outcome = "passed" # type: Literal["passed", "skipped", "failed"]
320322
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)