mirror of https://github.com/python/cpython
gh-105292: Add option to make traceback.TracebackException.format_exception_only recurse into exception groups (#105294)
Co-authored-by: Erlend E. Aasland <erlend.aasland@protonmail.com> Co-authored-by: Łukasz Langa <lukasz@langa.pl>
This commit is contained in:
parent
92022d8416
commit
f4d8e10d0d
|
@ -333,19 +333,24 @@ capture data for later printing in a lightweight fashion.
|
|||
The message indicating which exception occurred is always the last
|
||||
string in the output.
|
||||
|
||||
.. method:: format_exception_only()
|
||||
.. method:: format_exception_only(*, show_group=False)
|
||||
|
||||
Format the exception part of the traceback.
|
||||
|
||||
The return value is a generator of strings, each ending in a newline.
|
||||
|
||||
Normally, the generator emits a single string; however, for
|
||||
:exc:`SyntaxError` exceptions, it emits several lines that (when
|
||||
printed) display detailed information about where the syntax
|
||||
error occurred.
|
||||
When *show_group* is ``False``, the generator normally emits a single
|
||||
string; however, for :exc:`SyntaxError` exceptions, it emits several
|
||||
lines that (when printed) display detailed information about where
|
||||
the syntax error occurred. The message indicating which exception
|
||||
occurred is always the last string in the output.
|
||||
|
||||
The message indicating which exception occurred is always the last
|
||||
string in the output.
|
||||
When *show_group* is ``True``, and the exception is an instance of
|
||||
:exc:`BaseExceptionGroup`, the nested exceptions are included as
|
||||
well, recursively, with indentation relative to their nesting depth.
|
||||
|
||||
.. versionchanged:: 3.13
|
||||
Added the *show_group* parameter.
|
||||
|
||||
.. versionchanged:: 3.10
|
||||
Added the *compact* parameter.
|
||||
|
@ -354,6 +359,7 @@ capture data for later printing in a lightweight fashion.
|
|||
Added the *max_group_width* and *max_group_depth* parameters.
|
||||
|
||||
|
||||
|
||||
:class:`StackSummary` Objects
|
||||
-----------------------------
|
||||
|
||||
|
|
|
@ -113,6 +113,13 @@ pathlib
|
|||
:meth:`~pathlib.Path.rglob`.
|
||||
(Contributed by Barney Gale in :gh:`77609`.)
|
||||
|
||||
traceback
|
||||
---------
|
||||
|
||||
* Add *show_group* paramter to :func:`traceback.TracebackException.format_exception_only`
|
||||
to format the nested exceptions of a :exc:`BaseExceptionGroup` instance, recursively.
|
||||
(Contributed by Irit Katriel in :gh:`105292`.)
|
||||
|
||||
Optimizations
|
||||
=============
|
||||
|
||||
|
|
|
@ -2792,6 +2792,20 @@ class TestTracebackException_ExceptionGroups(unittest.TestCase):
|
|||
|
||||
self.assertEqual(formatted, expected)
|
||||
|
||||
def test_exception_group_format_exception_onlyi_recursive(self):
|
||||
teg = traceback.TracebackException.from_exception(self.eg)
|
||||
formatted = ''.join(teg.format_exception_only(show_group=True)).split('\n')
|
||||
expected = [
|
||||
'ExceptionGroup: eg2 (2 sub-exceptions)',
|
||||
' ExceptionGroup: eg1 (2 sub-exceptions)',
|
||||
' ZeroDivisionError: division by zero',
|
||||
' ValueError: 42',
|
||||
' ValueError: 24',
|
||||
''
|
||||
]
|
||||
|
||||
self.assertEqual(formatted, expected)
|
||||
|
||||
def test_exception_group_format(self):
|
||||
teg = traceback.TracebackException.from_exception(self.eg)
|
||||
|
||||
|
|
|
@ -826,7 +826,7 @@ class TracebackException:
|
|||
def __str__(self):
|
||||
return self._str
|
||||
|
||||
def format_exception_only(self):
|
||||
def format_exception_only(self, *, show_group=False, _depth=0):
|
||||
"""Format the exception part of the traceback.
|
||||
|
||||
The return value is a generator of strings, each ending in a newline.
|
||||
|
@ -839,8 +839,10 @@ class TracebackException:
|
|||
The message indicating which exception occurred is always the last
|
||||
string in the output.
|
||||
"""
|
||||
|
||||
indent = 3 * _depth * ' '
|
||||
if self.exc_type is None:
|
||||
yield _format_final_exc_line(None, self._str)
|
||||
yield indent + _format_final_exc_line(None, self._str)
|
||||
return
|
||||
|
||||
stype = self.exc_type.__qualname__
|
||||
|
@ -851,9 +853,9 @@ class TracebackException:
|
|||
stype = smod + '.' + stype
|
||||
|
||||
if not issubclass(self.exc_type, SyntaxError):
|
||||
yield _format_final_exc_line(stype, self._str)
|
||||
yield indent + _format_final_exc_line(stype, self._str)
|
||||
else:
|
||||
yield from self._format_syntax_error(stype)
|
||||
yield from [indent + l for l in self._format_syntax_error(stype)]
|
||||
|
||||
if (
|
||||
isinstance(self.__notes__, collections.abc.Sequence)
|
||||
|
@ -861,9 +863,13 @@ class TracebackException:
|
|||
):
|
||||
for note in self.__notes__:
|
||||
note = _safe_string(note, 'note')
|
||||
yield from [l + '\n' for l in note.split('\n')]
|
||||
yield from [indent + l + '\n' for l in note.split('\n')]
|
||||
elif self.__notes__ is not None:
|
||||
yield "{}\n".format(_safe_string(self.__notes__, '__notes__', func=repr))
|
||||
yield indent + "{}\n".format(_safe_string(self.__notes__, '__notes__', func=repr))
|
||||
|
||||
if self.exceptions and show_group:
|
||||
for ex in self.exceptions:
|
||||
yield from ex.format_exception_only(show_group=show_group, _depth=_depth+1)
|
||||
|
||||
def _format_syntax_error(self, stype):
|
||||
"""Format SyntaxError exceptions (internal helper)."""
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
Add option to :func:`traceback.format_exception_only` to recurse into the
|
||||
nested exception of a :exc:`BaseExceptionGroup`.
|
Loading…
Reference in New Issue