カテゴリー: Python

Python 3.15正式リリースまであと13日!コードが壊れないか事前チェック - UTF-8デフォルト化(cp949脱出)・lazy import移行チェックリスト 🐍

この記事は韓国語の原文から翻訳したものです。

目次12項目

こんにちは!Mojangcamです 😎

ついにPython 3.15の正式版が2026年10月1日にリリースされます。今日は9月18日なので、ちょうどあと13日(D-13)です。

これまでこのブログでは、3.15についてベータ4編、rc1編、rc2編と「何が新しくなったか」を中心に扱ってきました。今日は少し方向を変えてみます。新機能の見物ではなく、「今使っているコードが3.15で壊れないか?」をチェックする記事です。

特に韓国でPythonを使っている人にとって、今回の3.15は本当に大きな変化です。あの悪名高いcp949エンコーディング地獄がついに終わるからです。ただ、終わる代わりに、これまでcp949に頼って動いていたコードは逆に壊れる可能性があります。😅

📌 準備するもの

  • Python 3.15.0rc2(2026-09-01リリース、正式版前の最後のrc)
  • 今まで使っていたPython 3.13または3.14(比較用に一緒に入れておくと便利です)
  • テスト用のテキストファイル数個(できればcp949で保存された韓国語のファイルを1つ)
  • 使い慣れたエディタ(VS Codeがおすすめ)

正式版の前に試しておくのが目的なので、既存のバージョンは削除せず、別途インストールしてください。Windowsではインストール時にpyランチャーも一緒に入るので、py -3.15 スクリプト.py でバージョンを選んで実行できます。

ターミナル
# インストールされているPythonのバージョンを確認(Windows)
py --list

# 3.15で実行
py -3.15 test.py

# Mac/Linux
python3.15 test.py

🧠 なぜ今チェックすべきなのか?

「正式版が出たらそのときやればいいや」と思っている方も多いですが、今回は少し事情が違います。

3.15のPEP 686は、ファイルを開く方法そのもののデフォルト値を変えます。これが怖いのは、エラーが出ずに静かに壊れることもあるという点です。プログラムは問題なく動いているのに、CSVの韓国語が ????? になっていたり、ログファイルを開いたら意味不明な文字の羅列になっていたりする状況です。

特に毎日自動で動くスクリプト(Excel自動化、クローリング、メール送信のようなもの)は、人が毎回結果を確認しないので、数日後にようやく気づくことになります。だから事前のチェックが必要なんです。

🧩 変化1 - これからファイルは必ずUTF-8で開かれます(PEP 686)

一番重要な変化から見ていきましょう。

3.14までは、open()にencodingを書かないとOSのロケールエンコーディングが使われていました。韓国語版Windowsでは、それがまさにcp949(EUC-KR系)でした。Mac・LinuxではUTF-8でしたね。だから同じコードをWindowsで動かすと文字化けして、Macでは問題ない、というあの有名な現象が起きていたわけです。

3.15からはOSに関係なく必ずUTF-8です。

Python
# Python 3.15 / エンコーディングを書かないと?

with open("메모.txt") as f:      # 3.14以下(Windows)→ cp949
    print(f.read())               # 3.15 → UTF-8(OSに関係なく)

一つずつ見ていきましょう 💡

  • open()のデフォルトencoding:3.15からUTF-8固定。PYTHONUTF8のような環境変数をいじらなくてもそうなります。
  • subprocess(text=True):こちらも同じくUTF-8基準で動作します。
  • encoding="locale":以前のように「OSのロケールに従え」と明示したいときは、この特殊な値を使います。
  • locale.getencoding():現在のOSロケールのエンコーディングが何かを直接調べるときに使う関数です。

🛠️ 自分のコードが危ないか30秒で確認する方法

一番早い方法は、エンコーディングを省略したopen()をすべて見つけ出すことです。Pythonにはそのための警告オプションが元々用意されています。

ターミナル
# デフォルトエンコーディングに依存しているコードに警告を出します
py -3.15 -X warn_default_encoding my_script.py

こうして実行すると EncodingWarning が表示され、何行目が問題なのかを教えてくれます。ここで引っかかった行が、3.15で動作が変わる候補です。

🛠️ 直し方は3つのうちどれか

① 一番おすすめ - エンコーディングを直接書く

Python
# これから作るファイル:UTF-8に統一
with open("result.txt", "w", encoding="utf-8") as f:
    f.write("こんにちは")

# 以前cp949で保存したファイルを読むとき
with open("old_data.csv", encoding="cp949") as f:
    print(f.read())

これが一番確実です。どのバージョンでも同じように動作しますからね。エンコーディングはとにかく常に書くを習慣にするのが正解です。

② 以前の動作のまま使いたいなら

Python
# 「OSのロケールに従え」と明示(韓国語版Windowsならcp949)
with open("legacy.txt", encoding="locale") as f:
    print(f.read())

③ 急ぎなら丸ごと元に戻す(応急処置)

ターミナル
# 環境変数でUTF-8モードをオフにする
set PYTHONUTF8=0        # Windows CMD

# または実行オプションで
py -3.15 -X utf8=0 my_script.py

③は応急処置です。PythonがUTF-8に向かう流れは戻ってこないので、時間を稼いでおいて、最終的には①で直すのが正しいやり方です。

💡 Excelの文字化けは少し話が違います

自動化の記事ネタとしてよく出てくるケースですが、UTF-8でCSVを保存したのにExcelで開くと文字化けする問題は、3.15とは関係なく昔からある別の問題です。ExcelはBOMなしのUTF-8をうまく認識できないんです。

Python
import csv

# Excelでダブルクリックして開くCSVならutf-8-sig
with open("보고서.csv", "w", encoding="utf-8-sig", newline="") as f:
    w = csv.writer(f)
    w.writerow(["名前", "部署", "点数"])
    w.writerow(["山田太郎", "営業", 92])

utf-8-sig:UTF-8で、ファイルの先頭にBOMという目印を付けてくれるエンコーディングです。Excelはこれを見て「あ、UTF-8だな」と認識します。

🧩 変化2 - lazy import、いよいよ本当に使えます(PEP 810)

2つ目は明示的な遅延インポートです。rc段階で確定したので、正式版にそのまま入ります。

Python
# Python 3.15+
lazy import json
lazy from pathlib import Path

lazy を付けたインポートは、その名前を実際に使う瞬間までモジュールを読み込みません。 重いライブラリを先頭にたくさん書いておいて、実際には特定のコマンドでしか使わないスクリプトなら、起動速度が目に見えて速くなります。

一つずつ見ていきましょう 💡

  • lazy:予約語ではなくソフトキーワードです。なので、lazy という変数名を使っていた既存のコードは壊れません。
  • モジュールのトップレベルでのみ使用可能:関数の中、クラスの中、try/except/finally の中で使うとSyntaxErrorになります。
  • 使えない組み合わせ:lazy from module import * と lazy from __future__ import ... は使えません。
  • sys.lazy_modules:遅延されたモジュールは sys.modules の代わりにここに名前が載ります。
  • -X lazy_imports=all:ソースを直さずにすべてを遅延させる実行オプション。逆に normal(デフォルト値)は lazy キーワードだけを尊重します。

⚠️ ここで注意すべき点

lazy import を使うと、インポートの失敗が後から発生します。 本来ならプログラム起動直後に ModuleNotFoundError で知らせてくれたはずのものが、そのモジュールを初めて使う時点でエラーになるからです。

24時間動き続けるDiscordボットのように、深夜に静かに落ちると困るプログラムなら、遅延インポートは控えめに使い、エラーハンドリング・ロギングを一緒に用意しておくことをおすすめします。(関連記事:「Python業務自動化 #13 - ログ・リトライ・失敗通知の仕組みを作る」、「Discordボット開発 #25 - エラーハンドリング・ロギングの仕組みを整える」)

もう一つ、3.15でしか使えない構文だという点も覚えておいてください。3.14以下で動かす必要があるコードに lazy を書くと、単に構文エラーになります。両方のバージョンをサポートする必要があるなら、__lazy_modules__ を使う方法があります。

Python
# 3.15では遅延、それ以下では単に無視される方式
__lazy_modules__ = ["json", "pathlib"]

import json      # 3.15ではlazyとして動作

📌 移行チェックリスト(コピペOK)

正式版の前に、この順番で一度ずつ実行してみてください。

ターミナル
# 1) 3.15rc2をインストールしたあと、既存バージョンと共存しているか確認
py --list

# 2) デフォルトエンコーディングに依存しているコードをすべて見つけ出す
py -3.15 -X warn_default_encoding my_script.py

# 3) テストがあるなら、警告をオンにして全体を実行
py -3.15 -X warn_default_encoding -m pytest

# 4) 主要ライブラリが3.15に対応しているか確認
py -3.15 -m pip install -r requirements.txt

そして、以下の項目を目視でチェックしてみてください。

  • open() に encoding= を書いていない箇所が残っていませんか?
  • 過去にcp949で保存したファイルを読むコードはありますか? → encoding="cp949" を明示
  • Excelで開くCSVを作っていますか? → encoding="utf-8-sig"
  • subprocess.run(..., text=True) でWindowsコマンドの出力を受け取っていますか? → 韓国語などの出力が文字化けしないか確認
  • requirements.txt のライブラリは3.15用のwheelを公開していますか?(C拡張を含むパッケージは新バージョンへの対応が遅れることがあります)
  • 本番サーバー・GitHub ActionsのPythonバージョンをいつ上げるか決めましたか?

📌 よく出るエラー

1. UnicodeDecodeError: 'utf-8' codec can't decode byte 0xb0 in position 0

  • 原因:3.15がUTF-8で読もうとしたのに、そのファイルが実はcp949で保存された韓国語のファイルだったからです。3.14では問題なかったコードが3.15で初めてエラーになる代表的なケースです。
  • 解決:open(パス, encoding="cp949") と明示してください。ファイル自体をUTF-8で保存し直しておくほうがすっきりします。

2. UnicodeDecodeError: 'cp949' codec can't decode byte ...

  • 原因:上とは正反対。3.14以下のWindowsでUTF-8のファイルをエンコーディングなしで開いた場合です。
  • 解決:encoding="utf-8" を明示。このエラーがよく出るなら、3.15に上げるだけでもかなりの数が消えます。

3. SyntaxError: lazy import is only allowed at module level

  • 原因:lazy を関数の中や try ブロックの中で使いました。
  • 解決:ファイルの先頭に移動してください。条件付きで読み込む必要があるなら、以前のように関数の中で通常の import を使えばOKです。

4. プログラムは問題なく動くのに、結果ファイルだけ文字化けする(エラーなし)

  • 原因:読み込みと書き込みのエンコーディングが違うのに、例外が出なかったケース。一番見つけにくいタイプです。
  • 解決:読み込み・書き込みの両方にエンコーディングをすべて明示し、-X warn_default_encoding で漏れがないかざっと確認してください。

5. ERROR: Could not build wheels for ...(3.15インストール中)

  • 原因:そのライブラリがまだ3.15用のビルドを公開していません。正式リリース直後の数週間はよくあることです。
  • 解決:急ぎでなければ待ちましょう。業務用の環境は正式版が出てすぐに上げず、主要な依存関係が3.15のwheelを公開してから移行するのが安全です。

📌 まとめ

今日のまとめです 🙌

  • 10月1日にPython 3.15正式版リリース。今日の時点であと13日(D-13)
  • PEP 686:open() のデフォルトエンコーディングがOSに関係なくUTF-8に統一 → 韓国語版Windowsユーザーにとって一番大きな変化
  • 元に戻すなら encoding="locale"、PYTHONUTF8=0、-X utf8=0 — ただし応急処置
  • PEP 810:lazy import で起動速度を改善。ただしモジュールのトップレベルでのみ、そして3.15専用の構文
  • 今やることはただ一つ — -X warn_default_encoding でエンコーディングを書いていない箇所を見つけて埋める

次回は、正式リリースに合わせて実際に3.15へ引っ越す過程を扱おうと思っています。仮想環境を新しく作って、ライブラリの互換性を確認して、自動化スクリプトを無停止で移すところまでの手順です。お楽しみに!💬

📚 関連記事

  • Python 3.15の新機能まとめ - lazy import・frozendict・UTF-8デフォルト化(ベータ4時点)
  • Python 3.15 rc1リリース!lazy import確定 - ベータ4からの変更点まとめ
  • Python 3.15 rc2リリース!正式版(10/1)前の最終チェック - rc1からの変更点まとめ
  • Python 3.15正式版まであと13日 移行チェックリスト ← 今読んでいるこの記事!
  • Python仮想環境(venv)完全ガイド - 作成から有効化・削除まで

著者

Mojangcam

自分でコードを書き、サーバーを動かし、詰まったエラーを解決しながら学んだことを記録する開発・IT実践ブログ