Now I make a PR to print the exception as warning if there are some new exceptions raised.
The current implement here:
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):
try:
return func(value)
except:
if isinstance(exception_target, list):
typ, val, tb = sys.exc_info()
_add_exception_note(typ, val, tb, f"{what} {func.__name__}()",
exception_target, exception_exclude)
return f"<{what} {func.__name__}() failed>"
_ADD_EXC_NOTE_LIMIT = 10
def _add_exception_note(exc_type, exc_value, exc_tb, where,
exception_target, exception_exclude=None, _seen=threading.local()):
if not hasattr(_seen, "_seen"):
_seen._seen = set()
if not hasattr(_seen, "times"):
_seen.times = 0
if not isinstance(exception_target, list):
return
_seen.times += 1
tb_tuple = _traceback_to_tuples(exc_tb)
if tb_tuple not in _seen._seen and _seen.times <= _ADD_EXC_NOTE_LIMIT:
_seen._seen.add(tb_tuple)
if exception_exclude:
_remove_exception(exc_value, exception_exclude)
msg = "".join(TracebackException(exc_type, exc_value, exc_tb).format())
while msg.endswith("\n") or msg.endswith(" "):
msg = msg[:-1]
exception_target.append(
f"\nException ignored in {where}:"
)
exception_target.append(msg)
_seen.times -= 1
if _seen.times <= 0:
_seen.times = 0
_seen._seen.clear()
Change in TracebackException
Now there is a new attribute “exception_target” to save the result. The “__notes__” will be immediately evaluated to the list of the string, and the exception in _safe_string will be appended to “exception_target”.
In the format of the TracebackException, the new key argument “exception_target” (Yes, it has the same name) will control whether yield the message in the attribute “self.exception_target”, it default True.
