mirror of
https://github.com/cool-RR/PySnooper.git
synced 2026-07-17 16:36:24 +00:00
Format ExceptionGroup gracefully on Python 3.11+
This commit is contained in:
parent
90b2e96ef0
commit
4bff1205c8
3 changed files with 76 additions and 1 deletions
|
|
@ -569,7 +569,7 @@ class Tracer:
|
|||
format(**locals()))
|
||||
|
||||
if event == 'exception':
|
||||
exception = '\n'.join(traceback.format_exception_only(*arg[:2])).strip()
|
||||
exception = utils.format_exception(*arg[:2])
|
||||
if self.max_variable_length:
|
||||
exception = utils.truncate(exception, self.max_variable_length)
|
||||
self.write('{indent}{_FOREGROUND_RED}Exception:..... '
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
import abc
|
||||
import re
|
||||
import traceback
|
||||
|
||||
import sys
|
||||
from .pycompat import ABC, string_types, collections_abc
|
||||
|
|
@ -87,6 +88,21 @@ def truncate(string, max_length):
|
|||
return u'{}...{}'.format(string[:left], string[-right:])
|
||||
|
||||
|
||||
def format_exception(exc_type, exc_value):
|
||||
try:
|
||||
is_group = isinstance(exc_value, BaseExceptionGroup)
|
||||
except NameError:
|
||||
is_group = False
|
||||
if is_group:
|
||||
sub_types = ', '.join(type(e).__name__ for e in exc_value.exceptions)
|
||||
message = exc_value.args[0] if exc_value.args else ''
|
||||
return u"{}: '{}' ({} sub-exceptions: {})".format(
|
||||
exc_type.__name__, message,
|
||||
len(exc_value.exceptions), sub_types,
|
||||
)
|
||||
return u'\n'.join(traceback.format_exception_only(exc_type, exc_value)).strip()
|
||||
|
||||
|
||||
def ensure_tuple(x):
|
||||
if isinstance(x, collections_abc.Iterable) and \
|
||||
not isinstance(x, string_types):
|
||||
|
|
|
|||
|
|
@ -1908,6 +1908,65 @@ def test_exception():
|
|||
)
|
||||
|
||||
|
||||
@pytest.mark.skipif(sys.version_info < (3, 11),
|
||||
reason='ExceptionGroup requires Python 3.11+')
|
||||
def test_exception_group():
|
||||
string_io = io.StringIO()
|
||||
@pysnooper.snoop(string_io, color=False)
|
||||
def f():
|
||||
raise ExceptionGroup('task errors', [ValueError('bad'), TypeError('wrong'), RuntimeError('fail')])
|
||||
|
||||
with pytest.raises(ExceptionGroup):
|
||||
f()
|
||||
|
||||
output = string_io.getvalue()
|
||||
assert_output(
|
||||
output,
|
||||
(
|
||||
SourcePathEntry(),
|
||||
CallEntry(),
|
||||
LineEntry(),
|
||||
ExceptionEntry(),
|
||||
ExceptionValueEntry(
|
||||
value_regex=r"ExceptionGroup: 'task errors' "
|
||||
r"\(3 sub-exceptions: ValueError, TypeError, "
|
||||
r"RuntimeError\)"
|
||||
),
|
||||
CallEndedByExceptionEntry(),
|
||||
ElapsedTimeEntry(),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.skipif(sys.version_info < (3, 11),
|
||||
reason='ExceptionGroup requires Python 3.11+')
|
||||
def test_nested_exception_group():
|
||||
string_io = io.StringIO()
|
||||
@pysnooper.snoop(string_io, color=False)
|
||||
def f():
|
||||
raise ExceptionGroup('outer', [ExceptionGroup('inner', [ValueError('deep')]), TypeError('shallow')])
|
||||
|
||||
with pytest.raises(ExceptionGroup):
|
||||
f()
|
||||
|
||||
output = string_io.getvalue()
|
||||
assert_output(
|
||||
output,
|
||||
(
|
||||
SourcePathEntry(),
|
||||
CallEntry(),
|
||||
LineEntry(),
|
||||
ExceptionEntry(),
|
||||
ExceptionValueEntry(
|
||||
value_regex=r"ExceptionGroup: 'outer' "
|
||||
r"\(2 sub-exceptions: ExceptionGroup, TypeError\)"
|
||||
),
|
||||
CallEndedByExceptionEntry(),
|
||||
ElapsedTimeEntry(),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def test_exception_on_entry():
|
||||
@pysnooper.snoop(color=False)
|
||||
def f(x):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue