プログラミング初心者がアーキテクトっぽく語る

見苦しい記事も多数あるとは思いますが訂正しつつブログと共に成長していければと思います

簡単なPython配布パッケージの作り方

Pythonで書いたコードを利用するときどうしますか?

一番簡単な方法はpythonコマンドの引数として実行することでしょう。

python mycode.py

でも実行するたびにコードがあるフォルダに移動したり、逆に実行したいフォルダにコードをコピーしたり、もしくはコードがあるフォルダの長い絶対パスを書いたりするのって面倒じゃないですか?

また、もしコードが複数のモジュールで構成されていた場合はどうしましょう?複数のコードをコピーするのは煩雑です。エイリアス、リンク、PATHを活用すれば対処できそうですが、もっとよい方法はないものでしょうか?

ライブラリとして利用するならどうしましょう?開発環境にコピーしてimportしますか?面倒ですしコピーが沢山生まれて管理できなくなりませんか?


配布パッケージ

そんなときは配布パッケージにしてpipでインストールしましょう。そうすれば一般的なツールやライブラリのように利用できます。

また、作成したコードの配布、デプロイを意識することは開発者としてとてもよい勉強になります。是非、体験することをおすすめします。


作成方法

Pythonでは配布パッケージの種類も作り方も色々あります。

ここではPyCharmを活用した、追加のツールのインストールが不要な、シンプルな方法をご紹介します。pipとPyCharm CEがインストールされている前提ですがそれ以外は必須ではありません。

パッケージを広く公開したいならもっと色々と調べた方がよいですが、さくっと自分用パッケージを作って試したい人にはこれでも十分楽しめるはずです。


1. コードを書く

以下のような構成のコードを想定します。

mypkg
├── mypkg
│   ├── __init__.py
│   └── main.py
└── tests
    └── test_main.py

main.pyにはmain関数が定義されているとします。


2. setup.pyを作成する

以前はPyCharmの「Tools」から「Create setup.py」を選択して雛形を作成してから編集していましたがやめました。PyCharmが作成した雛形はdistutils.coreを使っていたり、修正する箇所が多く、もはやあまり意味がないと思ったからです。

  • 以下の内容をベースに最上位階層にsetup.pyを作成する。
from setuptools import setup

setup(
    name='mypkg',
    version='0.0.1',
    packages=['mypkg'],
    license='MIT',
    author='myname',
    install_requires=[
        "selenium==4.1.2",
    ],
    entry_points={
        'console_scripts': [
            'mypkg=mypkg.main:main',
        ],
    },
    description='Some description'
)
  • 足りない項目の追加、不要な項目の削除を行う。
  • 編集する上での注意点は下記3点。

packagesにテスト用のパッケージがないことを確認する

  • テストまで配布されてしまう。

依存パッケージがあればinstall_requiresを定義

  • 依存パッケージをinstall_requiresに書いておくとpip installするときに自動でインストールしてくれる。
  • 以前はバージョン指定ができなかったそうだが今日時点では指定できる
  • 依存パッケージがなければinstall_requiresは削除する

コマンドにするならentry_pointsを定義

  • ただのライブラリではなくCLIツールとしてインストールするならentry_pointsを定義する。
  • CLIツールにしないならentry_pointsは削除する

3. パッケージの挙動を確認

  • PyCharmのTerminalからインストールしてパッケージの挙動を確認する
cd <setup.pyがあるフォルダ>
pip install -e .
  • -eを指定すると開発環境のコードからリンクが張られた形になる
  • 下記のようにpipenvを使ってインストールすると開発環境のPipfileが更新されてしまうので注意
    • 開発対象パッケージが開発対象パッケージ自身に依存しているっておかしいですよね?
pipenv install -e .

4. 配布用のパッケージを作成

PyCharm → Tools → Run setup.py Task... → sdist
  • distフォルダが作成され、中にtar.gz形式のパッケージが配置される
  • bdist wheelでもよい
    • sdistの中身はソース, bdistの中身はバイナリ

5. 配布パッケージを仮想環境で試す(Option)

pipenvの場合

  • mkdir <適当なフォルダ>
  • cd <適当なフォルダ>
  • pipenv --python 3.9
  • pipenv install <配布パッケージ>
  • pipenv shell
  • 動作確認
  • deacitvate
  • exit
  • pipenv --rm
  • cd ..
  • rm -rf <適当なフォルダ>

virtualenvの場合

  • mkdir <適当なフォルダ>
  • cd <適当なフォルダ>
  • virtualenv <仮想環境名>
  • source <仮想環境名>/bin/activate
  • pip install <配布パッケージ>
  • 動作確認
  • deacitvate
  • rm -rf <適当なフォルダ>

6. pipで管理

これでpipで管理できるようになりました。後は普通にpipで管理してください。

レポジトリがあれば登録しましょう。レポジトリがない場合はローカルPCの自作パッケージフォルダにまとめておいて、インストールするときは以下のようにするのがよいと思います。

pip install --find-links <自作パッケージフォルダ> <配布パッケージ>

これでリポジトリではなくローカルPCの自作パッケージフォルダを探してインストールしてくれます。

この時点で動かないことが発覚したり、ツールが不要になったりして依存ライブラリごとまとめて削除したくなることもるでしょう。そのようなときはpip-autoremoveを使うと便利です。

以下の方法でインストールします。

pip install pip-autoremove

削除するときは以下のようにします。

pip-autoremove <パッケージ名>

削除対象パッケージと依存ライブラリが表示されます。他のパッケージが依存しているライブラリは除外してくれるそうですが念の為に確認した方がよいでしょう。問題なければ「Y」を押して削除を実行します。


付録

最後にパッケージ、モジュールの状態を確認するときに便利なPython機能をいくつか紹介します。

sys.modules

  • load済みのモジュール名とモジュールのマップ。
    • loadされていても意図したnamespaceにimportされているとは限らない
  • 確認するときはsorted(sys.modules)でソートしたキー配列を取得した方が見やすい。
>>> for i in sorted(sys.modules):
...  print(i)
... 

help('modules')

  • ロード可能なモジュールの一覧を表示する。
  • current directoryにあるモジュールも表示されるので混乱しないこと。

sys.path

  • importするモジュールを探すパス。
  • ここにパスが登録されていないとモジュールを見つけることができない

os.getcwd()

  • current directoryを表示