先日、PEP 657 (Include Fine Grained Error Locations in Tracebacks) が Final になったという PR を見かけました。
そして、cpython に実装が取り込まれていることが確認できたので、あらためて紹介しようと思います。
概要
- 1行で複数の処理をしている箇所でエラーが発生した際に、どの処理でエラーが発生したのか表示する
- 表示を改善するため、Python のバイトコード命令(bytecode instructions)に(行番号に加えて)オフセット情報を追加する
- バイトコードのサイズが約 20% 増加する (標準ライブラリでの計測値)。.pyc ファイルと消費メモリに影響する
- 環境変数
PYTHONNODEBUGRANGES
やコマンドラインオプション-Xno_debug_ranges
で無効化できる
アプローチ
以下の内容のスクリプトを用意しました。2行目では多段インデックスアクセスを行っていますが、値が入っていないので TypeError が発生します。
d = dict(x=None)
d['x']['y']['z'] = 1
このスクリプトを Python 3.9.5 で実行すると TypeError
が発生し、以下のようなエラーが表示されます。
$ python -V
Python 3.9.5
$ python test.py
Traceback (most recent call last):
File "/Users/tkomiya/test.py", line 2, in <module>
d['x']['y']['z'] = 1
TypeError: 'NoneType' object is not subscriptable
同じスクリプトを Python 3.11.0 (開発版)で実行すると、エラーが発生した箇所にアンダーラインが表示されます。
$ python3.11 -V
Python 3.11.0a0
$ python3.11 test.py
Traceback (most recent call last):
File "/Users/tkomiya/test.py", line 2, in <module>
d['x']['y']['z'] = 1
~~~~~~^^^^^
TypeError: 'NoneType' object is not subscriptable
なお、Python を対話モードで起動した場合はこの機能は動かないようです。
$ python3.11
Python 3.11.0a0 (heads/main:919ad53, Jul 16 2021, 11:07:03) [Clang 11.0.3 (clang-1103.0.32.62)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> d = dict(x=None)
>>> d['x']['y']['z'] = 1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'NoneType' object is not subscriptable
感想
- エラー表示がかっこよくなったのはめちゃくちゃ素敵。はやく 3.11 使いたい
- この修正はバイトコードへの属性追加であるため、エラー以外にも用途が広がる可能性はありそうです。IDE、デバッガなどでも対応してくれるとよさそう
- pytest が対応してくれるといいなあ