Pythonがファイルを開くときなどに使われるエンコーディングはロケール(WindowsではANSIコードページ)依存でした。
Unixの世界ではどんどんUTF-8ロケールが一般的になっている一方、WindowsのANSIコードページはなかなかUTF-8になりません。
そのために、Unixユーザーが open(filepath)
のようにエンコーディングを指定しないままUTF-8を仮定するコードを気軽に書いてしまって、Windowsユーザーがエラーで困るといった問題が発生します。
また、Windowsでもメモ帳(Notepad.exe)やVSCodeはすでにUTF-8をデフォルトのエンコーディングで使用しています。ANSIコードページがUTF-8になるのを待っていたらどんどん周りの環境から置いていかれ、レガシー化してしまいます。
Pythonがデフォルトで利用するエンコーディングをWindows上でもUTF-8にするために数年前から活動していたのですが、今回PEP 686がAcceptされ、Python 3.15 (2026年10月リリース予定)からUTF-8 Modeがデフォルトで有効になる予定になりました。(執筆時点ではまだ正式にAcceptはされてませんが、条件付きでAcceptされています)
なお、UTF-8 Mode自体はPython 3.7から利用できます。Windows上でPythonを使っている人は、2026年を待たずにUTF-8 Modeを使ってみて、フィードバックをもらえると嬉しいです。
UTF-8 Mode
UTF-8 Modeを有効にするには、 PYTHONUTF8=1
という環境変数を設定します。 -Xutf8
というコマンドラインオプションも利用できますが、PythonからサブプロセスとしてPythonを起動したときにUTF-8 Modeが引き継がれなくなるので、環境変数を使って設定する方をお勧めします。
UTF-8 Modeが有効になると、今までデフォルトでロケールエンコーディングを使用していたほとんどの場面でUTF-8がデフォルトに切り替わります。実際に影響が出る場面と、UTF-8 Modeでもロケールエンコーディングを使う方法を解説しておきます。
encoding
オプション
open()
や pathlib.Path.read_text()
, socket.makefile()
など、 TextIOWrapper
を利用している多くのAPIが encoding
オプションを持っていて、UTF-8 Modeが有効になると encoding
を指定しなかった時のデフォルトが UTF-8 になります。
明示的にロケールエンコーディングを使用するために encoding="locale"
というオプションを Python 3.10 で追加していたのですが、これもUTF-8 ModeではUTF-8になってしまっていました。この問題は Python 3.11 で解決されます。
Windows専用のスクリプトであれば "mbcs" というコーデックが昔からあるので、Python 3.10以前でも encoding="mbcs"
のように指定してロケールエンコーディングを利用できます。クロスプラットフォームのスクリプトでは使えないので、UTF-8 Modeを使うのは Python 3.11 を待ってからの方が良いでしょう。
Standard I/O
Windowsで標準入出力がコンソールになっているときは、 (PYTHONLEGACYWINDOWSSTDIO
という環境変数を設定しない限り)入出力はファイルAPIではなくUTF-16ベースのコンソールAPI (WriteConsoleW()
など)を使うので UTF-8 Mode は影響ありません。
問題は標準入出力がファイルやパイプにリダイレクトされている場合です。今まではロケールエンコーディングが使われていましたが、UTF-8 ModeではUTF-8が使われます。
標準入出力がUTF-8になるのはnode.jsやGo、Rustなど、モダンな言語やランタイムの多くと共通した挙動です。なのでできれば MSYS を使うとか、PowerShellであれば [console]::OutputEncoding
と [console]::InputEncoding
を UTF-8 に設定するなど、周辺の環境もまとめてUTF-8に統一できればそれが一番いいと思います。
標準入出力をロケールエンコーディングのままにしたい場合は、 PYTHONIOENCODING
環境変数が UTF-8 Mode よりも優先されるので、 set PYTHONIOENCODING=mbcs
とか set PYTHONIOENCODING=cp932
のように設定できます。
これまでの経緯
2016年 UTF-8 Mode 誕生 (PEP 540)
Docker等で en_US.UTF-8 や C.UTF-8 などの UTF-8 localeを持たないミニマムな環境が使われることが増え、そういった環境でもUTF-8ロケールと同じように振る舞う設定が欲しいということでUTF-8 Modeが生まれました。
私はこのPEPをBDFL-DelegateとしてAcceptしましたが、この時はまだUnix環境のことしか考えていませんでした。
2019~ UTF-8デフォルト化を真剣に考え始める
Windows 10 の 2019 May Update で、Notepad.exeのデフォルトエンコーディングがUTF-8に変更されました。
そのニュースがきっかけで、Pythonがテキストファイルを開く時のデフォルトエンコーディングをWindowsを含めた全プラットフォームでUTF-8に統一するべきだと考え、本格的に議論や活動を開始しました。
このときにUTF-8 ModeがWindowsでUTF-8をデフォルト化するのにも使えると気づき、UTF-8 Modeについての記事を書いたりもしていました。
- https://discuss.python.org/t/use-utf-8-as-default-text-file-encoding/1785
- https://discuss.python.org/t/pep-597-use-utf-8-for-default-text-file-encoding/1819
- https://discuss.python.org/t/pep-597-enable-utf-8-mode-by-default-on-windows/3122
- https://discuss.python.org/t/pep-597-emit-a-warning-when-encoding-is-omitted/3880
- https://qiita.com/methane/items/9a19ddf615089b071e71
このときに書いた提案が PEP 597ですが、最終的に PEP 597は大きく2回書き直しになりました。その中には、 PEP 686 と同じく(ただしWindowsでだけ) UTF-8 Mode を デフォルトで有効化する提案もありましたが、オプションで元に戻せるといっても後方互換性を大きく壊す変更に反対する意見が強く、このときには意見をまとめることができませんでした。
特に意見が分かれたのが、 encoding
を指定していない open()
等の呼び出し全てを Deprecate して警告を出すべきかどうかです。どちらの意見とも強く主張するコア開発者が居て、僕の英語力とファシリテーション力ではとても議論をまとめることができませんでした。
最終的に、PEP 597を、 encoding
を指定していない場合に EncodingWarning
という警告を出すオプションを追加するという、両者の間を取った提案に書き直して、2021年にAcceptされ、Python 3.10 に実装されました。
2022年3月 JDK18リリース
2022年3月、JDK18がリリースされました。このJDKからJEP 400 が採用され、JavaもUTF-8をデフォルトのテキストエンコーディングにするようになりました。
UTF-8デフォルト化の議論は今年後半まで休むつもりだったのですが、JEP 400に背中を押されて改めて議論を開始し、 PEP 686 を書きました。
- https://discuss.python.org/t/jep-400-utf-8-by-default-and-future-of-python/14246
- https://discuss.python.org/t/pep-686-make-utf-8-mode-default/14435
- https://discuss.python.org/t/pep-686-make-utf-8-mode-default-round-2/14737
JEP 400 では後方互換性のために -Dfile.encoding=COMPAT
というオプションも追加されています。同じようにオプションで当面の後方互換性を確保するべきだと思い、しかし新たなオプションを追加したくはないので、すでにon/offをオプションで切り替えられるUTF-8 Modeをデフォルトで有効化する提案にしました。
この提案は基本的には一度あきらめた提案(WindowsでUTF-8 Modeをデフォルトで有効にする)に近いものですが、今回はより多くの根拠を提示できました。
- Javaが同等の変更を先に行った
EncodingWarning
をたくさん修正してきた結果encoding
を省略しているコードはほとんどがencoding="utf-8"
に置き換えても問題ない。それで壊れることよりも、圧倒的にバグ修正になる方が多い。- ASCIIテキストを読み書きするのに
encoding
を指定するのは(やはり)大変な労力である。
また、UTF-8 Modeはロケールを無視してUTF-8を使う思想になっていたために、デフォルトで有効化すると今度は逆に本当にロケールエンコーディングを利用したいときに困ります。上で紹介した encoding="locale"
の問題もこの一部です。こういった問題を洗い出して、Python 3.11からUTF-8 Modeをより広い人に試してもらえるように修正していきました。
最終的に、変更を実施するバージョンを3.15まで先延ばしする条件でPEP 686はAcceptされました。
デフォルトが変更されるのに2026年まで待たないといけないのは残念ですが、将来デフォルトになることが公式になれば、ユーザーは2026年を待たずともUTF-8 Modeを有効にしやすくなるはずです。
特に学生にPythonでプログラミングを教えている場合は、後方互換性を気にする必要がないので、最初に全員にUTF-8 Modeを有効化してもらうと良いと思います。テキストとバイナリの違いやエンコーディングについて教えなくても、VSCode等のエディタで読み書きするのと同じテキストファイルを open(filename)
と open(filename, "w")
で読み書きできるようになるでしょう。
今回のPEPは、私にとってPython 3.6の Compact Ordered dict と同じくらい重要な Python の改善になりました。Pythonのコア開発者になって本当に良かったと思っています。 最後になりますが、今回の PEP を書く上で大きな力をくれた JEP 400 の作者の @naotoj さん、ありがとうございました。