PythonによるWeb APIをEXE化する


はじめに

ときどき以下のすべてを満たしたいというご要望をいただくことがあります。

  • Pythonで作ったAIをWindowsアプリに組み込みたい
  • AIのインターフェースはWeb APIにしたい
  • リモートサーバと通信したくない
  • 運用端末にはPythonをインストールしたくない

色々やり方はあると思いますが、

  1. AIの機能をPythonのWebフレームワークでWeb APIとして公開できるようにし、
  2. そのPythonスクリプトをEXEファイルに変換してアプリ本体に同梱し、
  3. アプリ本体からEXEを実行すればこれを実現できます。

今回は以下を使います。

  • OS:Windows 10
  • Python:3.7.7 (開発環境にのみインストール)
  • Webフレームワーク:Flask
  • EXE化:PyInstaller

(ちなみにFastAPI × uvicornだと、2020/3/30時点では問題がありました。 https://github.com/pyinstaller/pyinstaller/pull/4664

手順

FlaskとPyInstallerをインストールします。

コマンドプロンプトなどで、

pip install flask pyinstaller

Web APIを公開するPythonファイルを作ります。

main.py
from flask import Flask
app = Flask(__name__)

@app.route('/predict')
def predict():
  """
  AIによる予測を返すAPIのつもり
  """
  return {'result': 'Prediction by AI.'}

if __name__ == '__main__':
  app.run()

上記ファイルのあるディレクトリに移動し、

pyinstaller main.py --onefile

dist ディレクトリに main.exe ができているので、試しに実行してみます。

dist\main.exe
Traceback (most recent call last):
  File "site-packages\PyInstaller\loader\rthooks\pyi_rth_pkgres.py", line 13, in <module>
  File "c:\users\user\appdata\local\programs\python\python37\lib\site-packages\PyInstaller\loader\pyimod03_importers.py", line 623, in exec_module
    exec(bytecode, module.__dict__)
  File "site-packages\pkg_resources\__init__.py", line 86, in <module>
ModuleNotFoundError: No module named 'pkg_resources.py2_warn'
[24508] Failed to execute script pyi_rth_pkgres

上のように、 ModuleNotFoundError になる場合は、main.py と同階層に生成された main.spec ファイルをテキストエディタで編集します。
具体的には、 hiddenimports に見つからなかったモジュール名を含めます。

main.spec
             # -- 略 --
             hiddenimports=[],
             # -- 略 --

↓次のように修正して保存。

main.spec
             # -- 略 --
             hiddenimports=['pkg_resources.py2_warn'],
             # -- 略 --

pyファイルではなく、修正したspecファイルを指定して、pyinstallerを実行します。

pyinstaller main.spec --onefile

再度動作確認します。

dist\main.exe

アクセスしてみると、Web APIが公開されていることをざっくり確認できます。

実際はC#などのアプリからこのEXEを実行し(要プロセス管理)、おなじくアプリからHttpクライアントでアクセスすることになります。