For Python code using pykakasi, when I ran the executable file generated by PyInstaller, I got the error "KeyError:'max_key_len'". Make a note of how to avoid it. I'm running on Linux (Ubuntu 18.04), python 3.7.5 and using pipenv.
Code of this article was modified and used. I will use the following 3 files to start the binary version of foo.py.
foo.py
from mymod1 import bar
from mymod2 import hoge
bar("Hello!")
hoge("Hoge!")
mymod1.py
def bar(s):
print(f"bar: {s}")
mymod2.py
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))
Install the module by running the following on your terminal:
pipenv install setuptools pykakasi pyinstaller
The files that exist at this stage are as follows.
$ ls
Pipfile
foo.py
mymod1.py
mymod2.py
The generated Pipfile is as follows.
[[source]]
name = "pypi"
url = "https://pypi.org/simple"
verify_ssl = true
[dev-packages]
[packages]
setuptools = "*"
pykakasi = "*"
pyinstaller = "*"
[requires]
python_version = "3.7"
Execute the following on the terminal to create an executable file.
pipenv run pyinstaller foo.py
If you check the file with ls, the build directory and the dist directory are generated.
$ ls
Pipfile
build
dist
foo.py
foo.spec
mymod1.py
mymod2.py
Since "dist / foo / foo" is the executable file, when I tried to move it, "KeyError:'max_key_len'" occurred as shown below.
$ dist/foo/foo
bar: Hello!
Traceback (most recent call last):
File "foo.py", line 5, in <module>
hoge("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
The cause was that the dictionary of pykakashi under dist was insufficient. Under dist, there is one dictionary as shown below.
$ ls dist/foo/pykakasi/data/
itaijidict3.db
On the other hand, there were 8 dictionaries in the installation destination by pipenv.
$ 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
Copy this under dist by overwriting.
$ 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
When I ran the binary again, it worked fine as below.
$ dist/foo/foo
bar: Hello!
hoge!
I found a better way, so I'll add it. The following additional part was executed on Windows.
Reference page (Thank you) -Embed resources in executable file with PyInstaller
The first executable file generation also generated a foo.spec file. Edit this file as follows to add all the pykakasi dictionary files to a.datas. The location of the pykakasi dictionary file depends on the environment, so it needs to be rewritten (in my environment it is the location down from the C drive).
I've also modified hiddenimports, but see this article (https://qiita.com/kanedaq/items/e65507878c52ad67d002).
foo.spec
# -*- 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')
Use the rewritten spec file to generate the executable file again.
pipenv run pyinstaller foo.spec
When I ran the generated binary, it worked fine as below.
C:\Users\username\PycharmProjects\foo_project>dist\foo\foo
bar: Hello!
hoge!
Under the dist directory where the binary is located, a subdirectory for pykakasi is created as shown below, and you can see that the dictionary is copied.
C:\Users\username\PycharmProjects\foo_project>dir .\dist\foo\pykakasi\data
C:\Users\username\PycharmProjects\foo_project\dist\foo\pykakasi\directory of 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
That's all for the postscript.