0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

gradioを使用したPythonアプリをpyinstallerで実行ファイル化する手順

Posted at

gradio

gradioというPythonモジュールがあります。

これを利用すると、AI Webアプリを簡単に実装でき、WebブラウザをフロントエンドにするローカルAIアプリも容易に実装できます。実際、Stable Diffusion Web UI AUTOMATIC1111などさまざまなAIアプリで活用されています。

AUTOMATIC1111の例

gradioを利用する場合、Webアプリなら開発者がHugging Face spaces等のサイトでアプリを構築すればユーザは簡単に利用できます。

ですが、Pythonアプリを配布して、ローカルでPythonアプリを実行させたい場合、ユーザーにPython本体をインストールさせた上でアプリを実行させる必要があります。コマンドラインに慣れている多くのLinuxユーザは別ですが、一般的なWindowsユーザやmacOSユーザにとっては使いやすくはないでしょう。

Pythonにはスクリプトを実行ファイルにするアプリやモジュールがいくつかあります。その一つPyinstallerを使うと1ファイルの実行ファイルにまとめることができます。

ただ、gradioを利用したPythonスクリプトは単にコマンドを実行するだけでは実行ファイル化できなかったので、こちらこちらを参考に、1ファイル化する具体的な手順を解説します。また、macOSではPyinstaller(またはgradio)にバグがあり、うまく動かない場合があるので、代替手法も説明します。

Pyinstallerでの1ファイル化

サンプルアプリ作成

Python公式サイトのなどを参照し、Pythonをインストールして実行できるようにしておいてください。

適当な作業用フォルダに移動しvenvで仮想環境を構築します(pythonではなくpython3の場合もあります)。

python -m venv venv

仮想環境を有効にします。

Windowsの場合

.\venv\Scripts\Activate.ps1  

macOSやLinuxの場合

. venv/bin/activate

gradioをpipでインストールします(pipではなくpip3の場合もあります)。

pip install gradio

gradioのサイトのトップの例を自動でブラウザが立ち上がるよう修正したスクリプトを作成します。次の内容のtest.pyを適当なエディタで作成してください。

import gradio as gr

def greet(name):
    return "Hello " + name + "!"

demo = gr.Interface(fn=greet, inputs="text", outputs="text")
demo.launch(inbrowser=True)   

動作確認します。

python test.py

自動的にブラウザが起動します。

tmp1.png

name欄に名前を入れて送信ボタンを押せば、outputにメッセージが表示されます。

tmp2.png

一旦終了してください。

Pyinstallerによる実行ファイル化

Pyinstallerをpipでインストールします。

pip install pyinstaller

pyinstallerで1ファイルとして実行ファイル化してみます。

pyinstaller --onefile test.py

distフォルダ以下にtest.exe(Windowsの場合)またはtest(macOSやLinux場合)が生成されるので、一見正しく動作しているように見えるのですが、実行してみるとうまく動きません。

Windowsの場合

dist\test.exe
...
  File "gradio_client\serializing.py", line 16, in <module>
FileNotFoundError: [Errno 2] No such file or directory: 'C:\\Users\\asfdwe\\AppData\\Local\\Temp\\_MEI127082\\gradio_client\\types.json'
[PYI-5196:ERROR] Failed to execute script 'test' due to unhandled exception!

macOSの場合

dist/test
...
  File "gradio_client/serializing.py", line 16, in <module>
FileNotFoundError: [Errno 2] No such file or directory: '/var/folders/lc/q4l6040s4n709pxrsg3jx7d40000gp/T/_MEI0cSdr3/gradio_client/types.json'
[PYI-14006:ERROR] Failed to execute script 'test' due to unhandled exception!

Linuxの場合

dist/test
...
  File "gradio_client/serializing.py", line 16, in <module>
FileNotFoundError: [Errno 2] No such file or directory: '/tmp/_MEIrMnl9c/gradio_client/types.json'
[PYI-358283:ERROR] Failed to execute script 'test' due to unhandled exception!

エラーの原因はgradio_clientモジュールのファイル不足のようです。

--collect-datagradio_clientを指定します。もう一度pyinstallerを実行します。

pyinstaller --onefile --collect-data gradio_client test.py
dist\test.exe
...
  File "safehttpx\__init__.py", line 12, in get_version
FileNotFoundError: [Errno 2] No such file or directory: 'C:\\Users\\asfdrwe\\AppData\\Local\\Temp\\_MEI74442\\safehttpx\\version.txt'
[PYI-3580:ERROR] Failed to execute script 'test' due to unhandled exception!

safehttpxモジュールのファイルが足らないので追加してもう一度pyinstallerを実行します。

pyinstaller --onefile --collect-data gradio_client --collect-data safehttpx test.py
dist\test.exe
...
  File "groovy\__init__.py", line 5, in <module>
  File "pathlib.py", line 1134, in read_text
  File "pathlib.py", line 1119, in open
FileNotFoundError: [Errno 2] No such file or directory: 'C:\\Users\\asfdrwe\\AppData\\Local\\Temp\\_MEI146082\\groovy\\version.txt'
[PYI-6776:ERROR] Failed to execute script 'test' due to unhandled exception!

groovyモジュールを追加します。

pyinstaller --onefile --collect-data gradio_client --collect-data safehttpx --collect-data groovy  test.py
dist\test.exe
...
  File "gradio\component_meta.py", line 111, in create_or_modify_pyi
  File "pathlib.py", line 1134, in read_text
  File "pathlib.py", line 1119, in open
FileNotFoundError: [Errno 2] No such file or directory: 'C:\\Users\\asfdrwe\\AppData\\Local\\Temp\\_MEI81842\\gradio\\blocks_events.pyc'
[PYI-3820:ERROR] Failed to execute script 'test' due to unhandled exception!

まだダメです。gradioモジュールを追加します。

pyinstaller --onefile --collect-data gradio_client --collect-data safehttpx --collect-data groovy --collect-data gradio test.py
dist\test.exe
...
  File "gradio\blocks_events.py", line 20, in __new__
  File "gradio\component_meta.py", line 111, in create_or_modify_pyi
  File "pathlib.py", line 1134, in read_text
  File "pathlib.py", line 1119, in open
FileNotFoundError: [Errno 2] No such file or directory: 'C:\\Users\\asfdrwe\\AppData\\Local\\Temp\\_MEI38162\\gradio\\blocks_events.pyc'
[PYI-11504:ERROR] Failed to execute script 'test' due to unhandled exception!

blocks_events.pycの扱いが問題のようです。specファイルを編集してgradio内のファイルの扱いを変更します。

pyinstallerはpythonスクリプトを解析し、specファイルを出力し、specファイル内容に基づいて実行ファイル化します。pyi-makespecコマンドで一旦specファイルを出力させたあと、specファイルを修正した上で、specファイルに基づいて実行ファイル化するようにします。

pyi-makespecコマンドをpyinstallerを同じ引数で実行します。

pyi-makespec --onefile --collect-data gradio_client --collect-data safehttpx --collect-data groovy --collect-data gradio test.py

生成されたtest.specをテキストエディタで開いてください。

# -*- mode: python ; coding: utf-8 -*-
from PyInstaller.utils.hooks import collect_data_files

datas = []
datas += collect_data_files('gradio_client')
datas += collect_data_files('safehttpx')
datas += collect_data_files('groovy')
datas += collect_data_files('gradio')


a = Analysis(
    ['test.py'],
    pathex=[],
    binaries=[],
    datas=datas,
    hiddenimports=[],
    hookspath=[],
    hooksconfig={},
    runtime_hooks=[],
    excludes=[],
    noarchive=False,
    optimize=0,
)
pyz = PYZ(a.pure)

exe = EXE(
    pyz,
    a.scripts,
    a.binaries,
    a.datas,
    [],
    name='test',
    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,
)

上の方に--collection-dataで追加した内容が書かれています。gradioに対するmodule_collection_modeを変更するために、中央付近のoptimize=0あたりを次のように修正して保存します。

# -*- mode: python ; coding: utf-8 -*-
from PyInstaller.utils.hooks import collect_data_files

datas = []
datas += collect_data_files('gradio_client')
datas += collect_data_files('safehttpx')
datas += collect_data_files('groovy')
datas += collect_data_files('gradio')


a = Analysis(
    ['test.py'],
    pathex=[],
    binaries=[],
    datas=datas,
    hiddenimports=[],
    hookspath=[],
    hooksconfig={},
    runtime_hooks=[],
    excludes=[],
    noarchive=False,
    optimize=0,
    module_collection_mode={
        'gradio': 'py',  # Collect gradio package as source .py files
    },
)
pyz = PYZ(a.pure)

exe = EXE(
    pyz,
    a.scripts,
    a.binaries,
    a.datas,
    [],
    name='test',
    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,
)

pyinstallerをspecファイルに対して実行します。

pyinstaller test.spec

これでWindowsやLinuxでは正常に実行できるようになります。

Windowsの場合

dist\test.exe

test.exeファイルをどこかに移動させて、エクスプローラーでダブルクリックしても自動的にターミナルとブラウザが起動して正常に動作するはずです。

Linuxの場合

dist/test

同様にうまく動くはずです。

macOSの場合

dist/test

とすると、python3.9(コマンドライン版xcodeでインストールされるものやbrew python@3.9でインストールされるもの両方)は正常に動くはずです。

python3.10~3.13の場合、

* Running on local URL:  http://127.0.0.1:7860
* To create a public link, set `share=True` in `launch()`.
* Running on local URL:  http://127.0.0.1:7863
* To create a public link, set `share=True` in `launch()`.
* Running on local URL:  http://127.0.0.1:7864
* To create a public link, set `share=True` in `launch()`.
* Running on local URL:  http://127.0.0.1:7865
* To create a public link, set `share=True` in `launch()`.
* Running on local URL:  http://127.0.0.1:7866
* To create a public link, set `share=True` in `launch()`.
* Running on local URL:  http://127.0.0.1:7867
* To create a public link, set `share=True` in `launch()`.
* Running on local URL:  http://127.0.0.1:7868
...

と、ブラウザを何度の起動するバグにぶつかります。

python3.9で問題ないならばpython3.9で実行ファイル化してください。

macOSでのPyinstaller以外の手段

他にもやり方はあるのかもしれませんが、python実行環境をどのフォルダに移動させても実行できるポータブルアプリ化した上でアプリに組み込み、その実行環境を使用するようにしたcommandファイルを作成すれば、ユーザにpythonを意識させずにpythonアプリを利用させることができます。

通常のmacOS用のPython実行環境はそのままでは移動できないので、この記事が参考にpython 3.12.10をrelocateable-pythonツールを使ってポータブル化してみましょう。

今までのPython仮想環境に入っている場合抜けてください。

deactivate

test.pyのあるフォルダで作業を続けます。gitでrelocatable-pythonをインストールします。

git clone https://github.com/gregneagle/relocatable-python
cd relocatable-python
./make_relocatable_python_framework.py --python-version 3.12.10 --os-version 11 --upgrade-pip

Python.frameworkフォルダがポータブル実行環境です。このフォルダはどこに移動させても動作します。python3pip3などの実行ファイルはPython.framework/Versions/Current/bin以下に存在します。

test.pyのあるフォルダに戻り、Python.frameworkフォルダ移動させます。

mv Python.framework ..
cd ..

relocatable-pythonはもういらないので削除します。

rm -rf relocatable-python

ポータブルPython環境にパスを通します。

export PATH=Python.framework/Versions/Current/bin:$PATH
hash -r

gradioモジュールをインストールしてtest.pyを動かせるようにします。

pip3 install gradio

test.pyがこの環境で動くか確認します。

python3 test.py

次の内容でtest.commandを作成してください。

#!/bin/sh
cd "$(dirname "$0")"
exec ./Python.framework/Versions/Current/bin/python3 test.py

test.commandに実行属性をつけます。

chmod +x test.command

こうすれば、test.pyのあるフォルダをどこに移動させてもFinderでtest.commandをダブルクリックすることで問題なく実行することができます。

あとは、この記事などを参考に、一般的なmacOSアプリ形式にすれば、gradioを使用したmacOSアプリを一般ユーザでも扱いやすい形で配布できると思います。

0
1
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
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?