embeddableバージョンのPythonを利用したPythonの自作アプリの配布について


概要

Windowsユーザーへ自作のPythonアプリを配布する際,pyinstallerによるバイナリ化が一般的であると考える.しかし,これが何らかの理由により,多くの場合で失敗するため,これの代わりとなる方法として,embeddableバージョンのPythonを利用する方法を記す.

考慮している点

配布されるユーザーは,パソコンに関して疎く,プログラミングに対する知識が全くないと仮定し,彼らがなるべく簡単な操作でこれをできるようにする.

環境

Windows 10 1903 64bitで確認済み

実際にこの配布方法を利用した自作プロジェクト

video_to_picture_everyseconds:開始時間と間隔の時間を設定すると,それに合わせて動画から静止画をキャプチャするプログラム(私の作ったプログラムの宣伝です)

方法

ディレクトリ構造は以下のように作成する.今回の例であるプロジェクトmy_projectでは,プロジェクト名もアプリ名もmy_projectとする.

tree
my_project/
├── install.bat
├── my_project.bat
├── powershell
│   ├── install.ps1
│   └── my_project.ps1
├── requirements.txt
└── src
    └── my_project.py

プログラム側とユーザー側がそれぞれやること

プログラム側がやること

Pythonのインストール

  1. python.orgからWindows x86-64 embeddable zip fileをダウンロードし,これを展開する.(なお,厳密には,圧縮ファイルのダウンロードと展開をするだけですが,Pythonをインストールするとの表現を用います.)
  2. 展開したPythonにpipをインストールする.
  3. pip install -r requirements.txtする.

該当の自作アプリmy_projectの起動

  1. インストールしたPythonから,該当の自作アプリmy_projectのPythonスクリプトmy_project.pyを起動する.

ユーザー側がやること

  • Pythonのインストール:プロジェクトフォルダmy_project/の中にあるinstall.batをクリックする.
  • 該当の自作アプリmy_projectの起動:プロジェクトフォルダmy_project/の中にあるmy_project.batをクリックする.
  • Pythonと該当の自作アプリmy_projectのアンインストール:my_project/を削除するだけでよい.

アプリmy_project内での操作を除けば,ユーザーは,クリック以外の操作は行わなくてよい.よって,ユーザーには,install.batmy_project.batのみを操作し,他のファイルやフォルダは決して触らないように指示すればよい.

それぞれのフォルダとスクリプトの詳細について

それぞれのフォルダについて

my_project/src/

このフォルダには,自作アプリmy_projectのPythonのスクリプトが入っている.この例では,メインとなるスクリプトをmy_project.pyとする.

my_project/powershell/

このフォルダには,Python本体のインストールやPythonのスクリプトmy_project/src/my_project.pyの実行に必要な,PowerShellのスクリプトmy_project/powershell/*.ps1が入っている.これらのPowerShellのスクリプトはバッチファイルmy_project/*.batを介して実行する.また,このことからPowerShellのスクリプト内のパスは,my_project/がカレントディレクトリとして考えられている.

なお,バッチファイルを介さず,PowerShellファイルを直接実行しても問題はないが,これのダブルクリックによる実行は,デフォルトでは不可能であるため,(特にパソコンに疎い)ユーザーにとって困難となりうる1.また,バッチファイルから直接Pythonを実行する場合,セキュリティの設定など,複雑な操作が困難であるため,バッチファイルからPowerShellを介した処理を行っている.

それぞれのスクリプトについて

インストールのためのスクリプト

この例では,Pythonのバージョンが3.6.8,amd64となっているが,必要に応じて,変更してもかまわない.変更する部分は,3.6.8amd64python36._pthと書いてある部分である.

まずは,PowerShellのスクリプトであるmy_project/powershell/install.ps1を示す.

my_project/powershell/install.ps1
# Download Python
if(!(Test-Path "python-3.6.8-embed-amd64.zip")){  
    wget "https://www.python.org/ftp/python/3.6.8/python-3.6.8-embed-amd64.zip" `
        -O "python-3.6.8-embed-amd64.zip"
}
## my_project/にPythonをダウンロードする.
## サーバーpython.orgに,不要な負荷をかけないため,すでにダウンロードされている場合は,ダウンロードしない.
Expand-Archive -Path python-3.6.8-embed-amd64.zip -DestinationPath python-3.6.8-embed-amd64 -Force  
## ダウンロードしたファイルを展開する.
## すでにある場合は,上書きする.

# Download get-pip
cd python-3.6.8-embed-amd64
## 展開したフォルダに入る.
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12;
## wgetを使うため,セキュリティの設定を変更する.
## このスクリプトが終了したら,変更されたセキュリティの設定はスクリプトの実行前の状態に戻る.
wget "https://bootstrap.pypa.io/get-pip.py" -O "get-pip.py"
## get-pip.pyをダウンロードする.
Add-Content -Path ./python36._pth -Value "import site" -Encoding UTF8
## get-pip.pyを実行するため,python36._pthにimport siteを追記する.
## 編集前のpython36._pthでは,これがコメントアウトされている.
./python get-pip.py
## get-pip.pyを実行する

# Install additional packages for my_project.py
cd ../
./python-3.6.8-embed-amd64/python -m pip install -r requirements.txt
## pip install -r requirements.txtを行う.

次に,バッチファイルのスクリプトであるmy_project/install.batをしめす.バッチファイルに日本語を表記すると,文字化け等の問題が生じる恐れがあるため,コメントアウトは英語で表記する.

my_project/install.bat
@echo off
@rem This makes the batch file not appear anything which is not needed for users.
echo Installing...
powershell -NoProfile -ExecutionPolicy Unrestricted .\powershell\install.ps1
@rem Chenge security setting to launch my_project/powershell/install.ps1.
@rem This setting return when this batch file is done.
echo Completed
pause

自作アプリを実行するためのアプリ

まずは,PowerShellのスクリプトであるmy_project/powershell/my_project.ps1を示す.

my_project/powershell/my_project.ps1
# Launch src/my_project.py
./python-3.6.8-embed-amd64/python ./src/my_project.py

次に,バッチファイルのスクリプトであるmy_project/my_project.batをしめす.

my_project/my_project.bat
@echo off
powershell -NoProfile -ExecutionPolicy Unrestricted .\powershell\my_project.ps1
pause

自作アプリにてtkinterを使用した場合

上記の方法での一番の欠点は,tkinterを利用できないことである.ただし,Open-file dialogのためだけにtkinterを用いる場合,my_project/powershell/my_project.ps1を改良することで,対処が可能である.

my_project/powershell/my_project.ps1
[void][System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms")
$dialog = New-Object System.Windows.Forms.OpenFileDialog
# おまじない
$dialog.Filter = "VIDEO FILE(*.MOV;*.MP4;*.AVI)|*.MOV;*.MP4;*.AVI"
# Open-file dialogにて,選択するファイルの種類を指定する.
$dialog.InitialDirectory = "~/Desktop"
# Open-file dialogがデフォルトで表示するフォルダを指定する.
$dialog.Title = "Choose the video file"
# Open-file dialogで表示するタイトルを指定する.

if($dialog.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK){
  ./python-3.6.8-embed-amd64/python ./src/my_project.py $dialog.FileName
  # Open-file dialogでファイル名を正常に取得した場合,my_project/src/my_project.pyを実行する.
}

なお,my_project/src/my_project.pyでは,以下に示すように,sys.argv[1]などで,コマンドライン引数を取得できるようにする.

my_project/src/my_project.py
import sys

filename = sys.argv[1]

参考文献

超軽量、超高速な配布用Python「embeddable python」(このページに書かれているものは,左のページに載っているものを自動化したものです.)