Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 20 additions & 7 deletions Lib/test/test_traceback.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,13 +225,13 @@ class X(Exception):
def __str__(self):
1/0
err = traceback.format_exception_only(X, X())
self.assertEqual(len(err), 1)
self.assertEqual(len(err), 10)
str_value = '<exception str() failed>'
if X.__module__ in ('__main__', 'builtins'):
str_name = X.__qualname__
else:
str_name = '.'.join([X.__module__, X.__qualname__])
self.assertEqual(err[0], "%s: %s\n" % (str_name, str_value))
self.assertIn("%s: %s\n" % (str_name, str_value), err[0])

def test_format_exception_group_without_show_group(self):
eg = ExceptionGroup('A', [ValueError('B')])
Expand Down Expand Up @@ -2482,7 +2482,10 @@ def __repr__(self):

e.__notes__ = Unprintable()
err_msg = '<__notes__ repr() failed>'
self.assertEqual(self.get_report(e), vanilla + err_msg + '\n')
ignore_msg = "Exception ignored in __notes__ repr():"
msg = self.get_report(e)
self.assertIn(vanilla + err_msg + '\n', msg)
self.assertIn(ignore_msg, msg)

# non-string item in the __notes__ sequence
e.__notes__ = [BadThing(), 'Final Note']
Expand All @@ -2492,7 +2495,9 @@ def __repr__(self):
# unprintable, non-string item in the __notes__ sequence
e.__notes__ = [Unprintable(), 'Final Note']
err_msg = '<note str() failed>'
self.assertEqual(self.get_report(e), vanilla + err_msg + '\nFinal Note\n')
msg = self.get_report(e)
self.assertIn(vanilla + err_msg + '\nFinal Note\n', msg)
self.assertIn("Exception ignored in note str():", msg)

e.__notes__ = "please do not explode me"
err_msg = "'please do not explode me'"
Expand Down Expand Up @@ -2602,7 +2607,9 @@ def __str__(self):
err = self.get_report(X())
str_value = '<exception str() failed>'
str_name = '.'.join([X.__module__, X.__qualname__])
self.assertEqual(MODULE_PREFIX + err, f"{str_name}: {str_value}\n")
ignore_sentence = "Exception ignored in exception str():"
self.assertIn(f"{str_name}: {str_value}\n", MODULE_PREFIX + err)
self.assertIn(ignore_sentence, err)


# #### Exception Groups ####
Expand Down Expand Up @@ -4226,8 +4233,14 @@ def __getattr__(self, attr):
raise AttributeError(23)

for cls in [A, B, C]:
actual = self.get_suggestion(cls(), 'bluch')
self.assertIn("blech", actual)
try:
getattr(cls(), "bluch")
except AttributeError:
msg = traceback.format_exc()
self.assertIn("blech", msg)
# actual = self.get_suggestion(cls(), 'bluch')
# self.assertIn("blech", actual)
# The above using is changed because it will get the warning in the ignore exception


class DelattrSuggestionTests(BaseSuggestionTests):
Expand Down
97 changes: 89 additions & 8 deletions Lib/traceback.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import tokenize
import io
import _colorize
import threading

from contextlib import suppress

Expand Down Expand Up @@ -198,11 +199,64 @@ def _format_final_exc_line(etype, value, *, insert_final_newline=True, colorize=
return line


def _safe_string(value, what, func=str):
def _remove_exception(exc_value, other_exc_value, _seen=None):
if _seen is None:
_seen = set()
if id(exc_value) not in _seen:
_seen.add(id(exc_value))
if exc_value.__cause__:
if exc_value.__cause__ is other_exc_value:
exc_value.__cause__ = None
else:
_remove_exception(exc_value.__cause__, other_exc_value, _seen)
if exc_value.__context__:
if exc_value.__context__ is other_exc_value:
exc_value.__context__ = None
else:
_remove_exception(
exc_value.__context__, other_exc_value, _seen
)


def _traceback_to_tuples(tb):
extracted = extract_tb(tb)
return tuple(
(f.filename, f.lineno, getattr(f, "name", None), f.line)
for f in extracted
) # handle SyntaxError


def _safe_string(value, what, func=str,
exception_target=None, exception_exclude=None,
_seen=threading.local()):
if not hasattr(_seen, "_seen"):
_seen._seen = set()
if not hasattr(_seen, "times"):
_seen.times = 0
try:
_seen.times += 1
return func(value)
except:
return f'<{what} {func.__name__}() failed>'
if isinstance(exception_target, list):
typ, val, tb = sys.exc_info()
tb_tuple = _traceback_to_tuples(tb)
if tb_tuple not in _seen._seen:
_seen._seen.add(tb_tuple)
if exception_exclude:
_remove_exception(val, exception_exclude)
msg = "".join(TracebackException(typ, val, tb).format())
while msg.endswith("\n") or msg.endswith(" "):
msg = msg[:-1]
exception_target.append(
f"\nException ignored in {what} {func.__name__}():"
)
exception_target.append(msg)
return f"<{what} {func.__name__}() failed>"
finally:
_seen.times -= 1
if _seen.times <= 0:
_seen.times = 0
_seen._seen.clear()

# --

Expand Down Expand Up @@ -1072,12 +1126,13 @@ def __init__(self, exc_type, exc_value, exc_traceback, *, limit=None,

# Capture now to permit freeing resources: only complication is in the
# unofficial API _format_final_exc_line
self._str = _safe_string(exc_value, 'exception')
try:
self.__notes__ = getattr(exc_value, '__notes__', None)
except Exception as e:
self.__notes__ = [
f'Ignored error getting __notes__: {_safe_string(e, '__notes__', repr)}']
exception_target = []
self._str = _safe_string(
exc_value,
"exception",
exception_target=exception_target,
exception_exclude=exc_value,
)

self._is_syntax_error = False
self._have_exc_type = exc_type is not None
Expand Down Expand Up @@ -1125,6 +1180,32 @@ def __init__(self, exc_type, exc_value, exc_traceback, *, limit=None,
self._str += f" Or did you forget to import '{wrong_name}'?"
else:
self._str += f". Did you forget to import '{wrong_name}'?"
try:
original__notes__ = getattr(exc_value, "__notes__", None)
except Exception as e:
original__notes__ = [
f"Ignored error getting __notes__: {_safe_string(e, '__notes__', repr, exception_target, exc_value)}"
]
if original__notes__ is not None and not isinstance(
original__notes__, list):
original__notes__ = [
_safe_string(
original__notes__,
"__notes__",
repr,
exception_target,
exc_value,
)
]
final_string_list = []
if original__notes__:
for i in original__notes__:
final_string_list.append(
_safe_string(
i, "note", str, exception_target, exc_value
)
)
self.__notes__ = final_string_list + exception_target
if lookup_lines:
self._load_lines()
self.__suppress_context__ = \
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Print the exception as warning (ignore) when handle a transmitted anomalies
Loading