Python でパッケージを開発して配布する標準的な方法 - Qiita の更新版です。setup.py の代わりに pyproject.toml というファイルが使われるようになった所が大きな変更点です。
Python コマンドが入っている前提で始めます。ここでは開発中のプロジェクトをインストールできる editable installs という機能を使うので、この機能が入った pip 21.3 以降がインストールされている事をご確認ください。
$ python --version
Python 3.7.17
$ pip --version
pip 23.0.1 from .pyenv/versions/3.7.17/lib/python3.7/site-packages/pip (python 3.7)
仮想環境の作成
まず環境が混ざらないように 12. 仮想環境とパッケージ — Python 3.11.4 ドキュメント を参考に仮想環境を作ります。
mkdir packaging_tutorial
cd packaging_tutorial
python -m venv .venv --prompt tutorial
source .venv/bin/activate
パッケージを実装する
こんな感じのパッケージを作ります。
├── LICENSE
├── README.md
├── corona/
│ └── __init__.py
├── pyproject.toml
└── tests/
前と同じですが、一応 __init__.py を書きます。まず新型コロナ情報を取得する __init__.py の例。
import requests
import sys
def get(country: str) -> str:
url = f"https://corona-stats.online/{country}?minimal=true"
response = requests.get(url, headers={'user-agent': 'curl'})
return response.text
def main() -> None:
country = sys.argv[1] if len(sys.argv) > 1 else ""
print(get(country))
pyproject.toml はこんな感じです。
[project]
name = "corona-propella"
version = "0.0.1"
authors = [{ name="Propella", email="propella@example.com" }]
description = "A covoid-19 tracker"
readme = "README.md"
requires-python = ">=3.7"
classifiers = [
"Programming Language :: Python :: 3",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
]
dependencies = [
"requests ~= 2.23.0",
]
[project.optional-dependencies]
dev = [
"build",
"twine",
]
[project.scripts]
corona = "corona:main"
[project.urls]
"Homepage" = "https://github.com/propella/sampleproject"
"Bug Tracker" = "https://github.com/propella/sampleproject/issues"
Declaring project metadata — Python Packaging User Guide に各項目の詳しい説明があります。特に私が重要だと思うのは以下の項目です。
-
dependencies: 必要なパッケージを書きます。ここではrequestsを使っています。 -
project.optional-dependencies: 開発時だけ使うパッケージを書きます。 -
project.scripts: このパッケージがコマンドを提供している時に書きます。この例だと、coronaコマンドを実行すると corona.main 関数が呼ばれます。python -m coronaでも実行されるので、この機能があると__main__.pyは不要みたいです。
おもむろに pip install -e . してみましょう。corona コマンドが使えるようになります。
(tutorial) $ pip install -e .
(tutorial) $ corona jp
Rank Country Total Cases New Cases ▲ Total Deaths New Deaths ▲ Recovered Active Critical Cases / 1M pop
1 Japan (JP) 33,803,572 74,694 33,728,878 83 269,169
World 692,178,105 4 ▲ 6,903,051 623,750,871 61,524,183 6,438 88,809.10
この -e オプションはローカルパスを受け取り編集可能 (editable) なままインストールします。ファイルを変更するとインストールされたパッケージも更新されるので開発に便利です。
配布パッケージの作成
作成したパッケージを配布するには、 build というツールで配布パッケージを作ります。すでに pyproject.toml の project.optional-dependencies に配布パッケージを作成する build が開発時の依存として指定されているので、開発時パッケージをインストールします。以下のようにパッケージ名(この場合はカレントディレクトリ)に続き [] の中にキーを指定します。
pip install -e .[dev]
パッケージを作成します。
python -m build
すると dist/ ディレクトリに二つのファイルができます。ソース配布の .tar.gz とビルド配布の .whl です。これらのファイルを受け取った人は pip コマンドでパッケージをインストールする事ができます。さらに次に説明する PyPI を使うと、プロジェクトの名前を指定するだけでインストールできるようになります。
PyPI に公開する
作ったパッケージは PyPI に公開しましょう。初めての方は、test.pypi.org にアカウントを作って練習すると良いです。以下 test.pypi.org の設定で説明します。
事前準備
- 自分のアカウントを登録する。
- API token を作成する。
- https://test.pypi.org/manage/account/#api-tokens
- この際に scope に Entire account と指定します。
- この画面を閉じると二度と token をみる事ができなくなるので注意。
作成した token を ~/.pypirc に保存しておくと、twine コマンドが読み取ってくれるようになります。
[testpypi]
username = __token__
password = pypi-hogehoge
test.pypi.org ではなく本番は pypi.org を使うときは上の [testpypi] を [pypi] とします。
このように、username は常に __token__ です。
PyPI にアップロードするには twine というツールを使います。ここではすでに project.optional-dependencies でインストール済みなので、以下でアップロードします。
python -m twine upload --repository testpypi dist/*
もしも ~/.pypirc にパスワードを書いていない場合はここでユーザ名とパスワードを聞かれるので、ユーザ名に __token__ パスワードに取得した token を回答します。
ここで https://test.pypi.org/manage/projects/ を参照すると、自動的に新規プロジェクトが作成されている事を確認できます。問題なければ実際にインストール可能か確認してみましょう。適当な venv を作って pip install します。ただ、この段階では test.pypi.org に必要な依存パッケージが無いので実際に動かすとエラーになると思います。
deactivate
mkdir -p ~/tmp/hoge
cd ~/tmp/hoge
python3 -m venv .venv
source .venv/bin/activate
python3 -m pip install --index-url https://test.pypi.org/simple/ --no-deps corona-propella
(色々テストする)
deactivate
ここで、pip install の引数には以下のような意味があります。
-
--index-url: test.pypi.org を使う事を指示します。本番 pypi.org では不要です。 -
--no-deps: もしもあなたのプロジェクトが他のパッケージに依存している場合、test.pypi.org からはインストールできないのでこのオプションを使って依存を無視する事を伝えます。これも本番 pypi.org では不要です。
これでうまく行けばいよいよ本番 pypi.org に公開してください。test.pypi.org との違いは以下です。
- 公開時の
twine uploadコマンドに--repository引数は不要です。 - テスト時の
pip installコマンドに--index-urlや--no-deps引数は不要です。