Flaskファイルをpyinstallerを使ってExe化してみよう

  • 2020.12.17
  • 2021.01.24
  • Flask
NO IMAGE

(前準備)簡単なFlaskファイルを作成


Exe化を作る前に簡単なFlaskファイルを作っておきます。

ファイル構成はflaskというフォルダに、main.pyだけを持っているファイルです。

flask
  └── main.py


main.pyのファイルの中身は、アクセスしたら文字列を返すだけのものです。

from flask import Flask

app = Flask(name)

@app.route('/', methods=['GET'])
def index():
    result = 'index'
    return result


@app.route('/a', methods = ['GET'])
def aaa():
    result = 'Hello World'
    return result


if name == 'main':
    app.run(debug=True, port=5000, threaded=True)


念のため動作確認しておきます。(コマンド操作でflaskフォルダがあるディレクトリまで移動が必要です)

$ python main.py


無事動きだしました。
localhost:5000のURLにもアクセスしてみましょう。


これで前準備終了です。
ターミナルで、control + c でpythonの起動を終わります。

いよいよExe化していきましょう。

pyinstallerでFlaskファイルをexe化


pipを使ってインストールしましょう。

$ pip install pyinstaller


あとはExe化したいディレクトリまで移動して、下記をたたきます。

$ pyinstaller main.py --onefile


成功すると最後の行に successfully と表示され

14635 INFO: Building EXE from EXE-00.toc completed successfully.


ファイル構成に新しくdistbuildというフォルダができました

flask
  ├── build
  ├── dist
      └── main(exe)
  ├── main.py
  └── main.spec


Exe化されたファイルはflask/dist/main(exe)の場所に出来上がりました。

flask/dist/main(exe) をダブルクリック、もしくはターミナルから実行してみましょう。

$ ./dist/main


python main.py のコマンドをたたいた時と同じ効果が得られます。

先ほど同じくlocalhost:5000のURLにもアクセスできればOKです。

pyファイルが複数ある場合のpyinstallerは?


先ほどまではmain.pyしかありませんでしたが、pyファイルがひとつとは限りません。

例えばですが以下のようにpyファイルが複数あったとしても

flask
  ├── hogehoge.py
  ├── piyopiyo.py
  └── main.py


先ほどと同じpyinstallerコマンドで問題ありません

$ pyinstaller main.py --onefile


他のファイルをmain.pyでimportしていたらpyinstallerが読み取ってくれますので、通常通り動きます。
階層等があっても問題ないと思われます。

config.ini等の設定ファイルがある場合


config.iniファイル等がちょっと厄介で、結構時間を取られました。。


ファイル構成は以下を想定。

flask
  ├── congi.ini
  └── main.py


config.iniファイルを用意します。

[DEFAULT]
 HOGE_HOGE=hoge_hoge
 PIYO_PIYO=piyo_piyo


main.pyでconfig.iniを読み取る通常バージョン

import configparser
from flask import Flask

CONFIG_PATH = './config.ini'
CONFIG_PARSER = configparser.ConfigParser()
CONFIG_PARSER.read(CONFIG_PATH)
CONFIG = CONFIG_PARSER['DEFAULT']

HOGE = CONFIG['HOGE_HOGE']
PIYO = CONFIG['PIYO_PIYO']

app = Flask(name)

@app.route('/', methods=['GET'])
def index():
    result = HOGE
    return result


@app.route('/a', methods = ['GET'])
def aaa():
    result = PIYO
    return result


if name == 'main':
    app.run(debug=True, port=5000, threaded=True)


ただ、今回はExe化するためファイルパスが変わります
のでファイルパスを取得する部分を書き換えます

main.pyでconfig.iniを読み取るExe化バージョン

import os # 追加
import sys # 追加
import configparser
from flask import Flask

dpath = os.path.dirname(sys.argv[0]) # 追加

# CONFIG_PATH = './config.ini' # 削除
CONFIG_PATH = dpath + '/config.ini' # 追加
CONFIG_PARSER = configparser.ConfigParser()
CONFIG_PARSER.read(CONFIG_PATH)
CONFIG = CONFIG_PARSER['DEFAULT']

HOGE = CONFIG['HOGE_HOGE']
PIYO = CONFIG['PIYO_PIYO']

app = Flask(name)

@app.route('/', methods=['GET'])
def index():
    result = HOGE
    return result


@app.route('/a', methods = ['GET'])
def aaa():
    result = PIYO
    return result


if name == 'main':
    app.run(debug=True, port=5000, threaded=True)


ファイルパスの取得方法を変えてあげたら、pyinstallerコマンドをたたきましょう。

$ pyinstaller main.py --onefile


successfully と表示されたら、Exe化ができたのと同じ階層に config.iniファイルをコピペしましょう。

flask
   ├── config.ini
   ├── build
   ├── dist
       ├── config.ini
       └── main(exe)
   ├── main.py
   └── main.spec


flask/dist/main(exe)ファイルを実行して、問題なく動けばOKです。

config.iniだけでなくて、ファイルパスを扱う場合は

dpath = os.path.dirname(sys.argv[0])

CONFIG_PATH = dpath + '/config.ini'


このような形を取って、exeファイルと同じ階層に置いておけば問題ないと思います。


あとはdistフォルダを別の場所に移動しても、フォルダ名を変えても
どこでもmain(exe)を実行が可能な状態になっているかと思います。

.spec内を編集してファイルを移動する方法


上記ではコピペでconfig.iniファイルを移動しましたが、
specファイルを利用してファイルを移動することもできます。

pyinstallerコマンドをたたくと、xxx.spec というファイルができると思います。
今回は pyinstaller main.py –onefile をしたので、main.specというファイルができました。

このmain.specの最後の行にこちらを追加します。

import shutil
shutil.copyfile('config.ini', '{0}/config.ini'.format(DISTPATH))

※ 「config.ini」ファイル用の追加行です。ファイル名が違う場合は名前を編集してください。
shutil.copyfile(‘hogehoge….) は、複数行書いてもOKです。

ファイルを編集したら、保存して、main.spacでpyinstallerをたたきます。

$ pyinstaller main.spac --onefile --clean

これで無事successしたら、移動したいファイルがexeと同じディレクトリに移動したかと思います。

そんなに移動するファイルが多くないのであれば、コピペでもいいとは思っています。。

iniファイルや他のファイルも一緒にpyinstallerするやり方


上記のやり方では、iniファイルをmain(exe)と同じ階層に置いておく必要がありました。


設定ファイルなのでそれでもいいとは思いますが、
main(exe)だけの方がシンプルで良い場合もあると思います。

他のファイルも一緒にpyinstallerするやり方です。
config.iniファイルも一緒にやる場合は、

$ pyinstaller -F --add-data "config.ini:." --onefile --clean main.py

そして、ファイルの場所が変わるので、main.pyを編集する必要があります。

import sys # 追加
import configparser
from flask import Flask

ROUTE_PATH = sys.path[1] if 2 == len(sys.path) else '.' # 追加
CONFIG_PATH = ROUTE_PATH + '/config.ini' # 編集
CONFIG_PARSER = configparser.ConfigParser()
CONFIG_PARSER.read(CONFIG_PATH)
CONFIG = CONFIG_PARSER['DEFAULT']

HOGE = CONFIG['HOGE_HOGE']
PIYO = CONFIG['PIYO_PIYO']

app = Flask(name)

@app.route('/', methods=['GET'])
def index():
    result = HOGE
    return result


@app.route('/a', methods = ['GET'])
def aaa():
    result = PIYO
    return result


if name == 'main':
    app.run(debug=True, port=5000, threaded=True)


sys.path[1]にてexeが実行されている場所を取得する必要が出てくるための編集です。

他にディレクトリの追加や複数の追加を行いたい場合などは,

$ pyinstaller -F --add-data "templates:templates" --onefile --clean main.py
pyinstaller -F --add-data "templates:templates" --add-data "build:build" --add-data "config.ini:." --onefile --clean main.py

ディレクトリの指定や複数の指定なども可能です。

xxx.specを使用してpyinstallerをする方法


今回でいえば、main.specになります。
xxx.specはxxx.pyファイルをpyinstallerをすると、xxx.pyファイルと同じ階層に作られます。

$ pyinstaller -F --onefile --clean main.py


–add-dataでもできますが、ファイル数が多いとなると大変です。
ファイル構成はこんなイメージでやってみます。

flask
  ├── build
      ├── index.html
      └── static
          ├── css
          └── js
  ├── config.ini
  └── main.py


そんな時にmain.specを使用します。

-- mode: python ; coding: utf-8 --
 block_cipher = None
 a = Analysis(['main.py'],
              pathex=['/Users/xxx/workspace/flask'],
              binaries=[],
              datas=[('build', 'build'), ('build/static', 'build/static'), ('config.ini', '.')], # 編集行
              hiddenimports=[],
              hookspath=[],
              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='main',
           debug=False,
           bootloader_ignore_signals=False,
           strip=False,
           upx=True,
           upx_exclude=[],
           runtime_tmpdir=None,
           console=True )


datas のリストに追加をして、再度pyinstallerをspecファイルで実行しましょう。

$ pyinstaller -F --onefile --clean main.spec

これで、exeファイルだけでも実行が可能になるかと思います。

ファイルを含めた場合の注意点


ファイルも一緒にpyinstallerすることもできますが、pathが変わってくるので
ファイルの読み込み場所を直しておく必要がありますのでご注意を。

取得の仕方はsysを使えば可能です。
例でindexhtmlをrender_templateする main.pyを書いてみましたので参考になれば幸いです。

import sys
from flask import Flask, render_template
from flask_cors import CORS

ROUTE_PATH = sys.path[1] if 2 == len(sys.path) else '.'
TEMPLATES_PATH = ROUTE_PATH + '/templates'

app = Flask(name, template_folder=TEMPLATES_PATH)
app.config['JSON_AS_ASCII'] = False
CORS(app)


@app.route('/')
def index():
    return render_template('index.html')


if name == 'main':
    app.run(port=5000, threaded=True)


以上、Flaskファイルをpyinstallerを使ってExe化してみようでした。

Flaskカテゴリの最新記事