Use attrs with all Repr classes (#6739)

Co-authored-by: Ran Benita <ran234@gmail.com>
This commit is contained in:
Daniel Hahler 2020-03-03 21:53:28 +01:00 committed by GitHub
parent bd7e33277b
commit b11bfa106c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 35 additions and 43 deletions

View File

@ -789,9 +789,9 @@ class FormattedExcinfo:
else: else:
message = excinfo and excinfo.typename or "" message = excinfo and excinfo.typename or ""
path = self._makepath(entry.path) path = self._makepath(entry.path)
filelocrepr = ReprFileLocation(path, entry.lineno + 1, message) reprfileloc = ReprFileLocation(path, entry.lineno + 1, message)
localsrepr = self.repr_locals(entry.locals) localsrepr = self.repr_locals(entry.locals)
return ReprEntry(lines, reprargs, localsrepr, filelocrepr, style) return ReprEntry(lines, reprargs, localsrepr, reprfileloc, style)
if excinfo: if excinfo:
lines.extend(self.get_exconly(excinfo, indent=4)) lines.extend(self.get_exconly(excinfo, indent=4))
return ReprEntry(lines, None, None, None, style) return ReprEntry(lines, None, None, None, style)
@ -911,6 +911,7 @@ class FormattedExcinfo:
return ExceptionChainRepr(repr_chain) return ExceptionChainRepr(repr_chain)
@attr.s
class TerminalRepr: class TerminalRepr:
def __str__(self) -> str: def __str__(self) -> str:
# FYI this is called from pytest-xdist's serialization of exception # FYI this is called from pytest-xdist's serialization of exception
@ -927,8 +928,9 @@ class TerminalRepr:
raise NotImplementedError() raise NotImplementedError()
@attr.s
class ExceptionRepr(TerminalRepr): class ExceptionRepr(TerminalRepr):
def __init__(self) -> None: def __attrs_post_init__(self):
self.sections = [] # type: List[Tuple[str, str, str]] self.sections = [] # type: List[Tuple[str, str, str]]
def addsection(self, name: str, content: str, sep: str = "-") -> None: def addsection(self, name: str, content: str, sep: str = "-") -> None:
@ -940,19 +942,20 @@ class ExceptionRepr(TerminalRepr):
tw.line(content) tw.line(content)
@attr.s
class ExceptionChainRepr(ExceptionRepr): class ExceptionChainRepr(ExceptionRepr):
def __init__( chain = attr.ib(
self, type=Sequence[
chain: Sequence[
Tuple["ReprTraceback", Optional["ReprFileLocation"], Optional[str]] Tuple["ReprTraceback", Optional["ReprFileLocation"], Optional[str]]
], ]
) -> None: )
super().__init__()
self.chain = chain def __attrs_post_init__(self):
super().__attrs_post_init__()
# reprcrash and reprtraceback of the outermost (the newest) exception # reprcrash and reprtraceback of the outermost (the newest) exception
# in the chain # in the chain
self.reprtraceback = chain[-1][0] self.reprtraceback = self.chain[-1][0]
self.reprcrash = chain[-1][1] self.reprcrash = self.chain[-1][1]
def toterminal(self, tw: TerminalWriter) -> None: def toterminal(self, tw: TerminalWriter) -> None:
for element in self.chain: for element in self.chain:
@ -963,13 +966,10 @@ class ExceptionChainRepr(ExceptionRepr):
super().toterminal(tw) super().toterminal(tw)
@attr.s
class ReprExceptionInfo(ExceptionRepr): class ReprExceptionInfo(ExceptionRepr):
def __init__( reprtraceback = attr.ib(type="ReprTraceback")
self, reprtraceback: "ReprTraceback", reprcrash: "ReprFileLocation" reprcrash = attr.ib(type="ReprFileLocation")
) -> None:
super().__init__()
self.reprtraceback = reprtraceback
self.reprcrash = reprcrash
def toterminal(self, tw: TerminalWriter) -> None: def toterminal(self, tw: TerminalWriter) -> None:
self.reprtraceback.toterminal(tw) self.reprtraceback.toterminal(tw)
@ -1010,30 +1010,22 @@ class ReprTracebackNative(ReprTraceback):
self.extraline = None self.extraline = None
@attr.s
class ReprEntryNative(TerminalRepr): class ReprEntryNative(TerminalRepr):
lines = attr.ib(type=Sequence[str])
style = "native" # type: _TracebackStyle style = "native" # type: _TracebackStyle
def __init__(self, tblines: Sequence[str]) -> None:
self.lines = tblines
def toterminal(self, tw: TerminalWriter) -> None: def toterminal(self, tw: TerminalWriter) -> None:
tw.write("".join(self.lines)) tw.write("".join(self.lines))
@attr.s
class ReprEntry(TerminalRepr): class ReprEntry(TerminalRepr):
def __init__( lines = attr.ib(type=Sequence[str])
self, reprfuncargs = attr.ib(type=Optional["ReprFuncArgs"])
lines: Sequence[str], reprlocals = attr.ib(type=Optional["ReprLocals"])
reprfuncargs: Optional["ReprFuncArgs"], reprfileloc = attr.ib(type=Optional["ReprFileLocation"])
reprlocals: Optional["ReprLocals"], style = attr.ib(type="_TracebackStyle")
filelocrepr: Optional["ReprFileLocation"],
style: "_TracebackStyle",
) -> None:
self.lines = lines
self.reprfuncargs = reprfuncargs
self.reprlocals = reprlocals
self.reprfileloc = filelocrepr
self.style = style
def _write_entry_lines(self, tw: TerminalWriter) -> None: def _write_entry_lines(self, tw: TerminalWriter) -> None:
"""Writes the source code portions of a list of traceback entries with syntax highlighting. """Writes the source code portions of a list of traceback entries with syntax highlighting.
@ -1118,18 +1110,18 @@ class ReprFileLocation(TerminalRepr):
tw.line(":{}: {}".format(self.lineno, msg)) tw.line(":{}: {}".format(self.lineno, msg))
@attr.s
class ReprLocals(TerminalRepr): class ReprLocals(TerminalRepr):
def __init__(self, lines: Sequence[str]) -> None: lines = attr.ib(type=Sequence[str])
self.lines = lines
def toterminal(self, tw: TerminalWriter, indent="") -> None: def toterminal(self, tw: TerminalWriter, indent="") -> None:
for line in self.lines: for line in self.lines:
tw.line(indent + line) tw.line(indent + line)
@attr.s
class ReprFuncArgs(TerminalRepr): class ReprFuncArgs(TerminalRepr):
def __init__(self, args: Sequence[Tuple[str, object]]) -> None: args = attr.ib(type=Sequence[Tuple[str, object]])
self.args = args
def toterminal(self, tw: TerminalWriter) -> None: def toterminal(self, tw: TerminalWriter) -> None:
if self.args: if self.args:

View File

@ -369,10 +369,10 @@ def _report_to_json(report):
""" """
def serialize_repr_entry(entry): def serialize_repr_entry(entry):
entry_data = {"type": type(entry).__name__, "data": entry.__dict__.copy()} entry_data = {"type": type(entry).__name__, "data": attr.asdict(entry)}
for key, value in entry_data["data"].items(): for key, value in entry_data["data"].items():
if hasattr(value, "__dict__"): if hasattr(value, "__dict__"):
entry_data["data"][key] = value.__dict__.copy() entry_data["data"][key] = attr.asdict(value)
return entry_data return entry_data
def serialize_repr_traceback(reprtraceback: ReprTraceback): def serialize_repr_traceback(reprtraceback: ReprTraceback):
@ -451,7 +451,7 @@ def _report_kwargs_from_json(reportdict):
lines=data["lines"], lines=data["lines"],
reprfuncargs=reprfuncargs, reprfuncargs=reprfuncargs,
reprlocals=reprlocals, reprlocals=reprlocals,
filelocrepr=reprfileloc, reprfileloc=reprfileloc,
style=data["style"], style=data["style"],
) # type: Union[ReprEntry, ReprEntryNative] ) # type: Union[ReprEntry, ReprEntryNative]
elif entry_type == "ReprEntryNative": elif entry_type == "ReprEntryNative":

View File

@ -14,8 +14,8 @@ import attr
from .reports import CollectErrorRepr from .reports import CollectErrorRepr
from .reports import CollectReport from .reports import CollectReport
from .reports import TestReport from .reports import TestReport
from _pytest._code.code import ExceptionChainRepr
from _pytest._code.code import ExceptionInfo from _pytest._code.code import ExceptionInfo
from _pytest._code.code import ExceptionRepr
from _pytest.compat import TYPE_CHECKING from _pytest.compat import TYPE_CHECKING
from _pytest.nodes import Collector from _pytest.nodes import Collector
from _pytest.nodes import Node from _pytest.nodes import Node
@ -276,7 +276,7 @@ def pytest_make_collect_report(collector: Collector) -> CollectReport:
if call.excinfo.errisinstance(tuple(skip_exceptions)): if call.excinfo.errisinstance(tuple(skip_exceptions)):
outcome = "skipped" outcome = "skipped"
r_ = collector._repr_failure_py(call.excinfo, "line") r_ = collector._repr_failure_py(call.excinfo, "line")
assert isinstance(r_, ExceptionRepr), r_ assert isinstance(r_, ExceptionChainRepr), repr(r_)
r = r_.reprcrash r = r_.reprcrash
assert r assert r
longrepr = (str(r.path), r.lineno, r.message) longrepr = (str(r.path), r.lineno, r.message)