Stack trace of re-raised exception is wrong
まだ誰も着手していません。
- 主要言語
- Python
- スター
- 77.2k
- フォーク
- 35.9k
- PR マージ指標
- PR 指標を取得中
説明
Bug report
Bug description:
If you catch an exception and hold on to it for later handling, and then re-raise the exception, the stack trace is wrong (the stack is a blend of the two stacks).
A simple example:
def err_returning_call() -> Exception:
try:
raise ValueError('foo')
except Exception as err:
return err
def raise_err(err):
raise err
err = err_returning_call()
raise_err(err)
Produces a traceback with:
$ python simplest_cpython_bug.py
Traceback (most recent call last):
File "example.py", line 14, in <module>
raise_err(err)
~~~~~~~~~^^^^^
File "example.py", line 10, in raise_err
raise err
File "example.py", line 3, in err_returning_call
raise ValueError('foo')
ValueError: foo
Note that the previously caught exception (raise err) produces a traceback that mixes the original stack and the new stack, rather than a clean traceback from the point where it is re-raised.
Raising the exception from another (adapting err_returning_call to raise ValueError('another') from err) produces a logical stack trace:
$ ./build/bin/python3 simplest_cpython_bug.py
Traceback (most recent call last):
File "example.py", line 3, in err_returning_call
raise ValueError('foo')
ValueError: foo
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "example.py", line 14, in <module>
raise_err(err)
~~~~~~~~~^^^^^
File "example.py", line 10, in raise_err
raise ValueError('another') from err
ValueError: another
The deeper the stacks, the more confusing the tb. It also seems to drop an important step in the traceback (even if we use the raise new_error from original_error pattern):
def root_call():
return err_returning_call()
def err_returning_call() -> Exception:
try:
error_raising()
except Exception as err:
return err
def error_raising():
raise ValueError('foo')
err = root_call()
def raise_err(err):
raise err
def indirect_raise_err(err):
raise_err(err)
indirect_raise_err(err)
With the result:
Traceback (most recent call last):
File "example.py", line 24, in <module>
indirect_raise_err(err)
~~~~~~~~~~~~~~~~~~^^^^^
File "example.py", line 21, in indirect_raise_err
raise_err(err)
~~~~~~~~~^^^^^
File "example.py", line 18, in raise_err
raise err
File "example.py", line 6, in err_returning_call
error_raising()
~~~~~~~~~~~~~^^
File "example.py", line 11, in error_raising
raise ValueError('foo')
ValueError: foo
Note that with the raise from included, the traceback is:
Traceback (most recent call last):
File "example.py", line 6, in err_returning_call
error_raising()
~~~~~~~~~~~~~^^
File "example.py", line 11, in error_raising
raise ValueError('foo')
ValueError: foo
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "example.py", line 24, in <module>
indirect_raise_err(err)
~~~~~~~~~~~~~~~~~~^^^^^
File "example.py", line 21, in indirect_raise_err
raise_err(err)
~~~~~~~~~^^^^^
File "example.py", line 18, in raise_err
raise ValueError('bar') from err
ValueError: bar
Notice how even in this case, the original traceback is missing the root_call call (possibly reasonable?).
The expected behaviour would be to simply raise the exception with the stack trace of the final raise call. This would be analogous to what happens when you capture an exception in a variable without raising it:
exception = ValueError('not raised directly')
raise exception
Which produces the following traceback:
Traceback (most recent call last):
File "/media/important/github/python/cpython/./exception_var.py", line 3, in <module>
raise exception
ValueError: not raised directly
This report is against main (at 2cf18a4430 - the latest commit on 15/3/2024), but applies all the way back to Python 3.7 at least.
CPython versions tested on:
3.8, 3.9, 3.10, 3.11, 3.12, 3.13, CPython main branch
Operating systems tested on:
Linux
コントリビューションガイド
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
調査の方向性
Start by reproducing the behavior with the issue's err_returning_call, raise_err, and indirect_raise_err examples across the described CPython versions. Trace how a caught exception's traceback changes when it is raised again, and compare that with raising a newly created exception. Done means a re-raised exception reports only the final raise call while explicit exception chaining retains the expected separate tracebacks.
索引モデルが issue の本文から書いたものです。
評価
- 技術スタック
- python
- 領域
- backend
- issue の種類
- バグ
- 難易度
- 5/5
- 見積もり時間
- 1週間以上
- 活発さ
- 停滞
- 明瞭さ
- 明確に書かれている
- 初心者へのやさしさ
- 35/100