概要
pyenv-virtualenvで構築したpython3.7.0仮想環境で、cx_Freeze(5.1.1)を使ってバイナリを作ろうとしたのですが失敗したので、その対処法をまとめました。
**注意:**本件のエラーはおそらく一時的なものなので、インストールしたモジュールを直接書き換えるというその場しのぎ的な対応になります。(2018年11月18日現在)
また、本記事では、仮想環境にインストールしたパッケージを触るだけなので、/usr/bin
に入っているpythonの環境や他に構築した仮想環境には影響がないのでご安心ください。
エラーが出るまで
仮想環境の構築
使用している環境としては、Linux(CentOS7),64bitです。
python3.7.0 の仮想環境の構築はpyenv と pyenv-virtualenv で環境構築を参照して作りました。
構築した仮想環境は、python3.7.0
でnew_3.7.0
という名前で作りました。
バイナリの作成
バイナリの作成には、cx_Freeze(5.1.1)
をpip
でインストールしてやりました。
【python】Python スクリプトを実行ファイルに変換する方法あたりを参考に作りました。
バイナリの実行/エラーの発生
作成したバイナリを実行すると次のようなエラーが出力されました。
(new_3.7.0)$ ./my_package
Fatal Python error: initfsencoding: unable to load the file system codec
ImportError: invalid flags 1536114382 in 'encodings'
Current thread 0x00007f6f3ffa9740 (most recent call first):
中止 (コアダンプ)
ちなみにpython3.6.6
で構築した仮想環境で作成したバイナリでは同様のエラーは確認されませんでした。
解決
しばらく調べてみていると分かったことが、本事案につきまして被害者は多数いる模様でした。
What could be the reason for fatal python error:initfsencoding:unable to load the file system codec?
によれば、
Fixed in 4c18633. The problem is that for version x64 for Python 3.7 it is not working due to class config for freezer.py. You need to go cx_Freezer installation folder. If you have a virtual environment, go to your environment folder
\lib\site-packages\cx_Freeze
, find the freezer.py and add the code found on the commit.You can find the patch at GitHub
なんと。
環境フォルダにあるfreezer.py
を4c18633にある通りに、書き直しなさいとのこと。
やってみましょう。
仮想環境内のcx_Freezerを触る
仮想環境の中でインストールしたモジュールは次のコマンドで確認します。
(new_3.7.0)$ pip show cx_freeze
Name: cx-Freeze
Version: 5.1.1
Summary: create standalone executables from Python scripts
Home-page: https://anthony-tuininga.github.io/cx_Freeze
Author: None
Author-email: None
License: Python Software Foundation License
Location: /#home_dir#/.pyenv/versions/3.7.0/envs/new_3.7.0/lib/python3.7/site-packages
Requires:
Required-by:
/#home_dir#/.pyenv/versions/3.7.0/envs/new_3.7.0/lib/python3.7/site-packages
にあるみたいなので、移動して確認します。
(new_3.7.0)$ ls
__init__.py dist.py initscripts setupwriter.py
__pycache__ finder.py macdist.py util.cpython-37m-x86_64-linux-gnu.so
bases freezer.py main.py windist.py
common.py hooks.py samples
なるほど。freezer.py
を触ったらいいみたいです。
修正箇所は、L550
以降のこの領域を全て削除して、
# the file is up to date so we can safely set this value to zero
if module.code is not None:
if module.file is not None and os.path.exists(module.file):
mtime = os.stat(module.file).st_mtime
else:
mtime = time.time()
if sys.version_info[:2] < (3, 3):
header = magic + struct.pack("<i", int(mtime))
else:
header = magic + struct.pack("<ii", int(mtime), 0)
data = header + marshal.dumps(module.code)
次のコードに書き換えてください。
# the file is up to date so we can safely set this value to zero
if module.code is not None:
if module.file is not None and os.path.exists(module.file):
stat = os.stat(module.file)
mtime = stat.st_mtime
size = stat.st_size & 0xFFFFFFFF
else:
mtime = time.time()
size = 0
if sys.version_info[:2] < (3, 3):
header = magic + struct.pack("<i", int(mtime))
elif sys.version_info[:2] < (3, 7):
header = magic + struct.pack("<ii", int(mtime), 0)
else:
header = magic + struct.pack("<iii", 0, int(mtime), size)
data = header + marshal.dumps(module.code)
ビルド/実行
python setup.py build
を実行してビルドします。
そして、バイナリが作られているのを確認して実行します。
するとまあ、なんということでしょう。
(new_3.7.0)$ ./my_package
Start analysis
...
そこには、元気に走り回るパッケージ君の姿が!!
最後に
こんな感じで、cx_Freezeがまだ更新されていないようなので、今回はこのようにコミットされていたパッチを参考に、仮想環境にインストールしたモジュールに直接書き込むことで、その場しのぎの緊急対応ができました。
ただ、モジュール自体に手を加えるのは想定外のバグをまねく可能性が大きいため、今回のようにあらかじめ構築した仮想環境上で、編集を加えた方がリスクが少なくります。
cx_Freeze
が更新されて、python3.7.0でも問題なく実行できるよう、
1日でも早くこの記事が闇に葬り去られる日が来ることを祈ります。
もし、記事に間違っている点やわからない点がありましたらコメントくださると幸いです。
お読みくださいましてありがとうございました。