Edited at

超軽量、超高速な配布用Python「embeddable python」

追記:18-10-31 パッケージのインストールエラーに対応する方法を追記

Pythonを独立したExeファイルとして書き出すPyInstallerの記事を以前書きました

しかし、この方法よりも実行がより高速、より軽量な方法がありました。

embeddable pythonと呼ばれるもので、Windows限定ではありますが、ダウンロードしてきた圧縮状態なら 7 MB解凍しても 14 MB程度のPython実行環境です。

もちろんこのままではパッケージを使えませんので、そのパッケージ導入方法も合わせて記載します。


公式

3. Windows で Python を使う — Python 3.6.4 ドキュメント



ダウンロード


Python Releases for Windows | Python.org


上記ページにあるWindows x86-64 embeddable zip fileをダウンロードして解凍。

もしくは、WindowsPowerShellで以下を実行。(wgetが使えるため楽だから)


PowerShell

# 作業用フォルダへ移動(適当なところでOK)

cd (適当なところ)
# PowerShellのwgetでファイルをダウンロード(ウィルス対策ソフトに注意)
wget "https://www.python.org/ftp/python/3.6.4/python-3.6.4-embed-amd64.zip" -O "epython_zip.zip"
# 解凍
Expand-Archive -Path epython_zip.zip -DestinationPath epython


最小構成

pythonNN.dll、pythonNN.zip、vcruntime140.dllがあればPythonは動作します。

python.exeは、pythonNN.dllへ引数を送るだけの実行ファイルですので、独自C++に組み込むときは必要ありません。

(pythonNN.zipは圧縮ファイルですが、このままで機能しますので解凍してはいけません。 フォルダ名さえ気をつけていれば解凍しても使えます。詳しくは下部の「エラー対応」項目を参照)

今回は他の構成ファイルを使うのでそのままにしておいてください。


PIP


pythonNN._pthファイルを修正。

そのままではget-pip.pyが使えないので、少し手直しをする必要があります。

ダウンロードしてきたファイルの中にあるpython36._pthというファイルをエディタで開いて、


python36._pth

# import site


を、


python36._pth

import site


とコメント解除する必要があります。

python 3.6 embed cannot get pip · Issue #7 · pypa/get-pip · GitHub

それから、https://bootstrap.pypa.io/get-pip.pyのファイルをダウンロードするか、ダウンロード処理をPowerShellで以下の通りに実行。


PowerShell

# 解凍したepythonの中身へ移動

cd epython
# そのままではwgetでダウンロードできないので、下記を実行(PowerShellではデフォルトでTLS1.2が非対応のため)
# 一時的にTLS1.2を有効にします(PowerShellを閉じると解除される)
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12;
# Wget
wget "https://bootstrap.pypa.io/get-pip.py" -O "get-pip.py"


コマンドプロンプトで作業

これでget-pip.pyが使えるのでインストール作業をしますが、

ここからはコマンドプロンプトで作業します。(PowerShellではモジュールエラーで実行出来なかった。)


要注意点

get-pipに限らず、ルート環境にpythonがインストールされているときはembeddable pythonなのかルート環境なのかを注意して実行すること。(作業フォルダにembeddable pythonがあるかどうか。もしくはpipインストール後にpython -m pip list等でパッケージの中身を見て確認。)


cmd

cd (embeddable pythonのフォルダ)

# 念の為、python.exeがあるディレクトリか確認。
dir
# リストの中にpython.exeがあれば、get-pipをインストール
python get-pip.py

これでpipが使えるようになりました。

ただし、


cmd

# NG

# pip install numpy
# OK
python -m pip install numpy

のようにpython -m pip install (パッケージ名)の書式でなければなりません。


試しにMatplotlibとWxPython

試しにMatplotlibを使ってグラフを書きつつ、wxPythonで描画してみましょう。


Package

以下のようにパッケージをインストール


cmd

python -m pip install numpy

python -m pip install matplotlib
python -m pip install wxpython


テストスクリプト

テストスクリプトは下記URLから引用。

main.pyという名前でpython.exeと同じ階層に保存してください。


スクリプト引用元

python - Embedding a matplotlib figure inside a WxPython panel - Stack Overflow



main.py

#! env python

# -*- coding: utf-8 -*-

from numpy import arange, sin, pi
import matplotlib
matplotlib.use('WXAgg')

from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
from matplotlib.figure import Figure

import wx

class CanvasPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
self.figure = Figure()
self.axes = self.figure.add_subplot(111)
self.canvas = FigureCanvas(self, -1, self.figure)
self.sizer = wx.BoxSizer(wx.VERTICAL)
self.sizer.Add(self.canvas, 1, wx.LEFT | wx.TOP | wx.GROW)
self.SetSizer(self.sizer)
self.Fit()

def draw(self):
t = arange(0.0, 3.0, 0.01)
s = sin(2 * pi * t)
self.axes.plot(t, s)

if __name__ == "__main__":
app = wx.PySimpleApp()
fr = wx.Frame(None, title='test')
panel = CanvasPanel(fr)
panel.draw()
fr.Show()
app.MainLoop()



バッチファイルを保存

同じ階層にバッチファイルをmain.cmdという名前で以下のように記載して保存。


main.cmd

# このファイルの位置を作業ディレクトリに

cd /d %~dp0
# main.pyを実行
python.exe main.py

main.cmdを実行するとグラフが描画される。


容量

ディレクトリの容量は 183 M程度になりました。

パッケージを含めると流石に容量が大きいですが、しかし、速度はPyInstallerより高速に動いている感じがします。


エラー対応


pipでエラー

python -m pip install パッケージ名

上記のようにパッケージを追加しようとしたときエラーが表示されることがあります。

このエラーの原因は、zipファイル内のlib2to3というパッケージの中身が展開出来ないことによるもののようです。

対応方法としては、

1.同封の「PythonNN.zip」を失敗した時の為にバックアップ、コピーしておく。

2.「PythonNN.zip」をフォルダとして展開。

3.「PythonNN」というフォルダ名から「PythonNN.zip」に変更。

“python setup.py egg_info” failed with error code 1

https://stackoverflow.com/questions/42962765/embedded-python-3-5-python-setup-py-egg-info-failed-with-error-code-1


ModuleNotFoundError: No module named XXX

同じ階層にpyファイルがあり、それをimportしようとしてエラーが発生した場合、その発生箇所よりも前に、以下のスクリプトを追加する。

sys.path.append(os.path.dirname(os.path.abspath(sys.argv[0])))


C++にembeddable pythonを埋め込む

別の記事として編集中。そのうち気が向いたときにでも書いていきます。


公式

1. 他のアプリケーションへの Python の埋め込み — Python 3.6.4 ドキュメント

Python/C API リファレンスマニュアル — Python 3.6.4 ドキュメント