以前「Ruby 4.0 新機能と Ruby 3.x 系コードの互換性維持で注意すべき点」をまとめたが、Python においても 3.x 系の中で最新の 3.14 に至るまで、互換性維持のために注意すべき点がある。
本稿は、Python 3.1 から Python 3.14 までを俯瞰し、実務で境界になりやすい「互換性破壊ポイント」を整理したうえで、各マイナーバージョンの「後方互換性を壊しうる変更」を一覧表に落とし込む。
公式には後方互換性の扱いは PEP 387 に整理されているが[1]、実際の移行作業は「文法」「標準ライブラリ」「警告→削除のタイムライン」「周辺ツール(パーサ、型ヒント、ビルド)」の複合になる。
前提:Python は「互換性は原則守るが、非推奨(Deprecation)→削除(Removal)を段階的に行う」モデルで進む[1]。
そのため、保守観点では「警告が出ている箇所」と「次の削除予定」を早期に把握し、境界を越える前に潰すのが最も効率がよい。
公式の各バージョンの変更点は “What’s New in Python” にまとまっている[2]。
1. 実務で境界になりやすい互換性破壊ポイント
1.1 文法(予約語・構文追加)で「昔の書き方」が突然 NG になる
Python 3.x は言語の大枠は保守的に進むが、それでも「予約語化」や「新構文」により、過去に合法だった識別子が使えなくなる局面がある。
典型は async/await の予約語化(Python 3.7 以降)で、変数名や引数名に async/await を使っていた古いコードが文法エラーになる[3]。
もう一つの代表例は Python 3.10 の構造的パターンマッチ(match/case)で、match/case は “soft keyword” として導入されたが、特定の位置ではキーワードとして解釈されるため、コードジェネレータや DSL 系の字句処理が影響を受ける可能性がある[4][5]。
1.2 標準ライブラリの「非推奨→削除」が、想定より後で効いてくる
互換性破壊は文法よりも、標準ライブラリの削除で起きることが多い。
Python 3.12 では distutils が標準ライブラリから削除され、セットアップ周辺の古い実装が動かなくなる[6][7]。
Python 3.13 では PEP 594 により “dead batteries” とされた複数モジュールが削除され、運用スクリプトが標準ライブラリ依存だった場合に直撃する[8][9]。
さらに 2to3 / lib2to3 の削除も同じ系統で、変換ツール自体だけでなく「lib2to3 をパーサとして組み込んでいた」周辺ツールにも影響しうる[9]。
1.3 型ヒント(アノテーション)の「実行時挙動」を前提にすると壊れる
型ヒントは静的解析向けの仕組みだが、実務では「実行時にアノテーションを読む」系が普及した結果、アノテーション評価の仕様変更が互換性問題になりやすい。
“Postponed Evaluation of Annotations” は PEP 563 として整理され[10]、その後の流れを踏まえて Python 3.14 では PEP 649 に基づく “deferred evaluation” が導入されている[11][12]。
「__annotations__ を直接読んで文字列が入っている前提」や「評価タイミングを暗黙に期待する」コードは、バージョン境界で壊れやすい。
1.4 “警告を無視していると、2 年後に突然落ちる” になりがち
互換性維持のコツは単純で、テスト実行を -W default や -W error で回し、警告を「将来の破壊点の予告」として扱うことだ。
“What’s New” でも「警告を確認しろ」という誘導が繰り返し書かれている[13]。
逆に言えば、警告を CI で見ていないと、標準ライブラリ削除(3.12/3.13)や移動(collections.abc)などの “期限切れ” を踏む。
collections の ABC 移動は「deprecated から数年かけて削除」された典型で、PEP 606 でも例示されている[14]。
2. 互換性維持の実装指針(保守のやり方)
2.1 方針を 2 層に分ける
互換性維持を「全スクリプトで Python 3.1 まで完全互換」にするのではなく、方針に沿って “fully supported” と “partial compatibility” を分けるのが現実的である。
fully supported(例:Python 3.6+)は CI で毎回回す。
partial(例:3.1〜3.5)は「互換性を壊す変更を入れない」ことを優先し、必要なら互換レイヤ(条件分岐・フォールバック)を明示する。
2.2 実務上の境界に合わせた最小チェック項目
| 境界 | 壊れ方(代表例) | 検出・回避の最小策 |
|---|---|---|
| 3.6 | f-string を使い始めると 3.5 以前で即 SyntaxError(PEP 498) | 文字列フォーマットは format / % を残す。f-string は fully supported 範囲内でのみ許可。[15] |
| 3.7 | async/await が予約語化し、識別子として使っていたコードが壊れる | 変数名の衝突を lint で検出。古いコード取り込み時は機械置換を先にかける。[3] |
| 3.9 | collections の ABC エイリアスが削除され、collections.Mapping 等が ImportError | collections.abc から import する。移行期は警告をエラー化して早期発見。[14] |
| 3.10 | match/case(構造的パターンマッチ)により、パーサ依存ツールや DSL のトークン化が破綻しうる | 文字列テンプレートや AST 変換で “match/case をキーワードとして扱うか” を明示し、lib2to3 依存を避ける。[4][5] |
| 3.12 | distutils 削除でビルド/配布まわりが壊れる | setuptools 側へ寄せる(distutils 前提のコードは排除)。[6][7] |
| 3.13 | PEP 594 のモジュール削除、2to3/lib2to3 削除 | 該当モジュールの import を棚卸しし、必要なら代替(PyPI パッケージ等)を採用。変換・解析系で lib2to3 を使っていないか確認。[8][9] |
| 3.14 | アノテーション評価の仕様が PEP 649 により変更され、実行時に __annotations__ を読むコードが影響を受けうる | typing.get_type_hints など推奨 API に寄せる。直接 __annotations__ を読む実装は境界で要テスト。[11][12] |
補足:上の表は「壊れやすい境界」を優先している。個別のバージョン差分は次章の一覧表にまとめる。
情報源は “What’s New in Python” 各ページを基準とする[2]。
3. 各マイナーバージョンでの「後方互換性を壊しうる変更」一覧(3.1→3.14)
ここでいう「壊しうる変更」は、(a) 文法として実行不能になる、(b) 既存 API が削除・移動される、(c) 仕様の厳密化により例外が増える、のいずれかを指す。網羅は各 “What’s New” に委ねるが[2]、実務で事故になりやすい点に寄せて要約した。3.1〜3.5 は現代のコードベースでは「partial compatibility」の範囲に入りやすいため、主に “これ以上新機能を持ち込むと壊れる” という観点で記す。
| バージョン | 後方互換性を壊しうる変更(代表例) | 実務メモ(保守観点) |
|---|---|---|
| 3.1 |
|
古い系は “動けばよい” に寄せ、モダン機能を持ち込まない(型ヒント、async、f-string 等を禁止)。[16] |
| 3.2 |
|
文字列/バイト列の扱いは早い段階で統一し、曖昧な混在を避ける。[17] |
| 3.3 |
|
「古い import が動いてしまう」状態を放置しない。3.3 由来の非推奨は 3.9 などで刈り取られる。[14][18] |
| 3.4 |
|
pathlib など “便利だが古い版では使えない” API は、fully supported 範囲でのみ採用する。[19] |
| 3.5 |
|
“3.5 では動くが 3.7 以降で壊れる” を避けるため、async/await を識別子に使わない運用を早期に固定する。[20] |
| 3.6 |
|
fully supported を 3.6+ に置くなら、ここが最初の大きな境界になる。[15][21] |
| 3.7 |
|
古いコード取り込み時に「async/await の衝突」を最優先で潰す。[3][10] |
| 3.8 |
|
“API removals” と “Porting” を必ず読む運用にする(この頃から削除が実務の事故要因になる)。[22] |
| 3.9 |
|
import パスの互換レイヤは “いつか消える”。依存箇所は grep で棚卸しして先に直す。[13][14][23] |
| 3.10 |
|
パーサ依存(コード生成・変換)をやっているなら、lib2to3 前提を捨てる判断が必要になる。[4][24] |
| 3.11 |
|
この版以降は「非推奨の棚卸し」を CI の定常作業に組み込まないと、3.13 で一気に落ちる。[25] |
| 3.12 |
|
配布・ビルド系に distutils を触るコードが残っているなら最優先で排除。[6][7] |
| 3.13 |
|
運用スクリプトは “dead batteries” を使いがちなので、import の棚卸しが効く。[8][9][26] |
| 3.14 |
|
実行時にアノテーションを読むライブラリやユーティリティは、3.14 で挙動差が出うるので回帰テストを置く。[11][12][27] |
注:本表は「後方互換性を壊しうる」観点での代表例であり、個々の “Removed / Deprecated” の完全リストは各バージョンの “What’s New” を参照すること。[2][28]
4. 互換性検査ツール(find_pycompat.py)の位置づけ
実務上、互換性事故は「思っていたより新しい機能を混ぜてしまった」ことが原因になりやすい。
find_pycompat.py のように、f-string、subprocess.run、async/await、型ヒント、pathlib、shutil.which などを機械的に検出するアプローチは、“partial compatibility” を維持するうえで有効である。一方で、標準ライブラリ削除(PEP 594、distutils)やアノテーション仕様のように「構文ではなく依存関係・実行時仕様」で壊れるものは別枠で監視が必要になる。[6][8][12]
実務上の提案(最小構成):
fully supported 範囲(例:3.6+)は CI で全テストを実行し、-W error で警告を潰す。
partial compatibility(例:3.1〜3.5)は、find_pycompat.py の検出対象を “禁止リスト” として扱い、混入を止める。
さらに、標準ライブラリ削除系は import 棚卸し(静的 grep+実運用ログ)で二重に見る。
参考文献
- PEP 387 – Backwards Compatibility Policy. https://peps.python.org/pep-0387/
- What’s New in Python(一覧). https://docs.python.org/3/whatsnew/index.html
- What’s New In Python 3.7. https://docs.python.org/3/whatsnew/3.7.html
- What’s New In Python 3.10. https://docs.python.org/3/whatsnew/3.10.html
- PEP 634 – Structural Pattern Matching: Specification. https://peps.python.org/pep-0634/
- What’s New In Python 3.12. https://docs.python.org/3/whatsnew/3.12.html
- PEP 632 – Deprecate distutils module. https://peps.python.org/pep-0632/
- PEP 594 – Removing dead batteries from the standard library. https://peps.python.org/pep-0594/
- What’s New In Python 3.13. https://docs.python.org/3/whatsnew/3.13.html
- PEP 563 – Postponed Evaluation of Annotations. https://peps.python.org/pep-0563/
- PEP 649 – Deferred Evaluation Of Annotations Using Descriptors. https://peps.python.org/pep-0649/
- What’s new in Python 3.14. https://docs.python.org/3/whatsnew/3.14.html
- What’s New In Python 3.9. https://docs.python.org/3/whatsnew/3.9.html
- PEP 606 – Python Compatibility Version(collections ABC aliases removal の例示など). https://peps.python.org/pep-0606/
- What’s New In Python 3.6. https://docs.python.org/3/whatsnew/3.6.html
- What’s New In Python 3.1(参考:古い差分の確認用). https://docs.python.org/3/whatsnew/3.1.html
- What’s New In Python 3.2. https://docs.python.org/3/whatsnew/3.2.html
- What’s New In Python 3.3. https://docs.python.org/3/whatsnew/3.3.html
- What’s New In Python 3.4. https://docs.python.org/3/whatsnew/3.4.html
- What’s New In Python 3.5. https://docs.python.org/3/whatsnew/3.5.html
- PEP 498 – Literal String Interpolation(f-string). https://peps.python.org/pep-0498/
- What’s New In Python 3.8. https://docs.python.org/3/whatsnew/3.8.html
- LWN: Postponing some feature removals in Python 3.9(collections.abc などの削除延期の文脈). https://lwn.net/Articles/811369/
- CPython issue: Deprecate lib2to3(背景). https://github.com/python/cpython/issues/84540
- What’s New In Python 3.11. https://docs.python.org/3/whatsnew/3.11.html
- Python Discuss: PEP 594 implementation notice(3.13 removals). https://discuss.python.org/t/pep-594-has-been-implemented-python-3-13-removes-20-stdlib-modules/27124
- Real Python: Python 3.14: Lazy Annotations(解説). https://realpython.com/python-annotations/
- CPython docs sources (Doc/whatsnew). https://github.com/python/cpython/blob/main/Doc/whatsnew/3.14.rst