Use attrs with all Repr classes (#6739)
Co-authored-by: Ran Benita <ran234@gmail.com>
This commit is contained in:
		
							parent
							
								
									bd7e33277b
								
							
						
					
					
						commit
						b11bfa106c
					
				| 
						 | 
					@ -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:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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":
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue