Feedback for ignore the exception in traceback.py

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.

The effect that avoid the infinite recursion:

>>> a = "raise WrongException()"
>>> class WrongException(Exception):
...     def __str__(self):
...         global a
...         a = "\n" + a
...         exec(a)
...     __repr__ = __str__
...
>>> raise WrongException()
Traceback (most recent call last):
  File "<python-input-2>", line 1, in <module>
    raise WrongException()
WrongException: <exception str() failed>

Exception ignored in exception str():
Traceback (most recent call last):
  File "C:\Users\hh180\my_python\handle_keyerror\Lib\traceback.py", line 232, in _safe_string
    return func(value)
  File "<python-input-1>", line 5, in __str__
    exec(a)
    ~~~~^^^
  File "<string>", line 2, in <module>
WrongException: <exception str() failed>

Exception ignored in exception str():
Traceback (most recent call last):
  File "C:\Users\hh180\my_python\handle_keyerror\Lib\traceback.py", line 232, in _safe_string
    return func(value)
  File "<python-input-1>", line 5, in __str__
    exec(a)
    ~~~~^^^
  File "<string>", line 3, in <module>
WrongException: <exception str() failed>

Exception ignored in exception str():
Traceback (most recent call last):
  File "C:\Users\hh180\my_python\handle_keyerror\Lib\traceback.py", line 232, in _safe_string
    return func(value)
  File "<python-input-1>", line 5, in __str__
    exec(a)
    ~~~~^^^
  File "<string>", line 4, in <module>
WrongException: <exception str() failed>

Exception ignored in exception str():
Traceback (most recent call last):
  File "C:\Users\hh180\my_python\handle_keyerror\Lib\traceback.py", line 232, in _safe_string
    return func(value)
  File "<python-input-1>", line 5, in __str__
    exec(a)
    ~~~~^^^
  File "<string>", line 5, in <module>
WrongException: <exception str() failed>

Exception ignored in exception str():
Traceback (most recent call last):
  File "C:\Users\hh180\my_python\handle_keyerror\Lib\traceback.py", line 232, in _safe_string
    return func(value)
  File "<python-input-1>", line 5, in __str__
    exec(a)
    ~~~~^^^
  File "<string>", line 6, in <module>
WrongException: <exception str() failed>

Exception ignored in exception str():
Traceback (most recent call last):
  File "C:\Users\hh180\my_python\handle_keyerror\Lib\traceback.py", line 232, in _safe_string
    return func(value)
  File "<python-input-1>", line 5, in __str__
    exec(a)
    ~~~~^^^
  File "<string>", line 7, in <module>
WrongException: <exception str() failed>

Exception ignored in exception str():
Traceback (most recent call last):
  File "C:\Users\hh180\my_python\handle_keyerror\Lib\traceback.py", line 232, in _safe_string
    return func(value)
  File "<python-input-1>", line 5, in __str__
    exec(a)
    ~~~~^^^
  File "<string>", line 8, in <module>
WrongException: <exception str() failed>

Exception ignored in exception str():
Traceback (most recent call last):
  File "C:\Users\hh180\my_python\handle_keyerror\Lib\traceback.py", line 232, in _safe_string
    return func(value)
  File "<python-input-1>", line 5, in __str__
    exec(a)
    ~~~~^^^
  File "<string>", line 9, in <module>
WrongException: <exception str() failed>

Exception ignored in exception str():
Traceback (most recent call last):
  File "C:\Users\hh180\my_python\handle_keyerror\Lib\traceback.py", line 232, in _safe_string
    return func(value)
  File "<python-input-1>", line 5, in __str__
    exec(a)
    ~~~~^^^
  File "<string>", line 10, in <module>
WrongException: <exception str() failed>

Exception ignored in exception str():
Traceback (most recent call last):
  File "C:\Users\hh180\my_python\handle_keyerror\Lib\traceback.py", line 232, in _safe_string
    return func(value)
  File "<python-input-1>", line 5, in __str__
    exec(a)
    ~~~~^^^
  File "<string>", line 11, in <module>
WrongException: <exception str() failed>
>>> b = 1
>>> c = 2
>>> # prove that the python now is still available
>>>