diff --git a/src/_pytest/_code/code.py b/src/_pytest/_code/code.py index 4fcce0427ef..6ee56106df9 100644 --- a/src/_pytest/_code/code.py +++ b/src/_pytest/_code/code.py @@ -644,10 +644,13 @@ def __repr__(self) -> str: def exconly(self, tryshort: bool = False) -> str: """Return the exception as a string. - When 'tryshort' resolves to True, and the exception is an - AssertionError, only the actual exception part of the exception - representation is returned (so 'AssertionError: ' is removed from - the beginning). + This is usually a single line ": ", but + may also include additional lines for the exception notes, and detailed + information for SyntaxError's. + + :param tryshort: + If true, and the exception is an AssertionError, strip + 'AssertionError: ' from the beginning. """ def _get_single_subexc( @@ -666,7 +669,8 @@ def _get_single_subexc( ): return f"{subexc!r} [single exception in {type(self.value).__name__}]" - lines = format_exception_only(self.type, self.value) + lines = format_exception_only(self.value) + # The lines already include line separators. text = "".join(lines) text = text.rstrip() if tryshort: @@ -705,9 +709,11 @@ def getrepr( ) -> ReprExceptionInfo | ExceptionChainRepr: """Return str()able representation of this exception info. + The formatting parameters are ineffective if ``style="native"``, + since in this case the native formatting is used. + :param bool showlocals: Show locals per traceback entry. - Ignored if ``style=="native"``. :param str style: long|short|line|no|native|value traceback style. @@ -723,19 +729,19 @@ def getrepr( variable ``__tracebackhide__ = True``. * If a callable, delegates the filtering to the callable. - Ignored if ``style`` is ``"native"``. - :param bool funcargs: - Show fixtures ("funcargs" for legacy purposes) per traceback entry. + Show function arguments per traceback entry. :param bool truncate_locals: - With ``showlocals==True``, make sure locals can be safely represented as strings. + Whether to show a size-limited `repr()` of locals, or a full + pretty-printing. :param bool truncate_args: - With ``showargs==True``, make sure args can be safely represented as strings. + Whether to show a size-limited truncated `repr()` of function + arguments, or a full pretty-printing. :param bool chain: - If chained exceptions in Python 3 should be shown. + If chained exceptions should be shown. .. versionchanged:: 3.9 @@ -753,7 +759,7 @@ def getrepr( reprcrash=self._getreprcrash(), ) - fmt = FormattedExcinfo( + fmt = ExceptionInfoFormatter( showlocals=showlocals, style=style, abspath=abspath, @@ -864,14 +870,19 @@ def group_contains( @dataclasses.dataclass -class FormattedExcinfo: - """Presenting information about failing Functions and Generators.""" +class ExceptionInfoFormatter: + """Helper object to format ExceptionInfo's and individual exception parts + into TerminalRepr's. + + See :func:`ExceptionInfo.getrepr` for parameters. + """ # for traceback entries flow_marker: ClassVar = ">" fail_marker: ClassVar = "E" showlocals: bool = False + # Note: "native" is handled outside of ExceptionInfoFormatter. style: TracebackStyle = "long" abspath: bool = True tbfilter: TracebackFilter = True @@ -879,6 +890,7 @@ class FormattedExcinfo: truncate_locals: bool = True truncate_args: bool = True chain: bool = True + astcache: dict[str | Path, ast.AST] = dataclasses.field( default_factory=dict, init=False, repr=False ) @@ -1026,6 +1038,8 @@ def get_exconly( def repr_locals(self, locals: Mapping[str, object]) -> ReprLocals | None: if self.showlocals: lines = [] + # Variables starting with `@` are helpers injected by assertion + # rewriting, not user variables, so hide them. keys = [loc for loc in locals if loc[0] != "@"] keys.sort() for name in keys: @@ -1177,7 +1191,7 @@ def repr_excinfo(self, excinfo: ExceptionInfo[BaseException]) -> ExceptionChainR repr_chain: list[tuple[ReprTraceback, ReprFileLocation | None, str | None]] = [] e: BaseException | None = excinfo.value excinfo_: ExceptionInfo[BaseException] | None = excinfo - descr = None + description = None seen: set[int] = set() while e is not None and id(e) not in seen: seen.add(id(e)) @@ -1190,18 +1204,19 @@ def repr_excinfo(self, excinfo: ExceptionInfo[BaseException]) -> ExceptionChainR if isinstance(e, BaseExceptionGroup): # don't filter any sub-exceptions since they shouldn't have any internal frames traceback = filter_excinfo_traceback(self.tbfilter, excinfo) + extraline = ( + "All traceback entries are hidden. Pass `--full-trace` to see hidden and internal frames." + if not traceback + else None + ) reprtraceback = ReprTracebackNative( format_exception( type(excinfo.value), excinfo.value, traceback[0]._rawentry if traceback else None, - ) + ), + extraline=extraline, ) - if not traceback: - reprtraceback.extraline = ( - "All traceback entries are hidden. " - "Pass `--full-trace` to see hidden and internal frames." - ) else: reprtraceback = self.repr_traceback(excinfo_) @@ -1211,18 +1226,18 @@ def repr_excinfo(self, excinfo: ExceptionInfo[BaseException]) -> ExceptionChainR # ExceptionInfo objects require a full traceback to work. reprtraceback = ReprTracebackNative(format_exception(type(e), e, None)) reprcrash = None - repr_chain += [(reprtraceback, reprcrash, descr)] + repr_chain.append((reprtraceback, reprcrash, description)) if e.__cause__ is not None and self.chain: e = e.__cause__ excinfo_ = ExceptionInfo.from_exception(e) if e.__traceback__ else None - descr = "The above exception was the direct cause of the following exception:" + description = "The above exception was the direct cause of the following exception:" elif ( e.__context__ is not None and not e.__suppress_context__ and self.chain ): e = e.__context__ excinfo_ = ExceptionInfo.from_exception(e) if e.__traceback__ else None - descr = "During handling of the above exception, another exception occurred:" + description = "During handling of the above exception, another exception occurred:" else: e = None repr_chain.reverse() @@ -1231,6 +1246,9 @@ def repr_excinfo(self, excinfo: ExceptionInfo[BaseException]) -> ExceptionChainR @dataclasses.dataclass(eq=False) class TerminalRepr: + """Base class for terminal representations -- pieces of data that display + themselves to a terminal.""" + def __str__(self) -> str: # FYI this is called from pytest-xdist's serialization of exception # information. @@ -1246,10 +1264,17 @@ def toterminal(self, tw: TerminalWriter) -> None: raise NotImplementedError() -# This class is abstract -- only subclasses are instantiated. @dataclasses.dataclass(eq=False) class ExceptionRepr(TerminalRepr): - # Provided by subclasses. + """Base class for exception terminal representations. + + The representation generally contains: + - The exception traceback (`reprtraceback`) + - The exception message and location (`reprcrash`) + - Separated, titled sections with additional data (pytest core doesn't use + this currently). + """ + reprtraceback: ReprTraceback reprcrash: ReprFileLocation | None sections: list[tuple[str, str, str]] = dataclasses.field( @@ -1267,6 +1292,9 @@ def toterminal(self, tw: TerminalWriter) -> None: @dataclasses.dataclass(eq=False) class ExceptionChainRepr(ExceptionRepr): + """A chain of exceptions, separated by descriptions (e.g. "The above + exception was the direct cause of the following exception").""" + chain: Sequence[tuple[ReprTraceback, ReprFileLocation | None, str | None]] def __init__( @@ -1282,18 +1310,19 @@ def __init__( self.chain = chain def toterminal(self, tw: TerminalWriter) -> None: - for element in self.chain: - element[0].toterminal(tw) - if element[2] is not None: + for reprtraceback, reprcrash, description in self.chain: + reprtraceback.toterminal(tw) + if description is not None: tw.line("") - tw.line(element[2], yellow=True) + tw.line(description, yellow=True) super().toterminal(tw) @dataclasses.dataclass(eq=False) class ReprExceptionInfo(ExceptionRepr): - reprtraceback: ReprTraceback - reprcrash: ReprFileLocation | None + """A single exception with optional extra details (function arguments, + function locals, file location) and possible extra line and sections emitted + at the end.""" def toterminal(self, tw: TerminalWriter) -> None: self.reprtraceback.toterminal(tw) @@ -1302,6 +1331,9 @@ def toterminal(self, tw: TerminalWriter) -> None: @dataclasses.dataclass(eq=False) class ReprTraceback(TerminalRepr): + """A traceback with optional extra details (function arguments, function + locals, file location) and possible extra line emitted at the end.""" + reprentries: Sequence[ReprEntry | ReprEntryNative] extraline: str | None style: TracebackStyle @@ -1326,14 +1358,29 @@ def toterminal(self, tw: TerminalWriter) -> None: class ReprTracebackNative(ReprTraceback): - def __init__(self, tblines: Sequence[str]) -> None: + """A traceback in native style. + + The lines are emitted as is; uses a single entry for the entire native + traceback. + """ + + def __init__(self, tblines: Sequence[str], *, extraline: str | None = None) -> None: self.reprentries = [ReprEntryNative(tblines)] - self.extraline = None + self.extraline = extraline self.style = "native" @dataclasses.dataclass(eq=False) class ReprEntryNative(TerminalRepr): + """An entry in a traceback in native style. + + Emits the lines as is. The lines are assumed to include the line separators. + + [Note that we currently use a single "entry" for the entire native + traceback, so this is a bit misleading, but there's no point trying to parse + or split a native traceback.] + """ + lines: Sequence[str] style: ClassVar[TracebackStyle] = "native" @@ -1344,6 +1391,9 @@ def toterminal(self, tw: TerminalWriter) -> None: @dataclasses.dataclass(eq=False) class ReprEntry(TerminalRepr): + """An entry in a traceback with possible extra details (function arguments, + function locals, source snippet, function's file and line).""" + lines: Sequence[str] reprfuncargs: ReprFuncArgs | None reprlocals: ReprLocals | None @@ -1378,7 +1428,7 @@ def _write_entry_lines(self, tw: TerminalWriter) -> None: # separate indents and source lines that are not failures: we want to # highlight the code but not the indentation, which may contain markers # such as "> assert 0" - fail_marker = f"{FormattedExcinfo.fail_marker} " + fail_marker = f"{ExceptionInfoFormatter.fail_marker} " indent_size = len(fail_marker) indents: list[str] = [] source_lines: list[str] = [] @@ -1429,6 +1479,12 @@ def __str__(self) -> str: @dataclasses.dataclass(eq=False) class ReprFileLocation(TerminalRepr): + """A message at a file location, using the `:: ` + format that most editors understand. + + Only the first line of the message is emitted. + """ + path: str lineno: int message: str @@ -1437,8 +1493,6 @@ def __post_init__(self) -> None: self.path = str(self.path) def toterminal(self, tw: TerminalWriter) -> None: - # Filename and lineno output for each entry, using an output format - # that most editors understand. msg = self.message i = msg.find("\n") if i != -1: @@ -1449,15 +1503,19 @@ def toterminal(self, tw: TerminalWriter) -> None: @dataclasses.dataclass(eq=False) class ReprLocals(TerminalRepr): + """Function local variables (pre-formatted).""" + lines: Sequence[str] - def toterminal(self, tw: TerminalWriter, indent="") -> None: + def toterminal(self, tw: TerminalWriter, indent: str = "") -> None: for line in self.lines: tw.line(indent + line) @dataclasses.dataclass(eq=False) class ReprFuncArgs(TerminalRepr): + """Function arguments - name = value, comma separated.""" + args: Sequence[tuple[str, object]] def toterminal(self, tw: TerminalWriter) -> None: diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index 9e123be230a..b9793e41725 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -37,7 +37,7 @@ from _pytest import nodes from _pytest._code import getfslineno from _pytest._code import Source -from _pytest._code.code import FormattedExcinfo +from _pytest._code.code import ExceptionInfoFormatter from _pytest._code.code import TerminalRepr from _pytest._io import TerminalWriter from _pytest.compat import assert_never @@ -944,12 +944,12 @@ def toterminal(self, tw: TerminalWriter) -> None: lines = self.errorstring.split("\n") if lines: tw.line( - f"{FormattedExcinfo.fail_marker} {lines[0].strip()}", + f"{ExceptionInfoFormatter.fail_marker} {lines[0].strip()}", red=True, ) for line in lines[1:]: tw.line( - f"{FormattedExcinfo.flow_marker} {line.strip()}", + f"{ExceptionInfoFormatter.flow_marker} {line.strip()}", red=True, ) tw.line() diff --git a/src/_pytest/skipping.py b/src/_pytest/skipping.py index 3b067629de0..f7a4c4c04e3 100644 --- a/src/_pytest/skipping.py +++ b/src/_pytest/skipping.py @@ -129,7 +129,7 @@ def evaluate_condition(item: Item, mark: Mark, condition: object) -> tuple[bool, msglines = [ f"Error evaluating {mark.name!r} condition", " " + condition, - *traceback.format_exception_only(type(exc), exc), + *traceback.format_exception_only(exc), ] fail("\n".join(msglines), pytrace=False) @@ -140,7 +140,7 @@ def evaluate_condition(item: Item, mark: Mark, condition: object) -> tuple[bool, except Exception as exc: msglines = [ f"Error evaluating {mark.name!r} condition as a boolean", - *traceback.format_exception_only(type(exc), exc), + *traceback.format_exception_only(exc), ] fail("\n".join(msglines), pytrace=False) diff --git a/testing/code/test_excinfo.py b/testing/code/test_excinfo.py index d578148b554..d14f0c093ab 100644 --- a/testing/code/test_excinfo.py +++ b/testing/code/test_excinfo.py @@ -17,7 +17,7 @@ import _pytest._code from _pytest._code.code import ExceptionChainRepr from _pytest._code.code import ExceptionInfo -from _pytest._code.code import FormattedExcinfo +from _pytest._code.code import ExceptionInfoFormatter from _pytest._io import TerminalWriter from _pytest.monkeypatch import MonkeyPatch from _pytest.pathlib import bestrelpath @@ -586,7 +586,7 @@ def test_contains_exception_match_specific_depth(self) -> None: ) -class TestFormattedExcinfo: +class TestExceptionInfoFormatter: @pytest.fixture def importasmod(self, tmp_path: Path, _sys_snapshot): def importasmod(source): @@ -602,7 +602,7 @@ def importasmod(source): return importasmod def test_repr_source(self): - pr = FormattedExcinfo() + pr = ExceptionInfoFormatter() source = _pytest._code.Source( """\ def f(x): @@ -616,7 +616,7 @@ def f(x): assert lines[1] == " pass" def test_repr_source_out_of_bounds(self): - pr = FormattedExcinfo() + pr = ExceptionInfoFormatter() source = _pytest._code.Source( """\ def f(x): @@ -647,7 +647,7 @@ def f(): else: assert False, "did not raise" - pr = FormattedExcinfo() + pr = ExceptionInfoFormatter() source = pr._getentrysource(excinfo.traceback[-1]) assert source is not None lines = pr.get_source(source, 1, excinfo) @@ -660,7 +660,7 @@ def f(): ] def test_repr_source_not_existing(self): - pr = FormattedExcinfo() + pr = ExceptionInfoFormatter() co = compile("raise ValueError()", "", "exec") try: exec(co) @@ -671,7 +671,7 @@ def test_repr_source_not_existing(self): assert repr.chain[0][0].reprentries[1].lines[0] == "> ???" def test_repr_many_line_source_not_existing(self): - pr = FormattedExcinfo() + pr = ExceptionInfoFormatter() co = compile( """ a = 1 @@ -689,7 +689,7 @@ def test_repr_many_line_source_not_existing(self): assert repr.chain[0][0].reprentries[1].lines[0] == "> ???" def test_repr_source_failing_fullsource(self, monkeypatch) -> None: - pr = FormattedExcinfo() + pr = ExceptionInfoFormatter() try: _ = 1 / 0 @@ -704,7 +704,7 @@ def test_repr_source_failing_fullsource(self, monkeypatch) -> None: assert repr.chain[0][0].reprentries[0].lines[0] == "> ???" def test_repr_local(self) -> None: - p = FormattedExcinfo(showlocals=True) + p = ExceptionInfoFormatter(showlocals=True) loc = {"y": 5, "z": 7, "x": 3, "@x": 2, "__builtins__": {}} reprlocals = p.repr_locals(loc) assert reprlocals is not None @@ -719,7 +719,7 @@ class ObjWithErrorInRepr: def __repr__(self): raise NotImplementedError - p = FormattedExcinfo(showlocals=True, truncate_locals=False) + p = ExceptionInfoFormatter(showlocals=True, truncate_locals=False) loc = {"x": ObjWithErrorInRepr(), "__builtins__": {}} reprlocals = p.repr_locals(loc) assert reprlocals is not None @@ -738,7 +738,7 @@ class ObjWithErrorInRepr: def __repr__(self): raise ExceptionWithBrokenClass() - p = FormattedExcinfo(showlocals=True, truncate_locals=False) + p = ExceptionInfoFormatter(showlocals=True, truncate_locals=False) loc = {"x": ObjWithErrorInRepr(), "__builtins__": {}} reprlocals = p.repr_locals(loc) assert reprlocals is not None @@ -748,13 +748,13 @@ def __repr__(self): def test_repr_local_truncated(self) -> None: loc = {"l": [i for i in range(10)]} - p = FormattedExcinfo(showlocals=True) + p = ExceptionInfoFormatter(showlocals=True) truncated_reprlocals = p.repr_locals(loc) assert truncated_reprlocals is not None assert truncated_reprlocals.lines assert truncated_reprlocals.lines[0] == "l = [0, 1, 2, 3, 4, 5, ...]" - q = FormattedExcinfo(showlocals=True, truncate_locals=False) + q = ExceptionInfoFormatter(showlocals=True, truncate_locals=False) full_reprlocals = q.repr_locals(loc) assert full_reprlocals is not None assert full_reprlocals.lines @@ -771,14 +771,14 @@ def func1(m): mod.func1("m" * 500) excinfo.traceback = excinfo.traceback.filter(excinfo) entry = excinfo.traceback[-1] - p = FormattedExcinfo(funcargs=True, truncate_args=True) + p = ExceptionInfoFormatter(funcargs=True, truncate_args=True) reprfuncargs = p.repr_args(entry) assert reprfuncargs is not None arg1 = cast(str, reprfuncargs.args[0][1]) assert len(arg1) < 500 assert "..." in arg1 # again without truncate - p = FormattedExcinfo(funcargs=True, truncate_args=False) + p = ExceptionInfoFormatter(funcargs=True, truncate_args=False) reprfuncargs = p.repr_args(entry) assert reprfuncargs is not None assert reprfuncargs.args[0] == ("m", repr("m" * 500)) @@ -794,7 +794,7 @@ def func1(): with pytest.raises(ValueError) as excinfo: mod.func1() excinfo.traceback = excinfo.traceback.filter(excinfo) - p = FormattedExcinfo() + p = ExceptionInfoFormatter() reprtb = p.repr_traceback_entry(excinfo.traceback[-1]) # test as intermittent entry @@ -803,7 +803,7 @@ def func1(): assert lines[1] == '> raise ValueError("hello\\nworld")' # test as last entry - p = FormattedExcinfo(showlocals=True) + p = ExceptionInfoFormatter(showlocals=True) repr_entry = p.repr_traceback_entry(excinfo.traceback[-1], excinfo) lines = repr_entry.lines assert lines[0] == " def func1():" @@ -829,7 +829,7 @@ def func1(m, x, y, z): mod.func1("m" * 90, 5, 13, "z" * 120) excinfo.traceback = excinfo.traceback.filter(excinfo) entry = excinfo.traceback[-1] - p = FormattedExcinfo(funcargs=True) + p = ExceptionInfoFormatter(funcargs=True) reprfuncargs = p.repr_args(entry) assert reprfuncargs is not None assert reprfuncargs.args[0] == ("m", repr("m" * 90)) @@ -837,7 +837,7 @@ def func1(m, x, y, z): assert reprfuncargs.args[2] == ("y", "13") assert reprfuncargs.args[3] == ("z", repr("z" * 120)) - p = FormattedExcinfo(funcargs=True) + p = ExceptionInfoFormatter(funcargs=True) repr_entry = p.repr_traceback_entry(entry) assert repr_entry.reprfuncargs is not None assert repr_entry.reprfuncargs.args == reprfuncargs.args @@ -857,14 +857,14 @@ def func1(x, *y, **z): mod.func1("a", "b", c="d") excinfo.traceback = excinfo.traceback.filter(excinfo) entry = excinfo.traceback[-1] - p = FormattedExcinfo(funcargs=True) + p = ExceptionInfoFormatter(funcargs=True) reprfuncargs = p.repr_args(entry) assert reprfuncargs is not None assert reprfuncargs.args[0] == ("x", repr("a")) assert reprfuncargs.args[1] == ("y", repr(("b",))) assert reprfuncargs.args[2] == ("z", repr({"c": "d"})) - p = FormattedExcinfo(funcargs=True) + p = ExceptionInfoFormatter(funcargs=True) repr_entry = p.repr_traceback_entry(entry) assert repr_entry.reprfuncargs assert repr_entry.reprfuncargs.args == reprfuncargs.args @@ -882,7 +882,7 @@ def entry(): ) with pytest.raises(ValueError) as excinfo: mod.entry() - p = FormattedExcinfo(style="short") + p = ExceptionInfoFormatter(style="short") reprtb = p.repr_traceback_entry(excinfo.traceback[-2]) lines = reprtb.lines basename = Path(mod.__file__).name @@ -892,7 +892,7 @@ def entry(): assert reprtb.reprfileloc.lineno == 5 # test last entry - p = FormattedExcinfo(style="short") + p = ExceptionInfoFormatter(style="short") reprtb = p.repr_traceback_entry(excinfo.traceback[-1], excinfo) lines = reprtb.lines assert lines[0] == ' raise ValueError("hello")' @@ -918,7 +918,7 @@ def entry(): ) with pytest.raises(ZeroDivisionError) as excinfo: mod.entry() - p = FormattedExcinfo(style="short") + p = ExceptionInfoFormatter(style="short") reprtb = p.repr_traceback_entry(excinfo.traceback[-3]) assert len(reprtb.lines) == 1 assert reprtb.lines[0] == " func1()" @@ -944,10 +944,10 @@ def entry(): ) with pytest.raises(ValueError) as excinfo: mod.entry() - p = FormattedExcinfo(style="no") + p = ExceptionInfoFormatter(style="no") p.repr_traceback_entry(excinfo.traceback[-2]) - p = FormattedExcinfo(style="no") + p = ExceptionInfoFormatter(style="no") reprentry = p.repr_traceback_entry(excinfo.traceback[-1], excinfo) lines = reprentry.lines assert lines[0] == "E ValueError: hello" @@ -965,10 +965,10 @@ def entry(): ) with pytest.raises(ValueError) as excinfo: mod.entry() - p = FormattedExcinfo(tbfilter=True) + p = ExceptionInfoFormatter(tbfilter=True) reprtb = p.repr_traceback(excinfo) assert len(reprtb.reprentries) == 2 - p = FormattedExcinfo(tbfilter=False) + p = ExceptionInfoFormatter(tbfilter=False) reprtb = p.repr_traceback(excinfo) assert len(reprtb.reprentries) == 3 @@ -991,10 +991,10 @@ def entry(): with monkeypatch.context() as mp: mp.setattr(Code, "path", "bogus") - p = FormattedExcinfo(style="short") + p = ExceptionInfoFormatter(style="short") reprtb = p.repr_traceback_entry(excinfo.traceback[-2]) lines = reprtb.lines - last_p = FormattedExcinfo(style="short") + last_p = ExceptionInfoFormatter(style="short") last_reprtb = last_p.repr_traceback_entry(excinfo.traceback[-1], excinfo) last_lines = last_reprtb.lines assert lines[0] == " func1()" @@ -1017,7 +1017,7 @@ def entry(): styles: tuple[TracebackStyle, ...] = ("long", "short") for style in styles: - p = FormattedExcinfo(style=style) + p = ExceptionInfoFormatter(style=style) reprtb = p.repr_traceback(excinfo) assert len(reprtb.reprentries) == 2 assert reprtb.style == style @@ -1045,7 +1045,7 @@ def entry(): with pytest.raises(ValueError) as excinfo: mod.entry() - p = FormattedExcinfo(abspath=False) + p = ExceptionInfoFormatter(abspath=False) raised = 0 @@ -1131,7 +1131,7 @@ def entry(): mod.entry() for style in ("short", "long", "no"): - p = FormattedExcinfo(style="short") + p = ExceptionInfoFormatter(style="short") reprtb = p.repr_traceback(excinfo) assert reprtb.extraline == "!!! Recursion detected (same locals & position)" assert str(reprtb) @@ -1682,7 +1682,7 @@ def test_repr_traceback_with_unicode(style, encoding): raise RuntimeError(msg) except RuntimeError: e_info = ExceptionInfo.from_current() - formatter = FormattedExcinfo(style=style) + formatter = ExceptionInfoFormatter(style=style) repr_traceback = formatter.repr_traceback(e_info) assert repr_traceback is not None