見出し画像

pythonコードを実行ファイルexeにする pyinstallerのspec定義サンプル

pythonアプリケーションをプログラミングに馴染みのない一般の人が利用する場合、pythonインタープリタの環境セットアップがハードルになるため、
●サーバー経由のネットサービスでアプリ利用する
●実行ファイル(.exe)に変換されたアプリを使う
など、いくつかの利用形態があります。

pythonアプリを実行ファイル(.exe)に変換できれば、Windowsパソコンで手軽に利用できます。
ただ、pythonインタープリタをexeに内蔵しているようなものなので実行に必要なライブラリを含めるとファイルサイズが大きくなる点が要注意です。
※私が作ったpythonアプリのexe関連ファイルサイズは約800MBでした。
※下記URLに関連する記事があります。
グローバル・サプライチェーンを俯瞰する計画対話環境のご紹介(デモ版あり)|Yasushi Ohsugi

pythonソース・コードから実行ファイル(.exe)を生成する方法はいくつかありますが、ここでは「pyinstallerでspecファイルを使った場合」の実行ファイル生成方法を備忘録として記事にします。
chat GPTと丸1日やり取りして、specファイルの修正を繰り返し、pythonコードを実行ファイルexeに変換することができました。

specファイル定義のポイントとしては、以下の点が挙げられます。
●sys.path.append(win32com_dir)の定義は、前処理のpython codeです。
●binaries =……..  の定義部分は、google検索で見つけたhint and tipsです。
●excludes=['PyQt5'],  の定義は、default設定のPyQtを外しています。

【前提環境】Windows11 / anaconda / python / tkinter / matplotlib
pythonの環境は、windows11のanaconda環境を想定しています。
また、GUI環境は、実行ファイルがwindowsのstand aloneで動くことを念頭にtkinter と matplotlibのライブラリを使用しています。
※plotlyのグラフィック対話機能は魅力的ですが、今回はmatplotlibを使っています。

(1)  pyinstallerの導入
conda install -c conda-forge pyinstaller
またはpip install pyinstaller
※サーバーの問題なのかinstallに何回か失敗することがあります。

(2) specファイル (下記のコード参照) の修正
● まず、pythonソースコードが正常処理することを確認しておきます。
● pythonコードを置いてあるディレクトリとファイル名に修正する。
pythonコードが入ったディレクトリ :  C:\<directory_path>
python ソースコードのファイル名 : '<python_file_name>.py'
をspecファイルに定義する。

● specファイルの<user_name>を該当のwindows環境のユーザー名に変更する。

(3) pyinstallerの起動
修正したspecファイルを変換対象となるpythonコードと同じディレクトリ下に置いて、pyinstallerを起動する。
C:\<directory_path>\ pyinstaller  python2exe_sample_update.spec

#pyinstaller python2exe_sample.spec


import sys
import os
import win32com
import pythoncom
import pywintypes

gen_py_folder = os.path.join(os.path.dirname(win32com.__file__), 'gen_py')

print("gen_py_folder",gen_py_folder)

if not os.path.exists(gen_py_folder):
    os.mkdir(gen_py_folder)

# win32com モジュールのディレクトリを取得
win32com_dir = os.path.dirname(win32com.__file__)

print("win32com_dir",win32com_dir)

# sys.path に追加
sys.path.append(win32com_dir)


import win32com.client

# gen_pyフォルダを初期化するためのCOMオブジェクトのインスタンス化
shell = win32com.client.Dispatch("WScript.Shell")


from PyInstaller.utils.hooks import collect_submodules, copy_metadata

hiddenimports = collect_submodules('win32com') + ['pythoncom', 'pywintypes', 'win32com.gen_py', 'pywin32_system32']

# user_nameを<user_name>に指定
datas = copy_metadata('pywin32') + [(r'C:\Users\<user_name>\anaconda3\lib\site-packages\win32com\gen_py', 'win32com/gen_py')]


# user_nameを<user_name>に指定
binaries = [(r'C:\Users\<user_name>\anaconda3\lib\site-packages\pywin32_system32\pythoncom39.dll', '.')]


a = Analysis(['<python_file_name>.py'],         # ターゲットpython file name指
             pathex=[r'C:\<directory_path>'],   # python fileのdirectory指定

             binaries=binaries,
             datas=datas,
             hiddenimports=hiddenimports,

             # user_nameを<user_name>に指定
             hookspath=[r'C:\Users\<user_name>\hooks'],

             # user_nameを<user_name>に指定
             runtime_hooks=[r'C:\Users\<user_name>\hooks\pyi_rth_win32comgenpy.py'],
             excludes=['PyQt5'],
             win_no_prefer_redirects=False,
             win_private_assemblies=False,
             noarchive=False)

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

exe = EXE(pyz,
          a.scripts,
          [],
          exclude_binaries=True,

          name='<python_file_name>',         # ターゲットpython file name指定

          debug=False,
          bootloader_ignore_signals=False,
          strip=False,
          upx=True,
          upx_exclude=[],
          runtime_tmpdir=None,
          console=True)

coll = COLLECT(exe,
               a.binaries,
               a.zipfiles,
               a.datas,
               strip=False,
               upx=True,
               upx_exclude=[],

               name='<python_file_name>')    # ターゲットpython file name指定

(4) pyinstallerのexe生成処理
コンパイラと同様、exe生成には時間がかかります。
しばらく ( 私の場合は10~20分ほど ) 待つと、"dist"ディレクトリが作られて、その中に実行ファイル ".exe"が生成されます。
※途中、error messageのような"xxxxxx not found"が出ますが、スルーして最後まで行くと正常終了します。

(5)生成された".exe"を起動して動作を確認する。

【補足-1】pyinstallerの実行中にエラーが出た場合の対処方法
あくまで個人的な方法ですが、copilotのchat GPTに、以下のような問合せをして対処方法を教えてもらう、という事を エラーの都度、直ぐに問い合わせて、spec fileを修正していました。
「pyinstallerで以下のspec fileを使って、exe fileの生成処理中に、以下errorが発生しています。原因と対処方法を教えてください。
# spec fileをコピペ  #
# error massageをコピペ #」

【補足-2】pyinstallerの実行中にencode errorが発生した場合
pythonインタープリタでは出ないencode errorが、pyinstallerで出る場合があります。このような時には、pythonファイル形式のエラーチェック方法として、以下encoding_check.pyを流してpythonのファイル形式を確認する。

#encoding_check.py


import os
import chardet

# 検索するディレクトリを指定
target_dir = "C:\<directory_path>" # <directory_path>を修正する

# .py ファイルを取得
python_files = [os.path.join(root, file)
                for root, _, files in os.walk(target_dir)
                for file in files if file.endswith(".py")]

# 各ファイルのエンコーディングをチェック
for file in python_files:
    with open(file, "rb") as f:
        raw_data = f.read()
        encoding_detected = chardet.detect(raw_data)["encoding"]
    
    print(f"File: {file}, Encoding: {encoding_detected}")

以上、pythonコードから実行ファイルexeを生成する手順をご紹介しました。

本件、pyinstallerのマニュアルを読んでおらず、ネット上に散在する関連知識で、場当たり的に問題解決しておりますことを最後に付け加えさせていただきます。spec fileの一つ一つの定義の意味を正しく理解している訳ではないので冗長な部分や反則技を使っている可能性があります。

2025年1月
大杉 泰司

いいなと思ったら応援しよう!