Skip to content

Commit 6f7f89f

Browse files
committed
code: make TracebackEntry immutable
TracebackEntry being mutable caught me by surprise and makes reasoning about the exception formatting code harder. Make it a proper value.
1 parent 0a20452 commit 6f7f89f

File tree

3 files changed

+20
-10
lines changed

3 files changed

+20
-10
lines changed

src/_pytest/_code/code.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
from _pytest.pathlib import bestrelpath
5050

5151
if TYPE_CHECKING:
52+
from typing_extensions import Final
5253
from typing_extensions import Literal
5354
from typing_extensions import SupportsIndex
5455

@@ -197,18 +198,20 @@ class TracebackEntry:
197198
def __init__(
198199
self,
199200
rawentry: TracebackType,
201+
repr_style: Optional['Literal["short", "long"]'] = None,
200202
) -> None:
201-
self._rawentry = rawentry
202-
self._repr_style: Optional['Literal["short", "long"]'] = None
203+
self._rawentry: "Final" = rawentry
204+
self._repr_style: "Final" = repr_style
205+
206+
def with_repr_style(
207+
self, repr_style: Optional['Literal["short", "long"]']
208+
) -> "TracebackEntry":
209+
return TracebackEntry(self._rawentry, repr_style)
203210

204211
@property
205212
def lineno(self) -> int:
206213
return self._rawentry.tb_lineno - 1
207214

208-
def set_repr_style(self, mode: "Literal['short', 'long']") -> None:
209-
assert mode in ("short", "long")
210-
self._repr_style = mode
211-
212215
@property
213216
def frame(self) -> Frame:
214217
return Frame(self._rawentry.tb_frame)

src/_pytest/python.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
from _pytest._code import getfslineno
3636
from _pytest._code.code import ExceptionInfo
3737
from _pytest._code.code import TerminalRepr
38+
from _pytest._code.code import Traceback
3839
from _pytest._io import TerminalWriter
3940
from _pytest._io.saferepr import saferepr
4041
from _pytest.compat import ascii_escaped
@@ -1819,8 +1820,12 @@ def _prunetraceback(self, excinfo: ExceptionInfo[BaseException]) -> None:
18191820
# only show a single-line message for each frame.
18201821
if self.config.getoption("tbstyle", "auto") == "auto":
18211822
if len(excinfo.traceback) > 2:
1822-
for entry in excinfo.traceback[1:-1]:
1823-
entry.set_repr_style("short")
1823+
excinfo.traceback = Traceback(
1824+
entry
1825+
if i == 0 or i == len(excinfo.traceback) - 1
1826+
else entry.with_repr_style("short")
1827+
for i, entry in enumerate(excinfo.traceback)
1828+
)
18241829

18251830
# TODO: Type ignored -- breaks Liskov Substitution.
18261831
def repr_failure( # type: ignore[override]

testing/code/test_excinfo.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1122,8 +1122,10 @@ def i():
11221122
)
11231123
excinfo = pytest.raises(ValueError, mod.f)
11241124
excinfo.traceback = excinfo.traceback.filter(excinfo)
1125-
excinfo.traceback[1].set_repr_style("short")
1126-
excinfo.traceback[2].set_repr_style("short")
1125+
excinfo.traceback = _pytest._code.Traceback(
1126+
entry if i not in (1, 2) else entry.with_repr_style("short")
1127+
for i, entry in enumerate(excinfo.traceback)
1128+
)
11271129
r = excinfo.getrepr(style="long")
11281130
r.toterminal(tw_mock)
11291131
for line in tw_mock.lines:

0 commit comments

Comments
 (0)