PyPIにパッケージを公開する手順の整理
はじめに
この記事ではPyPIにパッケージを公開するための手順について理解が曖昧だった部分を中心に整理してまとめます。 内容はPython公式のチュートリアル Packaging Python Projects を参考にしています。
PyPIについて
PyPIはPython Package Indexの略でPythonのパッケージを管理しているリポジトリです。 普段pipを使ってインストールするパッケージはPyPIに登録されています。 PyPIにパッケージを登録する場合はDjangoやrequestsのような他のパッケージと重複しない一意なパッケージ名が必要になります。
パッケージの公開方法
パッケージを公開するための大まかな流れは次のようになります。
- パッケージ公開に必要なファイルの準備
- パッケージのビルド
- パッケージのアップロード
ディレクトリ構成
チュートリアルでは次のようなディレクトリ構成を採用していて、これがほぼ最小構成になります。
packaging_tutorial/ ├── LICENSE ├── pyproject.toml ├── README.md ├── setup.cfg ├── src/ │ └── example_package/ │ ├── __init__.py │ └── example.py └── tests/
- LICENSE: パッケージのライセンス
- pyproject.toml: パッケージをビルドするための情報を書く設定ファイル
- README.md: パッケージの利用者向けのドキュメント
- setup.cfg: パッケージ名やバージョンを含むパッケージ自体の情報を書く設定ファイル
- src/: パッケージを配置するディレクトリ
- src/example_package/: パッケージ本体
- tests/: テストコードを配置するディレクトリ
設定ファイルについて
Pythonの著名なパッケージではsetup.cfgの他にsetup.pyというファイルを利用していたり、pyproject.tomlがなかったりして闇雲に参考にすると混乱します。 そこでここでは各ファイルの役割を整理して説明します。
setup.cfg
setup.cfgはsetuptools用の静的なメタデータでconfigparserの形式で記述します。 setup.pyが実行可能なPythonコードであるのに対してsetup.cfgは静的な設定ファイルなのでバグが入り込む余地が少ないです。 そのため、基本的にはsetup.cfgに必要な設定を書くことが推奨されています。 チュートリアルでは次のような設定を使っています。
[metadata] name = example-pkg-YOUR-USERNAME-HERE version = 0.0.1 author = Example Author author_email = author@example.com description = A small example package long_description = file: README.md long_description_content_type = text/markdown url = https://github.com/pypa/sampleproject project_urls = Bug Tracker = https://github.com/pypa/sampleproject/issues classifiers = Programming Language :: Python :: 3 License :: OSI Approved :: MIT License Operating System :: OS Independent [options] package_dir = = src packages = find: python_requires = >=3.6 [options.packages.find] where = src
利用可能なメタデータはsetuptoolsのドキュメントにまとまっています。
setup.py
setup.pyもsetup.cfg同様にsetuptools用のメタデータですが、実行可能なPythonファイルであり、内容もインストール時に動的に決定します。 setup.pyではパッケージに関する必要な情報を引数にsetuptools.setup()関数を実行します。 上記のsetup.cfgをsetup.pyで書く場合は次のようになります。
import setuptools with open("README.md", "r", encoding="utf-8") as fh: long_description = fh.read() setuptools.setup( name="example-pkg-YOUR-USERNAME-HERE", version="0.0.1", author="Example Author", author_email="author@example.com", description="A small example package", long_description=long_description, long_description_content_type="text/markdown", url="https://github.com/pypa/sampleproject", project_urls={ "Bug Tracker": "https://github.com/pypa/sampleproject/issues", }, classifiers=[ "Programming Language :: Python :: 3", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", ], package_dir={"": "src"}, packages=setuptools.find_packages(where="src"), python_requires=">=3.6", )
昔からあるパッケージや複雑な処理が必要な大規模なパッケージではsetup.pyをよく利用していますが、最初にパッケージを公開する段階ではsetup.cfgで十分でしょう。 ただし、後述するpipのeditableインストールのために次のようなsetuptools.setup()関数を実行するだけのsetup.pyをプロジェクトに含めておくと良いです。
import setuptools
setuptools.setup()
pyproject.toml
pyproject.tomlはパッケージをビルドする手順を記載する設定ファイルになります。 ビルドに関する情報はbuild-systemテーブルに記載します。
[build-system] requires = [ "setuptools>=42", "wheel" ] build-backend = "setuptools.build_meta"
build-system.requiresには公開したいパッケージをビルドするために必要なビルド用のパッケージを列挙します。 チュートリアルではsetuptoolsとwheelを採用しています。
build-system.build-backendにはパッケージをビルドするためのPythonオブジェクトを指定します。 チュートリアルではsetuptools.build_metaを採用しています。
setuptoolsとwheelが最もスタンダードなパッケージになりますが、他にも選択肢*1はあり、それらを採用する場合はbuild-systemを書き換えます。
pyproject.tomlがなくともパッケージのビルドは可能で、その場合は必要なパッケージを手動でインストールし、各パッケージのビルド用のコマンドを実行することになります。 例えばよく紹介されている次の手順は手動でsetuptoolsとwheelをインストールしてビルドする方法になります。
# ビルド用のパッケージをインストール pip install --upgrade setuptools wheel # ビルド用のコマンドを実行 python setup.py sdist bdist_wheel
pyproject.tomlはパッケージのビルドに関する情報以外も記載することができ、各パッケージはtool.パッケージ名テーブルの中を自由に使うことができます。*2例えばPythonのLinter/Formatterであるblackやisortがオプションを設定する目的で使っています。
pipのeditableインストールについて
開発中のパッケージを開発環境にインストールしてインポートしたり実行可能なコマンドを利用可能にする場合にpipのeditable modeを有効にしてインストールすると、ソースコードを変更しても再インストール不要で変更が反映されます。 具体的には開発中のパッケージのプロジェクトルート(setup.cfgなどが置いてあるディレクトリ)で次のコマンドを実行します。
pip install -e .
pipのeditableインストールには以下のような変更の歴史があります。
- pip21.1より前のバージョンではeditableインストールをするにはsetup.pyが必須だった
- pip21.1の機能でsetup.py無しでsetup.cfgだけでもeditableインストールがサポートされた
- pip21.1.3でsetup.pyが無い場合はpyproject.tomlのbuild-systemの指定が必須に変更された
そのため、editableインストールを利用する場合は次の3ファイルを揃えておくと古いバージョンのpipに対しても互換性が確保されて不具合が起きにくいでしょう。
- パッケージのメタデータを記載したsetup.cfg
- setuptools.setup()を実行するだけのsetup.py
- build-systemを定義したpyproject.toml
ビルド方法
pyproject.tomlにビルド用のメタデータを記載している場合、buildパッケージだけでビルドができます。 buildパッケージを使う場合、ビルドに必要なsetuptoolsなどのパッケージはビルド用の一時的な仮想環境にインストールされるので、開発環境に影響を及ぼすこともありません。
# buildパッケージのインストール pip install --upgrade build # パッケージのビルド python -m build
ビルドが完了するとdistディレクトリの中にビルド済みのファイルが作成されます。
アップロード方法
事前にアカウント登録が必要になります。
アップロードにはtwineというパッケージを使います。 最初は本番のPyPIではなくテスト用のTestPyPI にアップロードすることを推奨します。
twineのインストール
pip install --upgrade twine
パッケージのチェック
アップロード前に内容に問題が無いかチェックします。 何もエラーが出なければアップロードに進みます。
twine check dist/*
TestPyPI へのアップロード
アップロード
twine upload --repository testpypi dist/*
TestPyPIからのインストール
pipインストール時に--index-url
オプションを付けると本番のPyPI以外からもインストールできます。
pip install --index-url https://test.pypi.org/simple/ パッケージ名
本番のPyPIへのアップロード
twine upload dist/*
本番のPyPIからのインストール
通常のpipインストールと同様です。
pip install パッケージ名