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
引数は不要です。