Skip to content

Commit

Permalink
Use attrs with all Repr classes
Browse files Browse the repository at this point in the history
Co-authored-by: Ran Benita <ran234@gmail.com>

(cherry picked from commit b11bfa1)

Ref: pytest-dev#6739

Conflicts:
	src/_pytest/_code/code.py
  • Loading branch information
blueyed committed Nov 8, 2020
1 parent 1f974e0 commit abf5a80
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 43 deletions.
68 changes: 30 additions & 38 deletions src/_pytest/_code/code.py
Expand Up @@ -820,9 +820,9 @@ def repr_traceback_entry(
else:
message = ""
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)
return ReprEntry(lines, reprargs, localsrepr, filelocrepr, style)
return ReprEntry(lines, reprargs, localsrepr, reprfileloc, style)
if excinfo:
lines.extend(self.get_exconly(excinfo, indent=4))
return ReprEntry(lines, None, None, None, style)
Expand Down Expand Up @@ -942,6 +942,7 @@ def repr_excinfo(self, excinfo: ExceptionInfo) -> "ExceptionChainRepr":
return ExceptionChainRepr(repr_chain)


@attr.s
class TerminalRepr:
def __str__(self) -> str:
# FYI this is called from pytest-xdist's serialization of exception
Expand All @@ -958,8 +959,9 @@ def toterminal(self, tw: TerminalWriter) -> None:
raise NotImplementedError()


@attr.s
class ExceptionRepr(TerminalRepr):
def __init__(self) -> None:
def __attrs_post_init__(self):
self.sections = [] # type: List[Tuple[str, str, str]]

def addsection(self, name: str, content: str, sep: str = "-") -> None:
Expand All @@ -971,19 +973,20 @@ def toterminal(self, tw: TerminalWriter) -> None:
tw.line(content)


@attr.s
class ExceptionChainRepr(ExceptionRepr):
def __init__(
self,
chain: Sequence[
chain = attr.ib(
type=Sequence[
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
# in the chain
self.reprtraceback = chain[-1][0]
self.reprcrash = chain[-1][1]
self.reprtraceback = self.chain[-1][0]
self.reprcrash = self.chain[-1][1]

def toterminal(self, tw: TerminalWriter) -> None:
for element in self.chain:
Expand All @@ -994,13 +997,10 @@ def toterminal(self, tw: TerminalWriter) -> None:
super().toterminal(tw)


@attr.s
class ReprExceptionInfo(ExceptionRepr):
def __init__(
self, reprtraceback: "ReprTraceback", reprcrash: "ReprFileLocation"
) -> None:
super().__init__()
self.reprtraceback = reprtraceback
self.reprcrash = reprcrash
reprtraceback = attr.ib(type="ReprTraceback")
reprcrash = attr.ib(type="ReprFileLocation")

def toterminal(self, tw: TerminalWriter) -> None:
self.reprtraceback.toterminal(tw)
Expand Down Expand Up @@ -1041,30 +1041,22 @@ def __init__(self, tblines: Sequence[str]) -> None:
self.extraline = None


@attr.s
class ReprEntryNative(TerminalRepr):
lines = attr.ib(type=Sequence[str])
style = "native" # type: _TracebackStyle

def __init__(self, tblines: Sequence[str]) -> None:
self.lines = tblines

def toterminal(self, tw: TerminalWriter) -> None:
tw.write("".join(self.lines))


@attr.s
class ReprEntry(TerminalRepr):
def __init__(
self,
lines: Sequence[str],
reprfuncargs: Optional["ReprFuncArgs"],
reprlocals: Optional["ReprLocals"],
filelocrepr: Optional["ReprFileLocation"],
style: "_TracebackStyle",
) -> None:
self.lines = lines
self.reprfuncargs = reprfuncargs
self.reprlocals = reprlocals
self.reprfileloc = filelocrepr
self.style = style
lines = attr.ib(type=Sequence[str])
reprfuncargs = attr.ib(type=Optional["ReprFuncArgs"])
reprlocals = attr.ib(type=Optional["ReprLocals"])
reprfileloc = attr.ib(type=Optional["ReprFileLocation"])

This comment has been minimized.

Copy link
@blueyed

blueyed Nov 9, 2020

Author Owner

NOTE: (internal) API change, actually used by others (see references in pytest-dev#6739). Would be good to support both the old and new (for current pytest) name for compat.

style = attr.ib(type="_TracebackStyle")

@staticmethod
def _color_error_lines(tw: TerminalWriter, lines: Sequence[str]) -> None:
Expand Down Expand Up @@ -1147,18 +1139,18 @@ def toterminal(self, tw: TerminalWriter, style: "_TracebackStyle" = None) -> Non
tw.line(": {}".format(self._get_short_msg()))


@attr.s
class ReprLocals(TerminalRepr):
def __init__(self, lines: Sequence[str]) -> None:
self.lines = lines
lines = attr.ib(type=Sequence[str])

def toterminal(self, tw: TerminalWriter, indent="") -> None:
for line in self.lines:
tw.line(indent + line)


@attr.s
class ReprFuncArgs(TerminalRepr):
def __init__(self, args: Sequence[Tuple[str, object]]) -> None:
self.args = args
args = attr.ib(type=Sequence[Tuple[str, object]])

def toterminal(self, tw: TerminalWriter) -> None:
if self.args:
Expand Down
6 changes: 3 additions & 3 deletions src/_pytest/reports.py
Expand Up @@ -364,10 +364,10 @@ def _report_to_json(report):
"""

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():
if hasattr(value, "__dict__"):
entry_data["data"][key] = value.__dict__.copy()
entry_data["data"][key] = attr.asdict(value)
return entry_data

def serialize_repr_traceback(reprtraceback: ReprTraceback):
Expand Down Expand Up @@ -446,7 +446,7 @@ def deserialize_repr_entry(entry_data):
lines=data["lines"],
reprfuncargs=reprfuncargs,
reprlocals=reprlocals,
filelocrepr=reprfileloc,
reprfileloc=reprfileloc,
style=data["style"],
) # type: Union[ReprEntry, ReprEntryNative]
elif entry_type == "ReprEntryNative":
Expand Down
4 changes: 2 additions & 2 deletions src/_pytest/runner.py
Expand Up @@ -13,8 +13,8 @@
from .reports import CollectErrorRepr
from .reports import CollectReport
from .reports import TestReport
from _pytest._code.code import ExceptionChainRepr
from _pytest._code.code import ExceptionInfo
from _pytest._code.code import ExceptionRepr
from _pytest.compat import TYPE_CHECKING
from _pytest.nodes import Collector
from _pytest.nodes import Node
Expand Down Expand Up @@ -285,7 +285,7 @@ def pytest_make_collect_report(collector: Collector) -> CollectReport:
if call.excinfo.errisinstance(tuple(skip_exceptions)):
outcome = "skipped"
r_ = collector._repr_failure_py(call.excinfo, "line")
assert isinstance(r_, ExceptionRepr), r_
assert isinstance(r_, ExceptionChainRepr), repr(r_)
r = r_.reprcrash
assert r
longrepr = (str(r.path), r.lineno, r.message)
Expand Down

0 comments on commit abf5a80

Please sign in to comment.