Python 3.15 に上げると何が壊れるのか
- Python 3.15 に上げたら何が壊れるのか、手を動かした結果で知りたい
- sqlite3 や argparse など、地味なところで出るエラーの直し方が知りたい
- lazy import は便利そうだけど、どこで使えるのかあやふや
対象読者: Python 3.14 以前のプロジェクトを 3.15 に上げる技術者。
前提知識: Python の基本構文と venv(または uv)での実行環境の作り方。
結論
3.15 で確実にエラーになるのは sqlite3.connect() の位置引数 と argparse の dest 推定 です。両方とも 3.14 が警告を出して教えてくれます。
# 影響箇所の洗い出しはこれで。EncodingWarning が1つも出なければ UTF-8 化の影響は無し
python -X warn_default_encoding -m pytest
# sqlite3 は位置引数をやめてキーワード引数に(3.14 の時点でやっておけば壊れない)
sqlite3.connect("app.db", timeout=5.0)
sqlite3.connect() の位置引数がエラーになった
3.14 では DeprecationWarning が出るだけでした。これが 3.15 では TypeError になります。
import sqlite3
conn = sqlite3.connect("app.db", 5.0) # 2番目の位置引数は timeout
# 実行結果(3.14.7)
# DeprecationWarning: Passing more than 1 positional argument to sqlite3.connect()
# is deprecated. Parameters 'timeout', 'detect_types', ... will become
# keyword-only parameters in Python 3.15.
同じコードを 3.15 で動かすとこうなります。エラー内容が素直なので直す箇所はすぐ分かりますね。
sqlite3: 3.53.4
位置引数 connect(db, 5.0): connect() takes at most 1 positional arguments (2 given)
キーワード引数 connect(db, timeout=5.0): OK
lazy import(PEP 810)
新しい lazy キーワードを import の前に置くと、モジュールのロードを初回使用時まで遅らせられます。まずは遅延の実測から。
import sys
lazy import json
print("宣言直後 json はロード済みか:", "json" in sys.modules)
data = json.dumps({"lang": "ja"})
print("使用後 json はロード済みか:", "json" in sys.modules, "| dumps:", data)
# 実行結果
# 宣言直後 json はロード済みか: False
# 使用後 json はロード済みか: True | dumps: {"lang": "ja"}
注意点は2つです。lazy はモジュールのトップレベルでしか書けないこと。関数内に書くと構文エラーで止まります。
File "probe_lazy_fn.py", line 2
lazy import json
^^^^^^^^^^^^^^^^
SyntaxError: lazy import not allowed inside functions
もう1つは、ロードが遅延した分、失敗も初回使用時まで遅れること。モジュールが存在しない場合、import 文の行ではなく最初に名前を触った行で例外が出ます。ソースを一括で lazy 化するときは、この2点を先に確認しておくと安心です。
ソースを書き換えずに全部の import を遅延させる -X lazy_imports=all(または環境変数 PYTHON_LAZY_IMPORTS)もあります。import 時に副作用のあるモジュールまで遅れるので、本番でいきなり all にするのではなく、まず lazy キーワードで重い所だけ指定するのが安全ですね。
UTF-8 が既定のエンコーディングに(PEP 686)
encoding 引数を省略した I/O が、システムのロケールではなく常に UTF-8 になります。影響が出るのは主に Windows で、レガシーなコードページ(cp932 など)の環境で Shift-JIS のファイルを読むコードです。
この検証機は Windows の ANSI コードページが 65001(UTF-8)の環境なので、3.14 と 3.15 の間で挙動は変わりませんでした(どちらも open(path).encoding は utf-8)。差が出るのは cp932 の環境です。
影響箇所の調べ方は、3.15 に上げる前から -X warn_default_encoding を付けてテストを回すこと。encoding を省略した箇所が全部 EncodingWarning として出ます。
C:\verify\probe_warn.py:1: EncodingWarning: 'encoding' argument not specified
with open("legacy.txt", "w") as f:
C:\verify\probe_warn.py:3: EncodingWarning: 'encoding' argument not specified
print("encoding:", open("legacy.txt").encoding)
encoding: utf-8
どうしても旧挙動が必要なときは PYTHONUTF8=0 で全体を戻すか、呼び出しごとに encoding="locale" を付けます。ただし両方ともその場しのぎにすぎないので、警告が出た箇所から encoding を明示していくのが本筋です。
frozendict と sentinel
組み込みに frozendict と sentinel が入りました。frozendict は dict のサブクラスではないので、isinstance(x, dict) で判定しているコードは直す必要があります。
fd = frozendict({"x": 1, "y": 2})
print("dict のサブクラスか:", isinstance(fd, dict), "| hash 可能か:", isinstance(hash(fd), int))
try:
fd["x"] = 99
except TypeError as e:
print("代入:", e)
# 実行結果
# dict のサブクラスか: False | hash 可能か: True
# 代入: 'frozendict' object does not support item assignment
sentinel は「値が指定されたかどうか」を判別するための専用型です。デフォルト引数に None を使うと「None を指定した」と「指定しない」を区別できませんでしたが、これがきれいに書けます。
MISSING = sentinel("MISSING")
def get(config, key, default=MISSING):
if default is MISSING:
print(key, "-> 未指定(KeyError 相当の扱い)")
return None
return config.get(key, default)
print(get({"a": 1}, "a", 0))
print(get({}, "b", 0))
get({}, "b")
# 実行結果
# 1
# 0
# b -> 未指定(KeyError 相当の扱い)
argparse の dest 推定が変わった
add_argument("-f", "-foo") のように単一ダッシュの長いオプションを混ぜると、3.15 から dest は短い方(f)ではなく foo になります。Namespace の属性名が変わるので、3.14 まで動いていたコードが AttributeError で落ちます。
# 3.14.7
AttributeError: 'Namespace' object has no attribute 'foo'
# 3.15.0
Namespace の属性: x
直し方は対象の引数に dest= を明示すること。Python 3.15 の Porting セクションにも同じ対処が書かれています。
つまずきポイント
- sqlite3: 位置引数は 3.15 で TypeError。3.14 の DeprecationWarning が実質的な移行告知
- lazy import: 関数内・クラス本体・try ブロックでは SyntaxError。失敗の発生箇所も遅延する
- frozendict: dict のサブクラスではない。isinstance で弾かれているコードがある
- argparse: 単一ダッシュの長いオプションで dest が変わる。dest を明示して固定する
- UTF-8 既定化: ANSI コードページが UTF-8 の Windows では挙動は変わらない。cp932 環境でだけ差が出る
検証環境
| OS | Windows 11 (build 10.0.26200) |
|---|---|
| CPU | AMD Ryzen 9 PRO 8945HS w/ Radeon 780M Graphics |
| メモリ | 28GB |
| ツール | CPython 3.15.0 / CPython 3.14.7 |
上記の環境で実際に実行して確認した結果です。各セクションのコードブロックと出力は、この実測からの抜粋です。

まとめ
3.15 の移行で必ず見るのは sqlite3・argparse・encoding の3点です。どれも 3.14 で警告か lint で拾えるので、3.15 に上げる前に -X warn_default_encoding 付きのテストを1回回しておくのが最短ルートだと思いますよ。
参考
- What’s new in Python 3.15 — Python 3.15.0 documentation
- PEP 686 – Make UTF-8 mode default
- PEP 810 – Explicit lazy imports
- PEP 814 – Add frozendict built-in type
- Porting to Python 3.15
【広告】【Value AI Writer byGMO】高品質なSEO記事をAIで自動生成。![]()
あわせて読みたい
- 【Python】frozendict 深掘り — Python 3.15 の不変辞書の制約とエッジケースを実測
- 【Python】t-string 深掘り — PEP 750 の制約と f-string との違いを実測
- 【Python】flake8 + black + isort から Ruff 0.17 へ移行したログ — 実際に壊れた4箇所
- 【Python】Python 3.14 完全ガイド2026 — Free-Threading と JIT
【広告】このサイトはConoHa WINGで運営しています。安定した高速サーバーで快適にブログを書けています。いつもありがとう!!![]()

コメント