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

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

Tip

このチュートリアルに出てくるコマンドを実行したら問題が発生したという場合には、コマンドと結果出力をコピーして、 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 ファイル群を格納するディレクトリは、プロジェクト名と同じ名前であるべきです。こうすることで設定が簡単になり、また、パッケージをインストールするユーザから見てより明白になります。

Creating the file __init__.py is recommended because the existence of an __init__.py file allows users to import the directory as a regular package, even if (as is the case in this tutorial) __init__.py is empty. [1]

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

Choosing a build backend#

Tools like pip and ビルド do not actually convert your sources into a distribution package (like a wheel); that job is performed by a build backend. The build backend determines how your project will specify its configuration, including metadata (information about the project, for example, the name and tags that are displayed on PyPI) and input files. Build backends have different levels of functionality, such as whether they support building extension modules, and you should choose one that suits your needs and preferences.

You can choose from a number of backends; this tutorial uses Hatchling by default, but it will work identically with Setuptools, Flit, PDM, and others that support the [project] table for metadata.

注釈

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

The pyproject.toml tells build frontend tools like pip and ビルド which backend to use for your project. Below are some examples for common build backends, but check your backend's own documentation for more details.

[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"

The requires key is a list of packages that are needed to build your package. The frontend should install them automatically when building your package. Frontends usually run builds in isolated environments, so omitting dependencies here may cause build-time errors. This should always include your backend's package, and might have other build-time dependencies.

The build-backend key is the name of the Python object that frontends will use to perform the build.

Both of these values will be provided by the documentation for your build backend, or generated by its command line interface. There should be no need for you to customize these settings.

Additional configuration of the build tool will either be in a tool section of the pyproject.toml, or in a special file defined by the build tool. For example, when using setuptools as your build backend, additional configuration may be added to a setup.py or setup.cfg file, and specifying setuptools.build_meta in your build allows the tools to locate and use these automatically.

メタデータを設定する#

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.8"
classifiers = [
    "Programming Language :: Python :: 3",
    "License :: OSI Approved :: MIT License",
    "Operating System :: OS Independent",
]

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

  • version is the package version. (Some build backends allow it to be specified another way, such as from a file or Git tag.)

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

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

  • readme is a path to a file containing a detailed description of the package. This is shown on the package detail page on PyPI. In this case, the description is loaded from README.md (which is a common pattern). There also is a more advanced table form described in the pyproject.toml guide.

  • requires-python gives the versions of Python supported by your project. An installer like pip will look back through older versions of packages until it finds one that has a matching Python version.

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

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

See the pyproject.toml guide for details on these and other fields that can be defined in the [project] table. Other common fields are keywords to improve discoverability and the dependencies that are required to install your package.

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

Tip

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

さて、 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 でのパッケージングについて読みたいのであれば、次のようなものがあります:

  • Read about advanced configuration for your chosen build backend: Hatchling, setuptools, Flit, PDM.

  • Look at the guides on this site for more advanced practical information, or the discussions for explanations and background on specific topics.

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


Notes