プロジェクト

全般

プロフィール

Python プログラム配布

はじめに

Pythonで作成したプログラムを、別なマシンで実行するために配布する方法を調査・整理するページです。
自分のマシン上でPythonのプログラムを作成し、実行するのは容易です。しかし、Pythonプログラムを別なマシンで実行する場合、配布先のマシンの制約によって配布がとても難しくなります。

Pythonはスクリプト言語なので、スクリプトファイルをコピーするだけで実行できるのではないか?と思います。そのスクリプトファイル1つだけで、標準モジュール以外を使用せず、同じバージョンのPythonが実行したいマシンに構築済みであれば、その通りです。あるいは、スクリプトファイルが依存するサードパーティパッケージの同じバージョンが実行したいマシンにインストール済みであれば、その通りです。しかし、実際にはなかなかそのように環境が一致することは稀です。

配布の条件を整理してみます。配布といっても、開発作業をしているマシンと実行するマシンとの間には次のように色々なケースがあります。

  • 開発環境と同じOS、同じPythonバージョンで実行する場合
  • 開発環境と同じOSだが、異なるPythonバージョンで実行する場合
  • 開発環境とは異なるOS、異なるPythonバージョンで実行する場合
  • 実行環境にPythonがない場合
  • 実行環境がインターネット接続を遮断されている場合
  • 実行環境を使うユーザーが管理者権限を持たない場合

本ページでは、配布先のマシンが異なるOSで、インターネット接続が遮断され、管理者権限がない場合を想定した配布方法を調べ、まとめていきます。

配布方法

配布方法として考えられる大まかな方針を列挙します。

  1. プログラム開発環境と同じ環境を配布先に再現する
  2. プログラムをパッケージ形式(wheel等)で作成し、パッケージとして配布する
  3. プログラムとプログラム実行に必要なものを一塊にまとめて配布する

1.は、配布というよりは同じ開発プロジェクトを実行環境で共有するようなイメージです。git等の開発リポジトリを介した共有になることが多く、実行プログラムを利用者に渡して実行してもらうには複雑な作業が発生します。
2.は、必要なパッケージファイル群を渡して実行環境にインストールして実行するイメージです。インターネット接続が遮断されている場合、プログラムが依存するライブラリ群の配布とインストールが必要になり、やや複雑な作業となります。
3.は、1つのファイルを渡してそれを実行するという、いわゆるソフトウェア(ツール)の配布に近いイメージです。

配布方法一覧

  • venvでプログラム開発環境を作成し、実行マシン上にvenvで同じ環境を再現する
  • venvでプログラム開発環境を作成し、パッケージ形式になるようプログラムを作成しパッケージ(wheel)と依存ライブラリ情報を生成する。実行マシン上にvenvを用意し、依存ライブラリ情報から依存ライブラリをインストールする。
    • インターネット接続が遮断されている場合、依存ライブラリをインターネット接続可能なマシンでダウンロードして配布する
  • OS固有の方法、OSに依存しない方法がいくつかある
    • Windows固有
      • Python公式サイトが配布する Python Windows embeddable packageを使い実行に必要なパッケージ等を組み込んで配布
      • PyInstallerでWindowsのEXE形式ファイルを作成し配布
    • Mac固有
      • PyInstallerでMac用の実行形式ファイルを作成し配布
    • Linux固有
      • PyInstallerでLinux用の実行形式ファイルを作成し配布

setuptools

setuptoolsパッケージを利用して、setup.pyに記述したパッケージ情報でプログラムのパッケージを wheel 形式のビルド済配布物を生成します。

ビルド済配布物の作成

まず、プログラムの開発ディレクトリを作成し、プログラムを作成します。

MacOS環境

仮想環境を作成します。

% mkdir work
% cd work
% python3 -m venv venv
% source venv/bin/activate
(venv) % 

アプリケーションのパッケージ(ディレクトリ)myappを作成し、__init__.py(中身は空)と、main.pyを作成します。
setup.pyに、パッケージの情報を記述(記述例は後述)します。

work
 +-- myapp
 |    +-- main.py
 |    +-- __init__.py
 +-- setup.py

ビルド済配布物の作成

(venv) % python setup.py bdist_wheel
  :
(venv) % ls dist 
myapp-0.0.1-py3-none-any.whl

setup.pyに定義した name, version に基づく配布物のファイルが生成されます。

  • bdist_wheel の実行には、wheelパッケージのインストールが必要

ビルド済配布物のインストールと実行

実行マシンにおいてビルド済配布物のインストールをします。
実行用のディレクトリにvenv仮想環境を作成し、そこにビルド済配布物をpipでインストールします。

% python3.12 -v venv myapp
% cd my myapp
% source bin/activate
(myapp) % pip install ~/Download/myapp-0.0.1-py3-none-any.whl
  :
(myapp) % bin/myapp
Hello, Python setuptools ['bin/myapp']
(myapp) %

サンプルの記述例

  • main.py
    import sys
    
    def main():
        print(f'Hello, Python setuptools {sys.argv}')
    
  • setup.py
    import setuptools
    
    setuptools.setup(
        name='myapp',
        version='0.0.1',
        packages=setuptools.find_packages(),
        entry_points={
            'console_scripts':[
                'myapp=myapp.main:main'
            ]
        }
    )
    

参考
https://python-packaging-user-guide-ja.readthedocs.io/ja/latest/distributing.html

Windows環境で配布物を作成

setuptoolsが見つからないエラー

  • 3rd partyのライブラリ setuptools が必要です。pipでinstallします。

dist_wheelがエラー

  • wheel パッケージが必要です。pipでインストールします。

作成

展開

調査メモ

配布方法の調査

意外なことに、Pythonで作成したプログラムを、実行可能なソフトウェアとして配布するには少し工夫が必要です。
目指す配布の姿としては、アーカイブファイルを展開するだけで実行可能となるものです。制約としてPythonだけ先にインストールしておくことはアリとします。
クローズな環境でも動かしたいので、配布時にネットワークからpip等でライブラリをDLするのは候補から除外します。

整理すると、配布の要件は次のようになります。

  • 実行環境は、Windows、MacOS、Linuxのいずれも対象とする
  • 配布先のマシンはクローズな環境もあるので配布時にインターネットからダウンロードする必要がない手段とする
    • アーカイブファイルを1つ用意し、それを配布先にコピーして展開すれば実行できるというイメージ(同等な手間であれば他の方法も検討対象とする)
  • 事前にPython実行環境は配布先マシンに入れておくことは可能

調べてみると、

  1. 開発環境と同じ環境を配布したPCに用意し、再現する
    → pip等でライブラリを入れる必要があり
  2. Python Embeddableを使う(Windows環境のみ)
  3. EXE化(Windows以外でも動かしたいので除外)
  4. setup.py?
  5. rye ?
  6. venv の共有
    → ユーザー固有情報・設定があるので難しい(pythonへの絶対パス、システムにないパッケージかつバージョン・OS固有パッケージがvenvに入る、など)
  • setup.pyはもう古い? pyproject.tomlを記述

開発環境において、仮想環境(venv)を作り、パッケージを開発

  • パッケージのアーカイブファイル形式
    古くはegg、今はwheel
  • パッケージの種類として、ソースディストリビューション(sdist)、ビルドディストリビューション(bdist)がある
    bdistの1つがwheel(拡張子.whl)
  • パッケージのインストールには、pipやpoetryなどを使う
  • pyproject.tomlファイルにパッケージのメタデータを記述

setuptoolsについて

  • setuptoolsは、distutilsを包含して拡張したもの。setuptoolsはdistutilsへの依存がないので、Python 3.12以降 distutilsは削除された
  • かつては、setup.pyを使っていたが、いまはpyproject.tomlを使う
pyproject.toml
[project]
name="hello-tool" 
version="0.1.1" 
author="Foo Bar" 
dependencies = [ "pyhoge" ]

[project.optional-dependenceis]
tests = ["pytest"]

pip

  • Python の標準ツールとしてPythonに含まれる
    Windowsの場合、Python\Python312\Scripts\pip.exe が含まれる
  • pipでインストールしたパッケージは、Python312\Lib\site-packages\下に配置

インターネットに接続できない環境

依存パッケージのインストール方法

見つけた手段(要確認)

  • インターネット接続環境でpip install --downloadし、その結果をターゲット環境に移して pip install
    • インターネット接続可能な環境で、pip install --download /path/to/some/dir pymongo
    • ターゲット環境で、pip install --no-index --find-links /path/to/some/dir/ pymongo
  • パッケージのソース一式をターゲット環境に展開し、
    • python setup.py build
    • python setup.py install
インターネット接続環境上で、pip download
前提
  • ターゲット環境のPythonとインターネット接続環境のOSおよびPythonのバージョンを合わせておく(バージョン番号の上2桁:例 python 3.9.x)
    ダウンロードされるパッケージファイルがOSおよびpythonのバージョンに依存する場合がある
download/pymongo$ pip download pymongo
Collecting pymongo
  Using cached pymongo-4.6.1-cp38-cp38-manylinux2014_x86_64.whl (716 kB)
  Saved ./pymongo-4.6.1-cp38-cp38-manylinux2014_x86_64.whl
Collecting dnspython<3.0.0,>=1.16.0
  Using cached dnspython-2.4.2-py3-none-any.whl (300 kB)
  Saved ./dnspython-2.4.2-py3-none-any.whl
Successfully downloaded pymongo dnspython
download/pymongo$ 
ターゲット環境で、pip install

ローカルディスクに wheel 形式アーカイブファイルを保存し、それを pip で install します。

~$ pip install --no-index --find-links=./download/pymongo pymongo
Looking in links: ./pymongo
Processing ./download/pymongo/pymongo-4.6.1-cp38-cp38-manylinux2014_x86_64.whl
Processing ./download/pymongo/dnspython-2.4.2-py3-none-any.whl
Installing collected packages: dnspython, pymongo
Successfully installed dnspython-2.4.2 pymongo-4.6.1
  • --no-index オプション
    インデックスサーバ(PyPI)への参照を無効化
  • --find-links オプション
    パッケージ検索を開始するディレクトリを指定


3ヶ月前に更新