From fcada1ea4763c0e3471cd58ac4b89e7c874e6264 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Thu, 13 Apr 2023 13:48:44 +0300 Subject: [PATCH] nodes: change _prunetraceback to return the new traceback instead of modifying excinfo This makes it usable as a general function, and just more understandable in general. --- src/_pytest/nodes.py | 12 +++++++----- src/_pytest/python.py | 15 +++++++++------ src/_pytest/unittest.py | 13 +++++++------ 3 files changed, 23 insertions(+), 17 deletions(-) diff --git a/src/_pytest/nodes.py b/src/_pytest/nodes.py index 738ab97e9..1a1a47a28 100644 --- a/src/_pytest/nodes.py +++ b/src/_pytest/nodes.py @@ -22,6 +22,7 @@ import _pytest._code from _pytest._code import getfslineno from _pytest._code.code import ExceptionInfo from _pytest._code.code import TerminalRepr +from _pytest._code.code import Traceback from _pytest.compat import cached_property from _pytest.compat import LEGACY_PATH from _pytest.config import Config @@ -432,8 +433,8 @@ class Node(metaclass=NodeMeta): assert current is None or isinstance(current, cls) return current - def _prunetraceback(self, excinfo: ExceptionInfo[BaseException]) -> None: - pass + def _traceback_filter(self, excinfo: ExceptionInfo[BaseException]) -> Traceback: + return excinfo.traceback def _repr_failure_py( self, @@ -452,7 +453,7 @@ class Node(metaclass=NodeMeta): if self.config.getoption("fulltrace", False): style = "long" else: - self._prunetraceback(excinfo) + excinfo.traceback = self._traceback_filter(excinfo) if style == "auto": style = "long" # XXX should excinfo.getrepr record all data and toterminal() process it? @@ -554,13 +555,14 @@ class Collector(Node): return self._repr_failure_py(excinfo, style=tbstyle) - def _prunetraceback(self, excinfo: ExceptionInfo[BaseException]) -> None: + def _traceback_filter(self, excinfo: ExceptionInfo[BaseException]) -> Traceback: if hasattr(self, "path"): traceback = excinfo.traceback ntraceback = traceback.cut(path=self.path) if ntraceback == traceback: ntraceback = ntraceback.cut(excludepath=tracebackcutdir) - excinfo.traceback = ntraceback.filter(excinfo) + return excinfo.traceback.filter(excinfo) + return excinfo.traceback def _check_initialpaths_for_relpath(session: "Session", path: Path) -> Optional[str]: diff --git a/src/_pytest/python.py b/src/_pytest/python.py index 8ed6b46df..ae09da48e 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -1802,7 +1802,7 @@ class Function(PyobjMixin, nodes.Item): def setup(self) -> None: self._request._fillfixtures() - def _prunetraceback(self, excinfo: ExceptionInfo[BaseException]) -> None: + def _traceback_filter(self, excinfo: ExceptionInfo[BaseException]) -> Traceback: if hasattr(self, "_obj") and not self.config.getoption("fulltrace", False): code = _pytest._code.Code.from_function(get_real_func(self.obj)) path, firstlineno = code.path, code.firstlineno @@ -1814,19 +1814,22 @@ class Function(PyobjMixin, nodes.Item): ntraceback = ntraceback.filter(filter_traceback) if not ntraceback: ntraceback = traceback + ntraceback = ntraceback.filter(excinfo) - excinfo.traceback = ntraceback.filter(excinfo) # issue364: mark all but first and last frames to # only show a single-line message for each frame. if self.config.getoption("tbstyle", "auto") == "auto": - if len(excinfo.traceback) > 2: - excinfo.traceback = Traceback( + if len(ntraceback) > 2: + ntraceback = Traceback( entry - if i == 0 or i == len(excinfo.traceback) - 1 + if i == 0 or i == len(ntraceback) - 1 else entry.with_repr_style("short") - for i, entry in enumerate(excinfo.traceback) + for i, entry in enumerate(ntraceback) ) + return ntraceback + return excinfo.traceback + # TODO: Type ignored -- breaks Liskov Substitution. def repr_failure( # type: ignore[override] self, diff --git a/src/_pytest/unittest.py b/src/_pytest/unittest.py index 7a5e73661..d42a12a3a 100644 --- a/src/_pytest/unittest.py +++ b/src/_pytest/unittest.py @@ -334,15 +334,16 @@ class TestCaseFunction(Function): finally: delattr(self._testcase, self.name) - def _prunetraceback( + def _traceback_filter( self, excinfo: _pytest._code.ExceptionInfo[BaseException] - ) -> None: - super()._prunetraceback(excinfo) - traceback = excinfo.traceback.filter( + ) -> _pytest._code.Traceback: + traceback = super()._traceback_filter(excinfo) + ntraceback = traceback.filter( lambda x: not x.frame.f_globals.get("__unittest"), ) - if traceback: - excinfo.traceback = traceback + if not ntraceback: + ntraceback = traceback + return ntraceback @hookimpl(tryfirst=True)