記事の内容
pykakasiを使用したPythonコードについて、PyInstallerで生成した実行ファイルを実行したら、エラー「KeyError: 'max_key_len'」が発生しました。
それを回避する方法を記録しておきます。
Linux(Ubuntu 18.04)、python 3.7.5で実行し、pipenvを使用しています。
※ (2020-02-27) エラー回避方法について、もっと良い方法がわかりましたので、末尾に追記しました。
サンプルコード
この記事のコードを改変して使用しました。以下の3ファイルを使用し、foo.pyをバイナリにしたものを起動することにします。
from mymod1 import bar
from mymod2 import hoge
bar("Hello!")
hoge("ほげ!")
def bar(s):
print(f"bar: {s}")
import pykakasi
def hoge(s):
kakasi = pykakasi.kakasi()
kakasi.setMode('H', 'a')
kakasi.setMode('K', 'a')
kakasi.setMode('J', 'a')
conv = kakasi.getConverter()
print(conv.do(s))
実行時エラーが起きるまでの手順
端末で以下を実行して、モジュールをインストールします。
pipenv install setuptools pykakasi pyinstaller
現段階で存在するファイルは以下の通りです。
$ ls
Pipfile
foo.py
mymod1.py
mymod2.py
生成されたPipfileは以下の通り。
[[source]]
name = "pypi"
url = "https://pypi.org/simple"
verify_ssl = true
[dev-packages]
[packages]
setuptools = "*"
pykakasi = "*"
pyinstaller = "*"
[requires]
python_version = "3.7"
端末で以下を実行して、実行ファイルを作成します。
pipenv run pyinstaller foo.py
lsでファイルを確認すると、buildディレクトリとdistディレクトリが生成されています。
$ ls
Pipfile
build
dist
foo.py
foo.spec
mymod1.py
mymod2.py
「dist/foo/foo」が実行ファイルですので、動かしてみると、以下のように「KeyError: 'max_key_len'」が発生しました。
$ dist/foo/foo
bar: Hello!
Traceback (most recent call last):
File "foo.py", line 5, in <module>
hoge("ほげ!")
File "mymod2.py", line 10, in hoge
conv = kakasi.getConverter()
File "pykakasi/kakasi.py", line 91, in getConverter
File "pykakasi/kanji.py", line 30, in __init__
File "pykakasi/kanji.py", line 126, in __init__
KeyError: '_max_key_len_'
[6202] Failed to execute script foo
実行時エラーの回避
dist配下のpykakashiの辞書が不足しているのが原因でした。
dist配下には、以下のように辞書が1つ存在します。
$ ls dist/foo/pykakasi/data/
itaijidict3.db
一方で、pipenvによるインストール先には8つの辞書がありました。
$ ls `pipenv --venv`/lib/python3.7/site-packages/pykakasi/data/
hepburndict3.db
itaijidict3.db
kunreidict3.db
passportdict3.db
hepburnhira3.db
kanwadict4.db
kunreihira3.db
passporthira3.db
これをdist配下に上書きでコピーします。
$ cp `pipenv --venv`/lib/python3.7/site-packages/pykakasi/data/* dist/foo/pykakasi/data/
$ ls dist/foo/pykakasi/data/
hepburndict3.db
itaijidict3.db
kunreidict3.db
passportdict3.db
hepburnhira3.db
kanwadict4.db
kunreihira3.db
passporthira3.db
再度バイナリを実行すると、以下のように正常に動きました。
$ dist/foo/foo
bar: Hello!
hoge!
(2020-02-27) 追記
もっと良い方法がわかりましたので、追記します。
以下の追記部分はWindowsで実行しました。
参考ページ(感謝します)
・PyInstallerで実行ファイルにリソースを埋め込み
最初の実行ファイル生成により、foo.specファイルも生成されました。
このファイルを以下のように編集して、a.datasにpykakasi辞書ファイルをすべて追加します。
pykakasi辞書ファイルの場所は環境によって異なりますので、そこは書き換える必要があります(私の環境ではCドライブから下っていった場所)。
hiddenimportsにも手を加えていますが、これについてはこちらの記事をご覧ください。
# -*- mode: python ; coding: utf-8 -*-
block_cipher = None
a = Analysis(['foo.py'],
pathex=['C:\\Users\\username\\PycharmProjects\\foo_project'],
binaries=[],
datas=[],
- hiddenimports=[],
+ hiddenimports=['pkg_resources.py2_warn'],
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False)
+a.datas += [('pykakasi\\data\\hepburndict3.db', 'C:\\Users\\username\\.virtualenvs\\d_predict-Evs_tHQX\\lib\\site-packages\\pykakasi\\data\\hepburndict3.db', 'DATA')]
+a.datas += [('pykakasi\\data\\hepburnhira3.db', 'C:\\Users\\username\\.virtualenvs\\d_predict-Evs_tHQX\\lib\\site-packages\\pykakasi\\data\\hepburnhira3.db', 'DATA')]
+a.datas += [('pykakasi\\data\\itaijidict3.db', 'C:\\Users\\username\\.virtualenvs\\d_predict-Evs_tHQX\\lib\\site-packages\\pykakasi\\data\\itaijidict3.db', 'DATA')]
+a.datas += [('pykakasi\\data\\kanwadict4.db', 'C:\\Users\\username\\.virtualenvs\\d_predict-Evs_tHQX\\lib\\site-packages\\pykakasi\\data\\kanwadict4.db', 'DATA')]
+a.datas += [('pykakasi\\data\\kunreidict3.db', 'C:\\Users\\username\\.virtualenvs\\d_predict-Evs_tHQX\\lib\\site-packages\\pykakasi\\data\\kunreidict3.db', 'DATA')]
+a.datas += [('pykakasi\\data\\kunreihira3.db', 'C:\\Users\\username\\.virtualenvs\\d_predict-Evs_tHQX\\lib\\site-packages\\pykakasi\\data\\kunreihira3.db', 'DATA')]
+a.datas += [('pykakasi\\data\\passportdict3.db', 'C:\\Users\\username\\.virtualenvs\\d_predict-Evs_tHQX\\lib\\site-packages\\pykakasi\\data\\passportdict3.db', 'DATA')]
+a.datas += [('pykakasi\\data\\passporthira3.db', 'C:\\Users\\username\\.virtualenvs\\d_predict-Evs_tHQX\\lib\\site-packages\\pykakasi\\data\\passporthira3.db', 'DATA')]
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
exe = EXE(pyz,
a.scripts,
[],
exclude_binaries=True,
name='foo',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
console=True )
coll = COLLECT(exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=True,
upx_exclude=[],
name='foo')
書き換えたspecファイルを使用して、再び実行ファイルを生成します。
pipenv run pyinstaller foo.spec
生成されたバイナリを実行すると、以下のように正常に動きました。
C:\Users\username\PycharmProjects\foo_project>dist\foo\foo
bar: Hello!
hoge!
バイナリのあるdistディレクトリ配下には、以下のようにpykakasi用のサブディレクトリが生成されて、辞書がコピーされているのが確認できます。
C:\Users\username\PycharmProjects\foo_project>dir .\dist\foo\pykakasi\data
C:\Users\username\PycharmProjects\foo_project\dist\foo\pykakasi\data のディレクトリ
<DIR> .
<DIR> ..
5,852 hepburndict3.db
5,649 hepburnhira3.db
13,981 itaijidict3.db
7,154,489 kanwadict4.db
5,843 kunreidict3.db
5,642 kunreihira3.db
6,635 passportdict3.db
6,370 passporthira3.db
追記は以上です。