Python のプロジェクトをパッケージングする

このチュートリアルでは、簡単な Python プロジェクトをどのようにしてパッケージするのかについて、一通り見て回ります。パッケージを構成するために必要なファイルやディレクトリを追加する方法や、パッケージをビルドする方法、そして、Python パッケージインデックス (PyPI) にパッケージをアップロードする方法をお見せします。

ちなみに

このチュートリアルに出てくるコマンドを実行したら問題が発生したという場合には、コマンドと結果出力をコピーして、 GitHub の packaging-problems リポジトリで 新たな課題 を追加してください。我々が全力であなたをお助けします!

コマンドの中のいくつかは新しめのバージョンの pip でないとだめなので、最新版をインストールして使っていることを最初に確認しておいてください:

python3 -m pip install --upgrade pip
py -m pip install --upgrade pip

単純なプロジェクト

このチュートリアルは、 example_package_YOUR_USERNAME_HERE という名前の単純なプロジェクトを使っています。もしあなたのユーザ名が me であるなら、パッケージの名前は example_package_me となるでしょう; こうすることで、このチュートリアルに従っている他の人たちがアップロードするパッケージ群と名前が衝突することのない一意なパッケージ名を使っていることを保証できます。自分自身のプロジェクトのパッケージングを始める前に、このプロジェクト名を使ってこのチュートリアルに沿った練習をすることをお勧めします。

ローカルに以下のファイル構造を作成する:

packaging_tutorial/
└── src/
    └── example_package_YOUR_USERNAME_HERE/
        ├── __init__.py
        └── example.py

Python ファイル群を格納するディレクトリは、プロジェクト名と同じ名前であるべきです。こうすることで設定が簡単になり、また、パッケージをインストールするユーザから見てより明白になります。

パッケージとしてのディレクトリをインポートするには __init__.py が存在していなければならず、また、それは空であるべきです。

example.py は、パッケージの中であなたのパッケージのロジック (関数・クラス・定数・その他) を含むであろうモジュールの例です。このファイルを開いて、以下の内容を入力してください:

def add_one(number):
    return number + 1

Python における モジュールパッケージのインポート に馴染みがなければ、数分を費やして パッケージとモジュールに関する Python の説明文書 を読み通してください。

この構造さえ作れば、このチュートリアルに出てくるすべてのコマンドを packaging_tutorial ディレクトリで実行したくなるでしょう。

パッケージファイルを作成する

ここで、プロジェクトの配布を準備するために使われるファイル群を追加しましょう。それが終わったら、プロジェクトの構造はこんな風になっていることでしょう:

packaging_tutorial/
├── LICENSE
├── pyproject.toml
├── README.md
├── src/
│   └── example_package_YOUR_USERNAME_HERE/
│       ├── __init__.py
│       └── example.py
└── tests/

test ディレクトリを作成する

tests/ は、テスト用のファイルを置くためのプレースホルダーです。現段階では、空のままにしておいてください。

pyproject.toml を作成する

pyproject.toml は、 pipビルド のような "フロントエンド" のビルドツールに対して、どの "backend"を使ってそのプロジェクトの 配布パッケージ を作成するべきかを指定します。バックエンドの選択肢は多数あります; このチュートリアルでは Hatchling をデフォルトとして使っていますが、 メタデータの設定 のための [project] テーブルをサポートしてさえいれば setuptoolsFlitPDM やその他のツールでも同じように動作することでしょう。

注釈

ビルド・アップロード・インストールと並んでプロジェクト初期化やバージョン管理のような追加機能を伴ったコマンドラインインタフェースを提供する、もっと大きなツールの一部を構成するビルドバックエンドもあります。このチュートリアルでは、独立に動作する単一目的のツールを扱います。

pyproject.toml を開いて、これらの [build-system] テーブルのうちのひとつを入力してください:

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[build-system]
requires = ["setuptools>=61.0"]
build-backend = "setuptools.build_meta"
[build-system]
requires = ["flit_core>=3.4"]
build-backend = "flit_core.buildapi"
[build-system]
requires = ["pdm-backend"]
build-backend = "pdm.backend"
  • requires は、当該パッケージをビルドするために必要となるパッケージ群を列挙したものです。これらを自分でインストールする必要はなく; pip のようなビルドフロントエンドが、ビルド作業の一環として、一時的で隔離された仮想環境に自動的にインストールしてくれることでしょう。

  • build-backend は、そのフロントエンドがビルドを実行するにあたって使用するであろう Python オブジェクトの名前です。

メタデータを設定する

pyproject.toml を開いて、以下の内容を入力してください。 name の値はあなたのユーザ名に書き換えてください; こうすることで、このチュートリアルを履修している他の人たちがアップロードするパッケージと衝突を起こさない一意なパッケージ名を使っていることを保証することができます。

[project]
name = "example_package_YOUR_USERNAME_HERE"
version = "0.0.1"
authors = [
  { name="Example Author", email="author@example.com" },
]
description = "A small example package"
readme = "README.md"
requires-python = ">=3.7"
classifiers = [
    "Programming Language :: Python :: 3",
    "License :: OSI Approved :: MIT License",
    "Operating System :: OS Independent",
]

[project.urls]
"Homepage" = "https://github.com/pypa/sampleproject"
"Bug Tracker" = "https://github.com/pypa/sampleproject/issues"
  • name は、あなたのパッケージの 配布物の名前 です。これは、文字・数字・ ._- だけで構成されている限りは、どんな名前でも構いません。また、 PyPI 上に既に存在するものであってはなりません。このチュートリアルでは、あなたのユーザ名を使って更新していることを 確実にしてください 、というのは、そうすることで、既存の名前と同じ名前のパッケージのアップロードを試みることがないと保証できるからです。

  • version は、パッケージのバージョン番号です。バージョン番号についてもっと詳細に知りたい場合は、 バージョン指定子 を見てください。ビルドバックエンドの中には、ファイルから、もしくは、 git のタグからのように、別の方法で指定することを許容するものもあります。

  • authors は、パッケージの作者を識別するために使われます; 作者の一人一人について名前と電子メールアドレスを指定します。同じフォーマットで maintainers を列挙することもできます。

  • description は、1文で短くパッケージを説明するものです。

  • readme は、パッケージに関する詳細な説明を含んだファイルへのパスです。 PyPI 上のパッケージ詳細のページにこの内容が表示されます。この場合、説明の文言は README.md (これがよくあるパターンです) からロードされます。他にも、 プロジェクトのメタデータの仕様 に記述されているもっと先進的なテーブル形式があります。

  • requires-python は、そのプロジェクトがサポートしている Python のバージョンを与えます。 pip のようなインストーラは、 Python バージョンが合致するものまでパッケージのバージョンを遡って探索します。

  • classifiers は、インデックスと pip に、そのパッケージに関する追加的なメタデータをいくつか与えます。この場合には、当該パッケージは Python 3 でのみ動作し、 MIT ライセンスの下に従うものであり、 OS には依らず独立のものです。どのバージョンの Python 上でそのパッケージが動作するのか、どのライセンスに従うのか、どのオペレーティングシステムで動作するのかを示しておくことは、常に最低限それだけはやるべきことです。 classifiers の完全なリストについては、 https://pypi.org/classifiers/ を見てください。

  • urls には、 PyPI で表示するその他のリンクを幾つでも列挙しておくことができます。一般的に、ソースコードや説明文書、課題追跡システムその他へのリンクを挙げておけばよいでしょう。

[project] テーブルに定義できるこれらのフィールドやその他のフィールドについての詳しい情報が必要であれば、 プロジェクトにおけるメタデータの仕様 を見てください。他のよく使われるフィールドには、検索にかかりやすくするための keywords や、当該パッケージをインストールするために必須のパッケージを示す dependencies があります。

README.md を作成する

README.md を開いて、以下の内容を入力してください。そうしたければ、カスタマイズした内容でも構いません。

# Example Package

This is a simple example package. You can use
[Github-flavored Markdown](https://guides.github.com/features/mastering-markdown/)
to write your content.

LICENSE ファイルを作成する

Python パッケージインデックスにアップロードされた各々のパッケージにとって、ライセンス条項を明示することは重要です。こうすることで、そのパッケージをインストールするユーザに対して、どのような条件のもとでそのパッケージを使うことができるのかを伝えることができるからです。ライセンス選択の助けが必要ならば、 https://choosealicense.com/ を見てください。どのライセンスにするか選択できたら、 LICENSE ファイルを開いてそのライセンス条項を書き込んでください。例えば、 MIT ライセンスを選択したなら次のようにします:

Copyright (c) 2018 The Python Packaging Authority

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

ほとんどのビルドバックエンドは、パッケージ群のライセンスファイルを自動的に取り込みます。詳細については、あなたが使うバックエンドの説明文書を見てください。

その他のファイルを包含する

上に挙げたファイル群は、 ソースコード配布物 に自動的に含まれるでしょう。他のファイルも含めておきたいなら、使っているビルドバックエンドの説明文書を見てください。

配布物アーカイブを生成する

次のステップでは、そのパッケージの 配布物パッケージ を生成します。これらは、 Python パッケージインデックスへアップロードされ、 pip でインストールされることができるものです。

PyPA の ビルド の最新版がインストールされていることを確認してください:

python3 -m pip install --upgrade build
py -m pip install --upgrade build

ちなみに

これらをインストールするのに困難を感じるようなら、 パッケージをインストールする チュートリアルを見てください。

さて、 pyproject.toml ファイルがあるのと同じディレクトリでこのコマンドを実行しましょう:

python3 -m build
py -m build

このコマンドから多くのテキストメッセージが出力されますが、実行が終われば dist ディレクトリにふたつのファイルが生成されていることでしょう:

dist/
├── example_package_YOUR_USERNAME_HERE-0.0.1-py3-none-any.whl
└── example_package_YOUR_USERNAME_HERE-0.0.1.tar.gz

tar.gz ファイルは ソースコード配布物 であり、 .whl ファイルは ビルド済配布物 です。新しめのバージョンの pip では、ビルド済配布物を優先的にインストールしますが、必要であればソースコード配布物にフォールバックします。ソースコード配布物については常にアップロードするべきであり、あなたのプロジェクトが動作するはずのプラットフォーム向けのビルド済配布物についても準備するべきです。我々の例示のためのパッケージはすべてのプラットフォーム上の Python で動作するものなので、ビルド済配布物がひとつあれば十分です。

配布物アーカイブをアップロードする

ついにあなたのパッケージを Python パッケージインデックスへアップロードする時が来ました!

最初に実行しなければならないことは、試験や実験の場となることを意図してパッケージインデックスとは別のインスタンスとして立てられたインスタンスである TestPyPI にアカウントを登録することです。このチュートリアルのように、必ずしも実際のインデックスにアップロードしたいと思わない場合には、 (TestPI は) 最適です。アカウントを登録するためには、 https://test.pypi.org/account/register/ へ行って、そのページにあるステップを完了してください。パッケージをアップロードできるようになる前に、あなたの電子メールアドレスを検証することも必要になるでしょう。もっと詳しいことが知りたければ、 TestPyPI を使う を見てください。

あなたのプロジェクトを安全にアップロードするためには、 PyPI API トークン が必要になるでしょう。 https://test.pypi.org/manage/account/#api-tokens で、 "スコープ" として "アカウント全体" を指定して、ひとつ作成してください。 トークンをコピーして保存するまで、ページを閉じないでください — トークンは二度と表示されません。

ユーザ登録が済んだら、 twine で配布物パッケージをアップロードすることができます。Twine のインストールが必要ならこのようにしてください:

python3 -m pip install --upgrade twine
py -m pip install --upgrade twine

インストールできたら、 dist ディレクトリ内にあるアーカイブファイルをすべてアップとーどしてください:

python3 -m twine upload --repository testpypi dist/*
py -m twine upload --repository testpypi dist/*

ユーザ名とパスワードの入力を促されることでしょう。ユーザ名には __token__ を使ってください。パスワードには接頭子の pypi- を含めたトークンの値を使ってください。

コマンド実行完了後、これに似た出力を見ることになるでしょう:

Uploading distributions to https://test.pypi.org/legacy/
Enter your username: __token__
Uploading example_package_YOUR_USERNAME_HERE-0.0.1-py3-none-any.whl
100% ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 8.2/8.2 kB • 00:01 • ?
Uploading example_package_YOUR_USERNAME_HERE-0.0.1.tar.gz
100% ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 6.8/6.8 kB • 00:00 • ?

あなたのパッケージをアップロードしたら、 TestPyPI 上で閲覧できるようになります; 例えば: https://test.pypi.org/project/example_package_YOUR_USERNAME_HERE

新しくアップロードしたあなたのパッケージをインストールする

pip を使えば、あなたのパッケージをインストールして動作を検証することができます。 仮想環境 を作成して、 TestPyPI からあなたのパッケージをインストールしましょう:

python3 -m pip install --index-url https://test.pypi.org/simple/ --no-deps example-package-YOUR-USERNAME-HERE
py -m pip install --index-url https://test.pypi.org/simple/ --no-deps example-package-YOUR-USERNAME-HERE

パッケージ名の中にあなたのユーザ名を指定するのをお忘れなく!

pip でパッケージを TestPyPI からインストールできるはずで、その出力は何か次のようなものに見えるでしょう:

Collecting example-package-YOUR-USERNAME-HERE
  Downloading https://test-files.pythonhosted.org/packages/.../example_package_YOUR_USERNAME_HERE_0.0.1-py3-none-any.whl
Installing collected packages: example_package_YOUR_USERNAME_HERE
Successfully installed example_package_YOUR_USERNAME_HERE-0.0.1

注釈

この例では --index-url フラグを使って本番環境である PyPI の代わりに TestPyPI を指定しています。さらに、 --no-deps も指定しています。 TestPyPI には本番環境である PyPI と同じだけのパッケージが存在しないので、依存先となるパッケージを同時にインストールしようとしても、失敗するか、または、期待していたものとは異なる何かをインストールしてしまう恐れがあります。我々の例として使っているパッケージには依存先がないので、 TestPyPI を使う時には依存先をインストールしないように指定しておくことは良いやり方です。

パッケージをインポートしてみることで、正しくインストールされたかどうかを試験することができます。まだ仮想環境内にいることを確認してから Python を走らせましょう:

python3
py

そして、パッケージをインポートしましょう:

>>> from example_package_YOUR_USERNAME_HERE import example
>>> example.add_one(2)
3

次なる一歩

おめでとうございます、あなたは Python のプロジェクトをパッケージングし、配布することができました! ✨ 🍰 ✨

このチュートリアルではあなたのパッケージを Test PyPI に、つまり永続的なストレージではないところにアップロードする方法について示したということを忘れないでください。テストシステムは、時折パッケージやアカウントを削除します。 Test PyPI は、このチュートリアルのような試行や実験のために使うのがベストです。

実際のパッケージを Python パッケージインデックスへアップロードする準備ができたら、このチュートリアルでやったのと概ね同じことをやれば大丈夫ですが、次のような重要な違いがあります:

  • あなたのパッケージ用に覚えやすくて独特な名前を付けましょう。チュートリアルでやったようにあなたのユーザ名を追記する必要はありませんが、既存の名前は使えません。

  • https://pypi.org でアカウントを登録しましょう - これらはふたつの相異なるサーバであって、テストサーバ側のログイン情報はメインサーバに共有されてはいません。

  • あなたのパッケージをアップロードするのに twine upload dist/* を使い、本番環境の PyPI で登録したアカウントの認証情報を入力しましょう。今回は出荷状態のパッケージをアップロードしようとしているので、 --repository を指定する必要はありません; 指定しないことでパッケージはデフォルトの https://pypi.org/ へアップロードされるでしょう。

  • python3 -m pip install [your-package を使って、あなたのパッケージを本番環境の PyPI からインストールしましょう。

ここまで読み進めてきて、もっと Python でのパッケージングについて読みたいのであれば、次のようなものがあります:

  • hatchflitpdmpoetry のように、単一のコマンドラインインタフェースでプロジェクト管理もパッケージングもできるパッケージングツールについて検討しましょう。

  • ビルドツールの設定に関する背景情報や詳細情報については、 PEP 517PEP 518 を読みましょう。

  • バイナリ拡張をパッケージングする について読みましょう。