Avoid run-time error "KeyError:'_max_key_len_'" for binaries generated by PyInstaller for Python code using pykakasi

Article content

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.

Sample code

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))

Steps to get a run-time error

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

Avoiding run-time errors

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!

(2020-02-27) Addendum

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.

Recommended Posts

Avoid run-time error "KeyError:'_max_key_len_'" for binaries generated by PyInstaller for Python code using pykakasi
Avoid run-time error ModuleNotFoundError for executables generated from Python code using Pyinstaller
[Python] for statement error