Improve types around repr_failure()

This commit is contained in:
Ran Benita 2020-05-01 14:40:17 +03:00
parent 54ad048be7
commit 2b05faff0a
6 changed files with 31 additions and 15 deletions

View File

@ -47,7 +47,7 @@ if TYPE_CHECKING:
from typing_extensions import Literal
from weakref import ReferenceType
_TracebackStyle = Literal["long", "short", "line", "no", "native", "value"]
_TracebackStyle = Literal["long", "short", "line", "no", "native", "value", "auto"]
class Code:

View File

@ -300,7 +300,10 @@ class DoctestItem(pytest.Item):
sys.stdout.write(out)
sys.stderr.write(err)
def repr_failure(self, excinfo):
# TODO: Type ignored -- breaks Liskov Substitution.
def repr_failure( # type: ignore[override] # noqa: F821
self, excinfo: ExceptionInfo[BaseException],
) -> Union[str, TerminalRepr]:
import doctest
failures = (

View File

@ -17,9 +17,8 @@ import py
import _pytest._code
from _pytest._code import getfslineno
from _pytest._code.code import ExceptionChainRepr
from _pytest._code.code import ExceptionInfo
from _pytest._code.code import ReprExceptionInfo
from _pytest._code.code import TerminalRepr
from _pytest.compat import cached_property
from _pytest.compat import overload
from _pytest.compat import TYPE_CHECKING
@ -29,7 +28,6 @@ from _pytest.config import PytestPluginManager
from _pytest.deprecated import NODE_USE_FROM_PARENT
from _pytest.fixtures import FixtureDef
from _pytest.fixtures import FixtureLookupError
from _pytest.fixtures import FixtureLookupErrorRepr
from _pytest.mark.structures import Mark
from _pytest.mark.structures import MarkDecorator
from _pytest.mark.structures import NodeKeywords
@ -43,6 +41,7 @@ if TYPE_CHECKING:
# Imported here due to circular import.
from _pytest.main import Session
from _pytest.warning_types import PytestWarning
from _pytest._code.code import _TracebackStyle
SEP = "/"
@ -355,8 +354,10 @@ class Node(metaclass=NodeMeta):
pass
def _repr_failure_py(
self, excinfo: ExceptionInfo[BaseException], style=None,
) -> Union[str, ReprExceptionInfo, ExceptionChainRepr, FixtureLookupErrorRepr]:
self,
excinfo: ExceptionInfo[BaseException],
style: "Optional[_TracebackStyle]" = None,
) -> TerminalRepr:
if isinstance(excinfo.value, ConftestImportFailure):
excinfo = ExceptionInfo(excinfo.value.excinfo)
if isinstance(excinfo.value, fail.Exception):
@ -406,8 +407,10 @@ class Node(metaclass=NodeMeta):
)
def repr_failure(
self, excinfo, style=None
) -> Union[str, ReprExceptionInfo, ExceptionChainRepr, FixtureLookupErrorRepr]:
self,
excinfo: ExceptionInfo[BaseException],
style: "Optional[_TracebackStyle]" = None,
) -> Union[str, TerminalRepr]:
"""
Return a representation of a collection or test failure.
@ -453,13 +456,16 @@ class Collector(Node):
"""
raise NotImplementedError("abstract")
def repr_failure(self, excinfo):
# TODO: This omits the style= parameter which breaks Liskov Substitution.
def repr_failure( # type: ignore[override] # noqa: F821
self, excinfo: ExceptionInfo[BaseException]
) -> Union[str, TerminalRepr]:
"""
Return a representation of a collection failure.
:param excinfo: Exception information for the failure.
"""
if excinfo.errisinstance(self.CollectError) and not self.config.getoption(
if isinstance(excinfo.value, self.CollectError) and not self.config.getoption(
"fulltrace", False
):
exc = excinfo.value

View File

@ -60,6 +60,7 @@ from _pytest.mark.structures import normalize_mark_list
from _pytest.outcomes import fail
from _pytest.outcomes import skip
from _pytest.pathlib import parts
from _pytest.reports import TerminalRepr
from _pytest.warning_types import PytestCollectionWarning
from _pytest.warning_types import PytestUnhandledCoroutineWarning
@ -1591,7 +1592,10 @@ class Function(PyobjMixin, nodes.Item):
for entry in excinfo.traceback[1:-1]:
entry.set_repr_style("short")
def repr_failure(self, excinfo, outerr=None):
# TODO: Type ignored -- breaks Liskov Substitution.
def repr_failure( # type: ignore[override] # noqa: F821
self, excinfo: ExceptionInfo[BaseException], outerr: None = None
) -> Union[str, TerminalRepr]:
assert outerr is None, "XXX outerr usage is deprecated"
style = self.config.getoption("tbstyle", "auto")
if style == "auto":

View File

@ -2,6 +2,7 @@
import bdb
import os
import sys
from typing import Any
from typing import Callable
from typing import cast
from typing import Dict
@ -256,7 +257,7 @@ class CallInfo(Generic[_T]):
"""
_result = attr.ib(type="Optional[_T]")
excinfo = attr.ib(type=Optional[ExceptionInfo])
excinfo = attr.ib(type=Optional[ExceptionInfo[BaseException]])
start = attr.ib(type=float)
stop = attr.ib(type=float)
duration = attr.ib(type=float)
@ -313,7 +314,8 @@ def pytest_runtest_makereport(item: Item, call: CallInfo[None]) -> TestReport:
def pytest_make_collect_report(collector: Collector) -> CollectReport:
call = CallInfo.from_call(lambda: list(collector.collect()), "collect")
longrepr = None
# TODO: Better typing for longrepr.
longrepr = None # type: Optional[Any]
if not call.excinfo:
outcome = "passed" # type: Literal["passed", "skipped", "failed"]
else:

View File

@ -148,7 +148,8 @@ def pytest_runtest_makereport(item: Item, call: CallInfo[None]):
elif item.config.option.runxfail:
pass # don't interfere
elif call.excinfo and call.excinfo.errisinstance(xfail.Exception):
elif call.excinfo and isinstance(call.excinfo.value, xfail.Exception):
assert call.excinfo.value.msg is not None
rep.wasxfail = "reason: " + call.excinfo.value.msg
rep.outcome = "skipped"
elif evalxfail and not rep.skipped and evalxfail.wasvalid() and evalxfail.istrue():