windows上のpythonでカジュアルにopen()したら UnicodeDecodeError: 'cp932' codec can't decode byte が出た話
windows環境での作業でなるほどと思ったのでメモ。 自作のスクリプトをwindows環境で動かしたら動かなかった。以下のようなエラーメッセージが出る。
UnicodeDecodeError: 'cp932' codec can't decode byte 0x81 in position 59: illegal multibyte sequence
$ ~/.local/pipx/venvs/shosai/Scripts/shosai hatena push --publish about-terraform-nested-output.md extra commands module is not found (shosai.hatena.extra_commands) INFO:shosai.hatena.configuration:read: C:\Users\podhmo/.config/shosai/config.json Traceback (most recent call last): File "C:\Users\podhmo\.local\pipx\venvs\shosai\Scripts\shosai-script.py", line 33, in <module> sys.exit(load_entry_point('shosai', 'console_scripts', 'shosai')()) File "c:\users\podhmo\ghq\github.com\podhmo\shosai\shosai\commands\shosai.py", line 215, in main return submain(args.service, rest_argv) File "c:\users\podhmo\ghq\github.com\podhmo\shosai\shosai\commands\shosai.py", line 300, in submain return params.pop("subcommand")(service, **params) File "c:\users\podhmo\ghq\github.com\podhmo\shosai\shosai\commands\shosai.py", line 137, in push parsed = parsing.parse_article(rf.read()) UnicodeDecodeError: 'cp932' codec can't decode byte 0x81 in position 59: illegal multibyte sequence
原因
原因はUTF-8で作られたファイルをcp932で開いているため。ところで、いまだにwindowsではUTF-8以外の値がエンコーディングのデフォルトとして使われる場合があることに驚いた。
>>> import pathlib >>> open(pathlib.Path("~/.config/shosai/config.json").expanduser()) <_io.TextIOWrapper name='C:\\Users\\podhmo\\.config\\shosai\\config.json' mode='r' encoding='cp932'>
encodingがcp932になっている。
うーん、sys.getdefaultencoding()はUTF-8を返すらしい。
>>> sys.getdefaultencoding() 'utf-8'
真面目にドキュメントをのぞいてみる。
https://docs.python.org/ja/3/library/functions.html?highlight=open#open
encoding が指定されていない場合に使われるエンコーディングはプラットフォームに依存します:locale.getpreferredencoding(False) を使って現在のロケールエンコーディングを取得します。
どうやら、localeモジュールの関数を使って現在のロケールのエンコーディングを取得するようだ。
>>> import locale >>> locale.getpreferredencoding(False) 'cp932'
たしかにcp932。
修正
とはいえ、個人的にopenにencodingを指定して回りたくない。真面目にデフォルトのエンコーディングをUTF-8にする方法を調べてみよう。
どうやら、PYTHONUTF8=1
と環境変数を設定するか -X utf8
というオプション付きで実行すれば良いらしい。
$ python -c 'from pathlib import Path; print(Path("~/.config/shosai/config.json").expanduser().open().encoding)' cp932 $ python -X utf8 -c 'from pathlib import Path; print(Path("~/.config/shosai/config.json").expanduser().open().encoding)' UTF-8
うまく機能していそう。
powershell
あとはこれをデフォルトで使うように設定する。 powershellでは以下のような感じ。
[Environment]::SetEnvironmentVariable("PYTHONUTF8", ”1”, 'User')
bash
とりあえず雑に.bashrcに以下を書いた(.bash_profileでも別に良い)
export PYTHONUTF8=1
気になること
とりあえず動くようになったが、設定は片方だけが良い。git bashで使われる環境変数とpowershellで設定されるuser毎の環境変数は別物何だろうか?