LoginSignup
14
18

Python でパッケージを開発して配布する標準的な方法 2023 年編

Last updated at Posted at 2023-07-28

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.tomlproject.optional-dependencies に配布パッケージを作成する build が開発時の依存として指定されているので、開発時パッケージをインストールします。以下のようにパッケージ名(この場合はカレントディレクトリ)に続き [] の中にキーを指定します。

pip install -e .[dev] 

パッケージを作成します。

python -m build

すると dist/ ディレクトリに二つのファイルができます。ソース配布の .tar.gz とビルド配布の .whl です。これらのファイルを受け取った人は pip コマンドでパッケージをインストールする事ができます。さらに次に説明する PyPI を使うと、プロジェクトの名前を指定するだけでインストールできるようになります。

PyPI に公開する

作ったパッケージは PyPI に公開しましょう。初めての方は、test.pypi.org にアカウントを作って練習すると良いです。以下 test.pypi.org の設定で説明します。

事前準備

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

参考

14
18
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
18