スキップしてメイン コンテンツに移動

Pythonで作ったCLIアプリの配布

Pythonで書いたプログラムをエンドユーザーに配布することになった。
カウンターパートの希望で、PyPIのようなpip経由でインストール可能な形式と、圧縮ファイル形式の2種が必要となった。
前者はプライベートネットワーク内のArtifactoryの中に構築してあるPyPIのレジストリへ、後者はそこへアクセスできない人へ。

検索してみるとsetup.pyを使用した配布が多い。とりあえず今回は巷にあるPythonのパッケージをExecutableなファイルにするツールは使わずこの正攻法を使用する。
そもそも開発環境ではpipenvを使用していて、依存性管理もそれで実施している。
が、pipenvでの実現方法が残念ながらストレートに理解できそうな方法では見当たらなかったので、Pipfileとsetup.pyで依存性をそれぞれ書きながらの対処となる。

まず、pipenv向けに作っていた修正前のsetup.py。

from setuptools import setup
setup()

これを例えば次のようにする。(値の一部はサニタイズのため適当)

from setuptools import setup, find_packages
setup(
   name='mypycli',
    version='1.0.0',
    packages=find_packages(include=["config*""src*"]),
    scripts=["src/mypycli.py"],
    entry_points={
        "setuptools.installation": [
            "eggsecutable = src.mypycli:main",
        ]
    },
    include_package_data=True,
    description='',
    install_requires=[
        'tqdm',
    ],
    url='https://github.com/x/x',
    author='tkhm',
    author_email='no-reply@example.com'
)

これを作成後、setup.pyを呼び出す。
pipenv環境では依存管理をしている環境に潜るために pipenv shellを使うので pipenv shell実施後に次のようにすれば、distディレクトリ以下にtar.gzのファイルとzipのファイルが出来上がる。

python setup.py sdist --formats="gztar,zip"

ちなみに、install_requiresは手書きでPipfileの[packages]から写した。この方法は、誰もが思うだろうけど、あまりスマートではないのでできることならpipenvらしいやり方を知りたいところだけど、今時点ではクリアではない。
次の内容は読んで参考にした。

今回自分はこの圧縮ファイルを直接PyPIに置いてアップロードしたけど、ガイドにある通り、次のようなコマンドでもいけそう。(というかコマンドであげる方が多分一般的)

python3 -m pip install --user --upgrade twine
python3 -m twine upload --repository testpypi dist/*

出典:https://packaging.python.org/tutorials/packaging-projects/#uploading-the-distribution-archives より抜粋

インストールについては、pipの方は単純だった。例えばこんな感じ。(リポジトリのアドレスはダミー)

python3 -m pip install https://private.artifactory.example.com/pypi/myuser/myrepo/1.0.0/mypycli-1.0.0.tar.gz

ただ、これでインストールしたらentry_pointsに指定した mypycli.py が実行コマンドとしてパスが通ったので、もう少し洗練させないといけないと思ったが、まだわかっていない。

また、圧縮ファイルの方は、展開後に次のコマンドでインストールできた。

# インストール
python3 setup.py build
python3 setup.py install
# 実行
python3 build/scripts-3.7/mypycli.py

基本的にこれでうまくいったのだけど、ユーザーからはsetup.pyにzip_safe=Falseを入れないとエラーでsetupうまくいかなかったとの報告も受けたが、チーム内では再現しないのでこれも継続調査。

Pythonの配布はまだまだ知らないことが多くて難しい......。