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 typing_extensions import Literal
from weakref import ReferenceType from weakref import ReferenceType
_TracebackStyle = Literal["long", "short", "line", "no", "native", "value"] _TracebackStyle = Literal["long", "short", "line", "no", "native", "value", "auto"]
class Code: class Code:

View File

@ -300,7 +300,10 @@ class DoctestItem(pytest.Item):
sys.stdout.write(out) sys.stdout.write(out)
sys.stderr.write(err) 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 import doctest
failures = ( failures = (

View File

@ -17,9 +17,8 @@ import py
import _pytest._code import _pytest._code
from _pytest._code import getfslineno from _pytest._code import getfslineno
from _pytest._code.code import ExceptionChainRepr
from _pytest._code.code import ExceptionInfo 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 cached_property
from _pytest.compat import overload from _pytest.compat import overload
from _pytest.compat import TYPE_CHECKING 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.deprecated import NODE_USE_FROM_PARENT
from _pytest.fixtures import FixtureDef from _pytest.fixtures import FixtureDef
from _pytest.fixtures import FixtureLookupError from _pytest.fixtures import FixtureLookupError
from _pytest.fixtures import FixtureLookupErrorRepr
from _pytest.mark.structures import Mark from _pytest.mark.structures import Mark
from _pytest.mark.structures import MarkDecorator from _pytest.mark.structures import MarkDecorator
from _pytest.mark.structures import NodeKeywords from _pytest.mark.structures import NodeKeywords
@ -43,6 +41,7 @@ if TYPE_CHECKING:
# Imported here due to circular import. # Imported here due to circular import.
from _pytest.main import Session from _pytest.main import Session
from _pytest.warning_types import PytestWarning from _pytest.warning_types import PytestWarning
from _pytest._code.code import _TracebackStyle
SEP = "/" SEP = "/"
@ -355,8 +354,10 @@ class Node(metaclass=NodeMeta):
pass pass
def _repr_failure_py( def _repr_failure_py(
self, excinfo: ExceptionInfo[BaseException], style=None, self,
) -> Union[str, ReprExceptionInfo, ExceptionChainRepr, FixtureLookupErrorRepr]: excinfo: ExceptionInfo[BaseException],
style: "Optional[_TracebackStyle]" = None,
) -> TerminalRepr:
if isinstance(excinfo.value, ConftestImportFailure): if isinstance(excinfo.value, ConftestImportFailure):
excinfo = ExceptionInfo(excinfo.value.excinfo) excinfo = ExceptionInfo(excinfo.value.excinfo)
if isinstance(excinfo.value, fail.Exception): if isinstance(excinfo.value, fail.Exception):
@ -406,8 +407,10 @@ class Node(metaclass=NodeMeta):
) )
def repr_failure( def repr_failure(
self, excinfo, style=None self,
) -> Union[str, ReprExceptionInfo, ExceptionChainRepr, FixtureLookupErrorRepr]: excinfo: ExceptionInfo[BaseException],
style: "Optional[_TracebackStyle]" = None,
) -> Union[str, TerminalRepr]:
""" """
Return a representation of a collection or test failure. Return a representation of a collection or test failure.
@ -453,13 +456,16 @@ class Collector(Node):
""" """
raise NotImplementedError("abstract") 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. Return a representation of a collection failure.
:param excinfo: Exception information for the 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 "fulltrace", False
): ):
exc = excinfo.value 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 fail
from _pytest.outcomes import skip from _pytest.outcomes import skip
from _pytest.pathlib import parts from _pytest.pathlib import parts
from _pytest.reports import TerminalRepr
from _pytest.warning_types import PytestCollectionWarning from _pytest.warning_types import PytestCollectionWarning
from _pytest.warning_types import PytestUnhandledCoroutineWarning from _pytest.warning_types import PytestUnhandledCoroutineWarning
@ -1591,7 +1592,10 @@ class Function(PyobjMixin, nodes.Item):
for entry in excinfo.traceback[1:-1]: for entry in excinfo.traceback[1:-1]:
entry.set_repr_style("short") 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" assert outerr is None, "XXX outerr usage is deprecated"
style = self.config.getoption("tbstyle", "auto") style = self.config.getoption("tbstyle", "auto")
if style == "auto": if style == "auto":

View File

@ -2,6 +2,7 @@
import bdb import bdb
import os import os
import sys import sys
from typing import Any
from typing import Callable from typing import Callable
from typing import cast from typing import cast
from typing import Dict from typing import Dict
@ -256,7 +257,7 @@ class CallInfo(Generic[_T]):
""" """
_result = attr.ib(type="Optional[_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) start = attr.ib(type=float)
stop = attr.ib(type=float) stop = attr.ib(type=float)
duration = 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: def pytest_make_collect_report(collector: Collector) -> CollectReport:
call = CallInfo.from_call(lambda: list(collector.collect()), "collect") 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: if not call.excinfo:
outcome = "passed" # type: Literal["passed", "skipped", "failed"] outcome = "passed" # type: Literal["passed", "skipped", "failed"]
else: else:

View File

@ -148,7 +148,8 @@ def pytest_runtest_makereport(item: Item, call: CallInfo[None]):
elif item.config.option.runxfail: elif item.config.option.runxfail:
pass # don't interfere 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.wasxfail = "reason: " + call.excinfo.value.msg
rep.outcome = "skipped" rep.outcome = "skipped"
elif evalxfail and not rep.skipped and evalxfail.wasvalid() and evalxfail.istrue(): elif evalxfail and not rep.skipped and evalxfail.wasvalid() and evalxfail.istrue():