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

![0.wybf0c7nin9.png](https://qiita-image-store.s3.amazonaws.com/0/76058/8b67f823-f19e-7c98-a750-3766adcd5746.png)

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

Pythonを独立したExeファイルとして書き出すPyInstallerの記事を[以前書きました](https://qiita.com/mm_sys/items/a7690dface9727704143)。
しかし、この方法よりも実行がより高速、より軽量な方法がありました。
embeddable pythonと呼ばれるもので、Windows限定ではありますが、ダウンロードしてきた**圧縮状態なら 7 MB**、**解凍しても 14 MB程度**のPython実行環境です。
もちろんこのままではパッケージを使えませんので、そのパッケージ導入方法も合わせて記載します。

> 公式
> [3. Windows で Python を使う — Python 3.6.4 ドキュメント](https://docs.python.jp/3/using/windows.html)

## ダウンロード

> [Python Releases for Windows | Python.org](https://www.python.org/downloads/windows/)

上記ページにある**Windows x86-64 embeddable zip file**をダウンロードして解凍。
もしくは、**WindowsPowerShell**で以下を実行。（wgetが使えるため楽だから）

```PowerShell: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
```

![0.igcn5ygx4xs.png](https://qiita-image-store.s3.amazonaws.com/0/76058/d5b6e484-e5cc-97b1-2d99-e07666a5b0d8.png)

## 最小構成

pythonNN.dll、pythonNN.zip、vcruntime140.dllがあればPythonは動作します。
python.exeは、pythonNN.dllへ引数を送るだけの実行ファイルですので、独自C++に組み込むときは必要ありません。
(pythonNN.zipは圧縮ファイルですが、このままで機能しますので~~解凍してはいけません。~~ フォルダ名さえ気をつけていれば解凍しても使えます。詳しくは下部の「エラー対応」項目を参照)

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

![0.9x3kncj8xjq.PNG](https://qiita-image-store.s3.amazonaws.com/0/76058/c0ff9db0-8c07-a4a7-bb90-ae7fb756fd6a.png)

## PIP

### `pythonNN._pth`ファイルを修正。

そのままでは`get-pip.py`が使えないので、少し手直しをする必要があります。
ダウンロードしてきたファイルの中にある`python36._pth`（pythonのバージョンによって数字部分が変わる）というファイルをエディタで開いて、

```python:python36._pth
# import site
```

を、

```python:python36._pth
import site
```

とコメント解除する必要があります。
[python 3.6 embed cannot get pip · Issue #7 · pypa/get-pip · GitHub](https://github.com/pypa/get-pip/issues/7)

※ pythonのバージョンによっては、以下の１行も`python36._pth`に追記します。（コメントの@alphya様からの情報）

```batchfile
./Lib/site-packages
```

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

```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"
```

### pthファイル

https://qiita.com/rhene/items/529386ce9b7c86cfe401

上記記事にあるように、3.6系以上のバージョンの場合は、ダウンロードしてきた`python.exe`と同じディレクトリに適当なファイル名に`.pth`拡張子をつけたファイルを作成し、以下の内容を記載して保存します。

```python
import sys; sys.path.append('')
```

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

これで`get-pip.py`が使えるのでインストール作業をしますが、
ここからは**コマンドプロンプト**で作業します。（PowerShellではモジュールエラーで実行出来なかった。）

![0.0v0n1hrp4rv.PNG](https://qiita-image-store.s3.amazonaws.com/0/76058/b0025c34-8b15-e52b-f7ca-9ab26d336019.png)

### 要注意点

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

**pip、get-pip関係のエラーは下部のエラー対策箇所にまとめておきました**

```cmd:cmd
cd (embeddable pythonのフォルダ)
# 念の為、python.exeがあるディレクトリか確認。
dir
# リストの中にpython.exeがあれば、get-pipをインストール
python get-pip.py
```

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

```cmd:cmd
# NG
# pip install numpy
# OK
python -m pip install numpy
# エラーが出る場合は以下のようにオプションをつけて実行
python -m pip --no-cache-dir install -I -U numpy
```

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

## 試しにMatplotlibとWxPython

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

### Package

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

```cmd: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](https://stackoverflow.com/questions/10737459/embedding-a-matplotlib-figure-inside-a-wxpython-panel)

```python: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`という名前で以下のように記載して保存。

```text:main.cmd
rem このファイルの位置を作業ディレクトリに
cd /d %~dp0
rem main.pyを実行
python.exe main.py
```

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

![0.wyhsc17sc2.png](https://qiita-image-store.s3.amazonaws.com/0/76058/6545d29b-e3e0-66cd-bc21-4dd87b7ac911.png)

### 容量

ディレクトリの容量は **183 M程度**になりました。
パッケージを含めると流石に容量が大きいですが、しかし、速度はPyInstallerより高速に動いている感じがします。

## エラー対応

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

```bash
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

##### 解決しない場合

setuptoolsのバージョンをダウングレードして解決する場合があります。これは、2to3をサポートしなくなったことに由来するとのこと。

> https://qiita.com/nkmr_RL/items/85edc2ee68c01ec5582e

```bash
python -m pip install setuptools==57.4.0
```

### ERROR: Could not install packages due to an OSError: .....

[こちらの記事に対策が書かれていました。](https://qiita.com/zousan010sanpotyu/items/2757715bd84cea8e5479#_reference-6530f63d84deae19f7d6)

### Python.h pythonNN.lib

`Python.hが見つかりません` や、 `'python37.lib' を開くことができません。` といった旨のエラーが出た場合は、`python -m pip install ....` コマンドを実行する前にそのファイルが含まれるパスを以下のコマンドで追加します。

[また、この手のエラーはコンパイラも必要となる場合もあります。](https://qiita.com/mm_sys/items/8dad44791094b07171a9) Visual Studioやbuild toolsをインストールしたりしましょう。

例えば、AnacondaやMinicondaのがインストールされている場合は、以下のようになります。（例はMiniconda)

```batchfile
SET INCLUDE=C:\ProgramData\Miniconda3\include;%INCLUDE%
SET LIB=C:\ProgramData\Miniconda3\libs;%LIB%
```

### ModuleNotFoundError: No module named XXX

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

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


> 公式
> [1. 他のアプリケーションへの Python の埋め込み — Python 3.6.4 ドキュメント](https://docs.python.jp/3/extending/embedding.html)
> [Python/C API リファレンスマニュアル — Python 3.6.4 ドキュメント](https://docs.python.jp/3/c-api/index.html#c-api-index)