Improve typing of reports' longrepr field
This commit is contained in:
@@ -25,6 +25,7 @@ from _pytest import deprecated
|
||||
from _pytest import nodes
|
||||
from _pytest import timing
|
||||
from _pytest._code.code import ExceptionRepr
|
||||
from _pytest._code.code import ReprFileLocation
|
||||
from _pytest.config import Config
|
||||
from _pytest.config import filename_arg
|
||||
from _pytest.config.argparsing import Parser
|
||||
@@ -200,8 +201,11 @@ class _NodeReporter:
|
||||
self._add_simple("skipped", "xfail-marked test passes unexpectedly")
|
||||
else:
|
||||
assert report.longrepr is not None
|
||||
if getattr(report.longrepr, "reprcrash", None) is not None:
|
||||
message = report.longrepr.reprcrash.message
|
||||
reprcrash = getattr(
|
||||
report.longrepr, "reprcrash", None
|
||||
) # type: Optional[ReprFileLocation]
|
||||
if reprcrash is not None:
|
||||
message = reprcrash.message
|
||||
else:
|
||||
message = str(report.longrepr)
|
||||
message = bin_xml_escape(message)
|
||||
@@ -217,8 +221,11 @@ class _NodeReporter:
|
||||
|
||||
def append_error(self, report: TestReport) -> None:
|
||||
assert report.longrepr is not None
|
||||
if getattr(report.longrepr, "reprcrash", None) is not None:
|
||||
reason = report.longrepr.reprcrash.message
|
||||
reprcrash = getattr(
|
||||
report.longrepr, "reprcrash", None
|
||||
) # type: Optional[ReprFileLocation]
|
||||
if reprcrash is not None:
|
||||
reason = reprcrash.message
|
||||
else:
|
||||
reason = str(report.longrepr)
|
||||
|
||||
@@ -237,7 +244,7 @@ class _NodeReporter:
|
||||
skipped = ET.Element("skipped", type="pytest.xfail", message=xfailreason)
|
||||
self.append(skipped)
|
||||
else:
|
||||
assert report.longrepr is not None
|
||||
assert isinstance(report.longrepr, tuple)
|
||||
filename, lineno, skipreason = report.longrepr
|
||||
if skipreason.startswith("Skipped: "):
|
||||
skipreason = skipreason[9:]
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from io import StringIO
|
||||
from pprint import pprint
|
||||
from typing import Any
|
||||
from typing import cast
|
||||
from typing import Dict
|
||||
from typing import Iterable
|
||||
from typing import Iterator
|
||||
@@ -15,6 +16,7 @@ import py
|
||||
|
||||
from _pytest._code.code import ExceptionChainRepr
|
||||
from _pytest._code.code import ExceptionInfo
|
||||
from _pytest._code.code import ExceptionRepr
|
||||
from _pytest._code.code import ReprEntry
|
||||
from _pytest._code.code import ReprEntryNative
|
||||
from _pytest._code.code import ReprExceptionInfo
|
||||
@@ -57,8 +59,9 @@ _R = TypeVar("_R", bound="BaseReport")
|
||||
class BaseReport:
|
||||
when = None # type: Optional[str]
|
||||
location = None # type: Optional[Tuple[str, Optional[int], str]]
|
||||
# TODO: Improve this Any.
|
||||
longrepr = None # type: Optional[Any]
|
||||
longrepr = (
|
||||
None
|
||||
) # type: Union[None, ExceptionInfo[BaseException], Tuple[str, int, str], str, TerminalRepr]
|
||||
sections = [] # type: List[Tuple[str, str]]
|
||||
nodeid = None # type: str
|
||||
|
||||
@@ -79,7 +82,8 @@ class BaseReport:
|
||||
return
|
||||
|
||||
if hasattr(longrepr, "toterminal"):
|
||||
longrepr.toterminal(out)
|
||||
longrepr_terminal = cast(TerminalRepr, longrepr)
|
||||
longrepr_terminal.toterminal(out)
|
||||
else:
|
||||
try:
|
||||
s = str(longrepr)
|
||||
@@ -233,7 +237,9 @@ class TestReport(BaseReport):
|
||||
location: Tuple[str, Optional[int], str],
|
||||
keywords,
|
||||
outcome: "Literal['passed', 'failed', 'skipped']",
|
||||
longrepr,
|
||||
longrepr: Union[
|
||||
None, ExceptionInfo[BaseException], Tuple[str, int, str], str, TerminalRepr
|
||||
],
|
||||
when: "Literal['setup', 'call', 'teardown']",
|
||||
sections: Iterable[Tuple[str, str]] = (),
|
||||
duration: float = 0,
|
||||
@@ -293,8 +299,9 @@ class TestReport(BaseReport):
|
||||
sections = []
|
||||
if not call.excinfo:
|
||||
outcome = "passed" # type: Literal["passed", "failed", "skipped"]
|
||||
# TODO: Improve this Any.
|
||||
longrepr = None # type: Optional[Any]
|
||||
longrepr = (
|
||||
None
|
||||
) # type: Union[None, ExceptionInfo[BaseException], Tuple[str, int, str], str, TerminalRepr]
|
||||
else:
|
||||
if not isinstance(excinfo, ExceptionInfo):
|
||||
outcome = "failed"
|
||||
@@ -372,7 +379,7 @@ class CollectReport(BaseReport):
|
||||
|
||||
|
||||
class CollectErrorRepr(TerminalRepr):
|
||||
def __init__(self, msg) -> None:
|
||||
def __init__(self, msg: str) -> None:
|
||||
self.longrepr = msg
|
||||
|
||||
def toterminal(self, out: TerminalWriter) -> None:
|
||||
@@ -436,16 +443,18 @@ def _report_to_json(report: BaseReport) -> Dict[str, Any]:
|
||||
else:
|
||||
return None
|
||||
|
||||
def serialize_longrepr(rep: BaseReport) -> Dict[str, Any]:
|
||||
def serialize_exception_longrepr(rep: BaseReport) -> Dict[str, Any]:
|
||||
assert rep.longrepr is not None
|
||||
# TODO: Investigate whether the duck typing is really necessary here.
|
||||
longrepr = cast(ExceptionRepr, rep.longrepr)
|
||||
result = {
|
||||
"reprcrash": serialize_repr_crash(rep.longrepr.reprcrash),
|
||||
"reprtraceback": serialize_repr_traceback(rep.longrepr.reprtraceback),
|
||||
"sections": rep.longrepr.sections,
|
||||
"reprcrash": serialize_repr_crash(longrepr.reprcrash),
|
||||
"reprtraceback": serialize_repr_traceback(longrepr.reprtraceback),
|
||||
"sections": longrepr.sections,
|
||||
} # type: Dict[str, Any]
|
||||
if isinstance(rep.longrepr, ExceptionChainRepr):
|
||||
if isinstance(longrepr, ExceptionChainRepr):
|
||||
result["chain"] = []
|
||||
for repr_traceback, repr_crash, description in rep.longrepr.chain:
|
||||
for repr_traceback, repr_crash, description in longrepr.chain:
|
||||
result["chain"].append(
|
||||
(
|
||||
serialize_repr_traceback(repr_traceback),
|
||||
@@ -462,7 +471,7 @@ def _report_to_json(report: BaseReport) -> Dict[str, Any]:
|
||||
if hasattr(report.longrepr, "reprtraceback") and hasattr(
|
||||
report.longrepr, "reprcrash"
|
||||
):
|
||||
d["longrepr"] = serialize_longrepr(report)
|
||||
d["longrepr"] = serialize_exception_longrepr(report)
|
||||
else:
|
||||
d["longrepr"] = str(report.longrepr)
|
||||
else:
|
||||
|
||||
@@ -85,7 +85,7 @@ class ResultLog:
|
||||
elif report.passed:
|
||||
longrepr = ""
|
||||
elif report.skipped:
|
||||
assert report.longrepr is not None
|
||||
assert isinstance(report.longrepr, tuple)
|
||||
longrepr = str(report.longrepr[2])
|
||||
else:
|
||||
longrepr = str(report.longrepr)
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
import bdb
|
||||
import os
|
||||
import sys
|
||||
from typing import Any
|
||||
from typing import Callable
|
||||
from typing import cast
|
||||
from typing import Dict
|
||||
@@ -22,6 +21,7 @@ from .reports import TestReport
|
||||
from _pytest import timing
|
||||
from _pytest._code.code import ExceptionChainRepr
|
||||
from _pytest._code.code import ExceptionInfo
|
||||
from _pytest._code.code import TerminalRepr
|
||||
from _pytest.compat import TYPE_CHECKING
|
||||
from _pytest.config.argparsing import Parser
|
||||
from _pytest.nodes import Collector
|
||||
@@ -327,8 +327,7 @@ 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")
|
||||
# TODO: Better typing for longrepr.
|
||||
longrepr = None # type: Optional[Any]
|
||||
longrepr = None # type: Union[None, Tuple[str, int, str], str, TerminalRepr]
|
||||
if not call.excinfo:
|
||||
outcome = "passed" # type: Literal["passed", "skipped", "failed"]
|
||||
else:
|
||||
@@ -348,6 +347,7 @@ def pytest_make_collect_report(collector: Collector) -> CollectReport:
|
||||
outcome = "failed"
|
||||
errorinfo = collector.repr_failure(call.excinfo)
|
||||
if not hasattr(errorinfo, "toterminal"):
|
||||
assert isinstance(errorinfo, str)
|
||||
errorinfo = CollectErrorRepr(errorinfo)
|
||||
longrepr = errorinfo
|
||||
result = call.result if not call.excinfo else None
|
||||
|
||||
@@ -1247,6 +1247,7 @@ def _folded_skips(
|
||||
d = {} # type: Dict[Tuple[str, Optional[int], str], List[CollectReport]]
|
||||
for event in skipped:
|
||||
assert event.longrepr is not None
|
||||
assert isinstance(event.longrepr, tuple), (event, event.longrepr)
|
||||
assert len(event.longrepr) == 3, (event, event.longrepr)
|
||||
fspath, lineno, reason = event.longrepr
|
||||
# For consistency, report all fspaths in relative form.
|
||||
|
||||
Reference in New Issue
Block a user