Pythonから実行ファイルの生成 ~pyinstaller

2022/08/08に公開

インストール

pip install pyinstaller

インストールすると(環境によるが)Scriptsフォルダの下にpyinstaller.exeが生成されていることがわかります。
※直接インストールしたなら...AppData\Local\Programs\Python\Python〇〇\Scripts
※venvなら venvフォルダの直下

実行

yourscript.pyを作成してみよう

yourscript.py
if __name__ == '__main__':
    print('こんにちは')
実行コマンド
pyinstaller yourscript.py

実行すると下記のファイルが生成されると思います。

フォルダ名
build 中間ファイルが保存されている。
dist 配布用

distのフォルダの中にあるEXEファイルをコマンドプロンプトで実行してみてください。
(カレントディレクトリで実行する必要があります。)

実行コマンド
yourscript

onefileオプション

私の環境では、10Mb程度容量があり、ファイルがいっぱいいっぱいありますね。まとめてみましょう。
※効果を確認するためdistフォルダはあらかじめ空にしておきましょう。

pyinstaller yourscript.py --onefile

ではDistフォルダの中身を確認してみましょう。
exeファイルが一つだけになっていますね。

specファイルの編集が必要なパターン

pipコマンドでライブラリを取得
pip install Pillow
pip install pyzbar

コードを書きましょう。

yourscript.py
from pyzbar.pyzbar import decode
from PIL import Image
import os

if __name__ == "__main__":
    cwd = os.getcwd()  # カレントディレクトリ
    im = Image.open(cwd + '\\test.png')
    print(im.format, im.size, im.mode)

このコード画像を読み込むだけで、pyzbarのライブラリをインポートしていますが使ってはいません。

このファイルをpyinstallerで実行するとエラーが発生します。
https://github.com/NaturalHistoryMuseum/pyzbar/issues/27
DLLの読み込みに失敗しているんですね。

実行するとspecファイルが生成されていたと思います。

さてspecファイルは下記のようになっていると思います。

yourscript.spec
# -*- mode: python ; coding: utf-8 -*-

block_cipher = None

a = Analysis(
    ['yourscript.py'],
    pathex=[],
    binaries=[],
    datas=[],
    hiddenimports=[],
    hookspath=[],
    hooksconfig={},
    runtime_hooks=[],
    excludes=[],
    win_no_prefer_redirects=False,
    win_private_assemblies=False,
    cipher=block_cipher,
    noarchive=False,
)
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)

exe = EXE(
    pyz,
    a.scripts,
    a.binaries,
    a.zipfiles,
    a.datas,
    [],
    name='yourscript',
    debug=False,
    bootloader_ignore_signals=False,
    strip=False,
    upx=True,
    upx_exclude=[],
    runtime_tmpdir=None,
    console=True,
    disable_windowed_traceback=False,
    argv_emulation=False,
    target_arch=None,
    codesign_identity=None,
    entitlements_file=None,
)

これを下記のように書き換えてみましょう。

yourscript.spec
# -*- mode: python ; coding: utf-8 -*-

from pathlib import Path

from pylibdmtx import pylibdmtx
from pyzbar import pyzbar

block_cipher = None


a = Analysis(
    ['yourscript.py'],
    pathex=[],
    binaries=[],
    datas=[],
    hiddenimports=[],
    hookspath=[],
    hooksconfig={},
    runtime_hooks=[],
    excludes=[],
    win_no_prefer_redirects=False,
    win_private_assemblies=False,
    cipher=block_cipher,
    noarchive=False,
)

a.binaries += TOC([
    (Path(dep._name).name, dep._name, 'BINARY')
    for dep in pylibdmtx.EXTERNAL_DEPENDENCIES + pyzbar.EXTERNAL_DEPENDENCIES
])

pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)

exe = EXE(
    pyz,
    a.scripts,
    a.binaries,
    a.zipfiles,
    a.datas,
    [],
    name='yourscript',
    debug=False,
    bootloader_ignore_signals=False,
    strip=False,
    upx=True,
    upx_exclude=[],
    runtime_tmpdir=None,
    console=True,
    disable_windowed_traceback=False,
    argv_emulation=False,
    target_arch=None,
    codesign_identity=None,
    entitlements_file=None,
)

上記では下記の二項目を追加しています。

from pathlib import Path

from pylibdmtx import pylibdmtx
from pyzbar import pyzbar
a.binaries += TOC([
    (Path(dep._name).name, dep._name, 'BINARY')
    for dep in pylibdmtx.EXTERNAL_DEPENDENCIES + pyzbar.EXTERNAL_DEPENDENCIES
])

pipコマンドも実行しておきましょう。

pip install pylibdmtx

指定ファイルはpyファイルではなく、specファイルであることに注意してください。

pyinstaller yourscript.spec

実行前に、dist直下に、(適当に用意した)test.pngファイルを設置してみましょう。
コマンドプロンプトを開き、dist直下のEXEファイルを実行してみましょう。

実行結果
PNG (189, 179) RGB

※用意した画像によって出力される数値の結果は異なります。

Discussion