PyPIでパッケージを配布するときのプラクティス
概要
Python Packaging User Guide と エキスパートPythonプログラミング 改訂3版 を読みました。
パッケージの生成や配布には様々なプラクティスがあるように思えたので個人的に印象的だったものを抜粋してまとめました。
中には古い情報もありそうでしたが、新しいツールを理解する下地としては使えそうだったのでそう言うものも含めて記録してます。
前提
以下のような経験や知識があること
- PyPIへのパッケージ登録を行ったことがある、もしくは大まかな手順は把握している
定義
自分が最初調査したときはいきなり 「ソース配布物」 みたいな単語が出てきて何のことかわからなかったので、そういったものは下表にまとめました。
用語 | 意味 |
---|---|
ビルド成果物 | 一般的にパッケージを公開するためにはパッケージ配布サービス(一般的にはPyPI)に登録しなければいけないが、そこにアップロードするもの。ソース配布物とビルド済み配布物に大別される |
ソース配布物 (ソースパッケージ) |
ビルド成果物の一種 (sdist ) 。パッケージのモジュールを圧縮済みのアーカイブ (.tar.gz ファイル)にしたもの |
ビルド済み配布物 (ビルド済みパッケージ) |
ビルド成果物の一種 (wheels ) |
バイナリ配布物 | ビルド済み配布物のうち、コンパイル済みのエクステンションを含むもの |
純Python | CやC++などの他言語で書かれた拡張を含まず、Pythonのソースだけで構成されていること |
sdist
や wheels
は頻出用語ですが、以下で詳細を確認できます。
プラクティスのindex
ツールの選定のプラクティス
- ソース配布物の生成には
setuptools
を用いる - ビルド済みパッケージ配布形式には
wheel
を用いる - ビルド成果物のアップロードには
twine
を用いる
パッケージ生成のプラクティス
- ある程度の規模のパッケージでは名前空間を分ける
- メタデータの設定値を極力ハードコーディングしない
- ライセンスは必ず指定する
- ビルド前にメタデータを検証する
パッケージ配布のプラクティス
- PyPIで配布する前にTestPyPIで予行演習をしておく
- バイナリ配布物は対応するソース配布物とペアで配布する
- PyPIでの検索利便性向上のために
classifiers
を設定しておく -
twine check
で PyPI でlong description
が正しく描画されるかを確認しておく
ツールの選定のプラクティス
setuptools
を用いる
ソース配布物の生成には 標準ライブラリで distutils
というものがあり、それでもプロジェクトの定義やソース配布物の作成が行えるが、より高機能な setuptools
の使用が推奨されている。
(distutils
は1998年に導入され、 setuptools
はその強化版として2003年にリリースされている)
従って、ソース配布物の生成には setuptools
を用いる。
※ Python 3.12 では distutils
が廃止される予定
wheel
を用いる
ビルド済みパッケージ配布形式には wheel
は egg
を置き換えるために開発されたパッケージ配布の新しい標準。
egg
形式は現在では非推奨となっているため wheel
を使う。
pipコマンドなどで wheel
をインストールしておくと bdist_wheel
が distutils
に追加される。
それにより python setup.py bdist_wheel
のようにビルドが実行できる。
wheel
の利点は以下で確認できる。
twine
を用いる
ビルド成果物のアップロードには パッケージ配布サービスへビルド成果物をアップロードするためのツールには twine
があり、これを使用することが推奨されている。
Warning In other resources you may encounter references to using python setup.py register and python setup.py upload. These methods of registering and uploading a package are strongly discouraged as it may use a plaintext HTTP or unverified HTTPS connection on some Python versions, allowing your username and password to be intercepted during transmission.
cite: https://packaging.python.org/en/latest/guides/distributing-packages-using-setuptools/#uploading-your-project-to-pypi
python setup.py register
と python setup.py upload
は通信上の脆弱性があるので使ってはいけない。
パッケージ生成のプラクティス
ある程度の規模のパッケージでは名前空間を分ける
ある程度大きなパッケージは構造化を工夫して並行して開発できるようにした方がいい。
例えば、当方が個人開発で自作しているボートレース関連のパッケージは汎用的な名前空間として metaboatrace
があって、その下にそれぞれ models
, scrapers
などの名前空間が派生している。
これならboatrace.models
と boatrace.scrapers
をそれぞれ独立して開発することができる。
メタデータの設定値を極力ハードコーディングしない
setup.py
や setup.cfg
の各項目には、値をプロジェクトに点在する他のソースコードや設定ファイルなどから取得できるものがある。
そういったものを動的に取得することで不整合を回避できる。
例えば、バージョンは以下のように設定することでモジュールでの定義を動的に読み込める。
[metadata]
version = attr: boatrace.models.__version__
long_description
なんかも README
のコンテンツを適用するなどのプラクティスがある。
ライセンスは必ず指定する
Every package should include a license file detailing the terms of distribution. In many jurisdictions, packages without an explicit license can not be legally used or distributed by anyone other than the copyright holder. If you’re unsure which license to choose, you can use resources such as GitHub’s Choose a License or consult a lawyer.
cite: https://packaging.python.org/en/latest/guides/distributing-packages-using-setuptools/#license-txt
上記引用に記載があるように多くの国では明示的なライセンス条項がないと、著作権保持者でなければ誰も合法的にパッケージを使用したり配布したりすることができない。
ライセンスファイル(LICENSE.txt)を含めて、メタデータの設定ファイルでは適用するライセンスを明示しておくといい。
Optional, but it is highly recommended to supply this.
cite> https://python-poetry.org/docs/pyproject/#license
Poetry のドキュメントの pyproject.toml
の節でも定義が推奨されている。
ビルド前にメタデータを検証する
setup.py
で定義する項目が不足していないかなどは以下のように確認できるので、ビルド前にチェックしておく。
$ python setup.py check
running check
warning: check: missing required meta-data: version, url
warning: check: missing meta-data: either (author and author_email) or (maintainer and maintainer_email) should be supplied
パッケージ配布のプラクティス
PyPIで配布する前にTestPyPIで予行演習をしておく
PyPIには本番と同じ外観で、パッケージのインストールなど一連の動作も同様に行えるテスト環境がある。
実際にPyPIで登録する前にこれを利用して動作確認をしておくのがベター。
ただ、依存パッケージなどを利用する際に、本番のPyPIに登録されているバージョンが必ずしもTestPyPIにも登録されているとは限らないのでその点は注意。
例えば、numpy
。
この記事を記載している時点だとTestPyPIには 1.9.3
しか登録されていない。
pip install
実行の際にパッケージインデックスに TestPyPI を指定すると依存パッケージも TestPyPIから取得されるようで、ここで存在しないバージョンを取ろうとしてしまいエラーになることもある。
例えば以下。
$ pip install -i https://test.pypi.org/simple/ boatrace.official==0.0.7
Looking in indexes: https://test.pypi.org/simple/
Collecting boatrace.official==0.0.7
Downloading https://test-files.pythonhosted.org/packages/dc/e8/de01130d29b2edb554859915473589aecffdb166e5563464fcb45b7d32cd/boatrace.official-0.0.7-py3-none-any.whl (41 kB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 41.4/41.4 KB 1.0 MB/s eta 0:00:00
ERROR: Could not find a version that satisfies the requirement numpy==1.23.4 (from boatrace-official) (from versions: 1.9.3)
ERROR: No matching distribution found for numpy==1.23.4
バージョンが 1.23.4
の numpy
に依存しているパッケージを TestPyPI からインストールしようとしているが、 バージョンが 1.23.4
の numpy
は本番のPyPIにはあるが、 TestPyPIにはないのでダウンロードができずにエラーになる。
この点は注意が必要。
バイナリ配布物は対応するソース配布物とペアで配布する
pip はソース配布物(sdist
)とビルド済み配布物(wheels
)両方に対応している。
それら両方がPyPIに存在しているときは wheels
の方が優先される。
Wheels are a pre-built distribution format that provides faster installation compared to Source Distributions (sdist), especially when a project contains compiled extensions.
cite: https://packaging.python.org/en/latest/tutorials/installing-packages/#source-distributions-vs-wheels
wheels
がバイナリ配布物である場合は sdist
(ソース配布物) に比べて短時間でインストールされる。
ただし、バイナリ配布物がパッケージを使用するプラットフォームに適合しているとは限らず、そういった場合にソース配布物からビルドを行えるように、バイナリ配布物は対応するソース配布物とペアで配布するのがベター。
純Python パッケージでは、典型的には、ひとつの "万能型" の wheel さえあれば十分らしいが、wheels
が利用できない場合のフォールバック先としてソース配布物が利用されるため、ソース配布物も同梱しておくのが無難。
classifiers
を設定しておく
PyPIでの検索利便性向上のために PyPIとdistutils は trove classifiers と呼ばれる分類名をパッケージのカテゴライズに用いる。
(setup.py
の classifiers
で指定しているもの)
これはパッケージのインストール自体に関わる情報ではないが、パッケージのカテゴリ検索などで用いられる。
適切に分類すればパッケージユーザーの利便性が上がり、パッケージエコシステムを健全に保つ責任を果たすことができるので設定するのがベター。
twine check
で PyPI で long description
が正しく描画されるかを確認しておく
これも前節と同様パッケージ配布サイト側(PyPI)での最適化の取り組み。
配布物をアップロードする前に以下のコマンドで brief/long description が文法的に妥当かどうかを確かめることができる。
$ twine check dist/*
Checking dist/boatrace.official-0.0.1-py3-none-any.whl: PASSED
Checking dist/boatrace.official-0.0.1.tar.gz: PASSED
最後に
冒頭でも触れたように中には古くなっている情報もありそうでした。
例えば今だと pyproject.yml
に設定が集約される傾向があったり、それを利用するなら python -m build
でビルド成果物を生成するのでそれなら setup
系のコマンド使う必要がなかったりしそうでした。
The configuration file depends on the tool used to create the build artifacts. The standard practice is to use a pyproject.toml file in the TOML format.
cite: https://packaging.python.org/en/latest/flow/#the-configuration-file
※ Python Packaging User Guide にも pyproject.toml
を使うのが一般的という記載がある
トレンドが変わりこの記事の大半がdeprecatedなものになるなどした時点で、記事を更新するなり新規作成するなりしたいと思います。
参考
Discussion