Python 3.10 の問題点:内部変更によるバグの露呈(PEP 626 等)
By JoeVu, at: 2025年8月6日15:01
Estimated Reading Time: __READING_TIME__ minutes
![[Python 3.10 issues] Internal Changes Exposed Bugs (PEP 626 & friends)](/media/filer_public_thumbnails/filer_public/1c/ce/1cce4ecb-573f-4c12-b812-74284cc12ee3/python_310_issues_internal_changes_exposed_bugs_pep_626__friends.png__1500x900_q85_crop_subsampling-2_upscale.jpg)
![[Python 3.10 issues] Internal Changes Exposed Bugs (PEP 626 & friends)](/media/filer_public_thumbnails/filer_public/1c/ce/1cce4ecb-573f-4c12-b812-74284cc12ee3/python_310_issues_internal_changes_exposed_bugs_pep_626__friends.png__400x240_q85_crop_subsampling-2_upscale.jpg)
発生する可能性のある症状
-
デバッガ/プロファイラ/トレーサが異常な動作をする(ブレークポイントが行をスキップする、カバレッジが不一致になる)。
-
バイトコードオフセットに依存するツールが動作しなくなる(例:frame.f_lastiに関する仮定)。
-
フレーム/バイトコードを検査するカスタムインストルメンテーションが不正確になる。
原因
Python 3.10では正確な行番号テーブル(PEP 626)が採用され、インタプリタがバイトコードをソース行にマッピングする方法が変わりました。これに伴い:
-
frame.f_lastiは、生のバイトオフセットではなく命令オフセットになりました(インタプリタは以前はwordcodeに切り替わっていましたが、3.10ではセマンティクスが厳密化されました)。What’s New in 3.10 → Tracebacks & line numbersとフレームオブジェクトのドキュメントを参照してください。
-
例外とトレースの行番号テーブルはより正確になりましたが、これはユーザーにとって素晴らしいことですが、古い非公式な動作に依存していたツールを壊してしまいます。
修正/移行パターン
1) 内部バイトコードの詳細に依存しない。サポートされているAPIを使用する。
-
オフセットをハードコーディングする代わりに、命令/オフセットについて推論するにはdisモジュールを使用することをお勧めします。
-
ソースの位置については、内部を自分でデコードするのではなく、frame.f_linenoとコードオブジェクト属性(co_filename、co_firstlineno、3.11+ではco_positions())を使用してください。inspectを参照してください。
2) PEP 626のセマンティクスに合わせてトレース/デバッグロジックを更新する。
-
sys.settrace/sys.setprofileを実装する場合は、より多くのトレースイベントとより正確な行のジャンプを期待してください。トレースフックのドキュメントを確認し、カバレッジの過剰カウントやブランチの欠落がないようにフィルタを調整してください。
3) 可能な限り上位レベルのライブラリを使用する。
-
コードの書き換え/解析には、バイトコードを直接操作するのではなく、ast/LibCSTを使用することをお勧めします。バイトコードは安定したインターフェースではありません。ASTは安定しています。
4) C拡張とフレーム/バイトコードの統合を再構築して再テストする。
-
C拡張がフレームの内部またはコードオブジェクトに触れる場合は、3.10で再コンパイルし、3.10 C-APIの変更点に対して監査してください。
例:バイトオフセットを想定したトレースツール
# 旧:生のf_lastiを比較(バイトオフセットとして扱う)
def trace(frame, event, arg):
if frame.f_lasti in SOME_BYTE_OFFSETS: # 脆い
...
return trace
# 新:disを使用して命令オフセットを確実にマッピングする
import dis
def trace(frame, event, arg):
code = frame.f_code
instrs = list(dis.get_instructions(code))
# 生の数値の代わりに(offset, starts_line)またはopname/nameパターンで一致させる
...
return trace
次回以降のトラブル回避方法
-
バイトコードは不安定なものとして扱う。検査する必要がある場合は、disを通して行い、オフセットのシフトに対してロジックを堅牢に保つ。
-
新しいPythonバージョン(アルファ版/ベータ版)で早期にCIを追加することで、GA前にトレース/カバレッジツールで変更点を検出する。
-
バージョン間の移植性を高めるために、AST/ソースレベルのインストルメンテーションを優先する。