LoginSignup
0
2

More than 3 years have passed since last update.

pykakasiを使用したPythonコードについて、PyInstallerで生成したバイナリの実行時エラー「KeyError: '_max_key_len_'」を回避する

Last updated at Posted at 2020-01-23

記事の内容

pykakasiを使用したPythonコードについて、PyInstallerで生成した実行ファイルを実行したら、エラー「KeyError: 'max_key_len'」が発生しました。
それを回避する方法を記録しておきます。
Linux(Ubuntu 18.04)、python 3.7.5で実行し、pipenvを使用しています。

※ (2020-02-27) エラー回避方法について、もっと良い方法がわかりましたので、末尾に追記しました。

サンプルコード

この記事のコードを改変して使用しました。以下の3ファイルを使用し、foo.pyをバイナリにしたものを起動することにします。

foo.py

from mymod1 import bar
from mymod2 import hoge

bar("Hello!")
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))

実行時エラーが起きるまでの手順

端末で以下を実行して、モジュールをインストールします。


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にも手を加えていますが、これについてはこちらの記事をご覧ください。

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

書き換えた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

追記は以上です。

0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2