Here is a short code example. I’m chaining function calls f
, g
, and eventually h
where an exception is being raised. The traceback, as expected, shows the call to f
(in <module>
), then the call to g
(in f
), then the call to h
(in g
), and eventually the raise
(in h
):
>>> def f(): g()
...
>>> def g(): h()
...
>>> def h(): raise Exception()
...
>>> f()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1, in f
File "<stdin>", line 1, in g
File "<stdin>", line 1, in h
Exception
>>>
As far as I understand, I can use BaseException.with_traceback
to modify __traceback__
of an exception. In my case, I want only the most recent step of the traceback to remain, i.e. the actual line (raise Exception
in h
) that caused the exception to show up. (Similar to sys.tracebacklimit = 1
.) So I’m using tb_next
to “walk” to the most recent step of the traceback, set that with with_traceback
as the traceback of the exception and then raise
. This does exactly what I expected and what I need:
Python 3.11.3 (main, May 3 2023, 08:13:29) [GCC 10.2.1 20210110] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> def f(): g()
...
>>> def g(): h()
...
>>> def h(): raise Exception
...
>>> try:
... f()
... except Exception as e:
... tb = e.__traceback__
... while tb.tb_next is not None:
... tb = tb.tb_next
... e.with_traceback(tb)
... raise
...
Exception()
Traceback (most recent call last):
File "<stdin>", line 1, in h
Exception
>>>
However, unfortunately, this is only the case for Python 3.11. If I run above code in Python 3.10, it’s like with_traceback
has no effect:
Python 3.10.11 (main, May 4 2023, 06:08:16) [GCC 10.2.1 20210110] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> def f(): g()
...
>>> def g(): h()
...
>>> def h(): raise Exception
...
>>> try:
... f()
... except Exception as e:
... tb = e.__traceback__
... while tb.tb_next is not None:
... tb = tb.tb_next
... e.with_traceback(tb)
... raise
...
Exception()
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
File "<stdin>", line 1, in f
File "<stdin>", line 1, in g
File "<stdin>", line 1, in h
Exception
>>>
How can this different behavior be explained? Isn’t this a bug in Python 3.10 that has been fixed in 3.11 but not backported? I had a look at the changelog, but couldn’t find anything that would match my issue. Same for the commit history in Objects/exceptions.c
. So I’m probably just overlooking it?