pyproject.toml を書く#

pyproject.toml は、lintersやtype checkersやその他のパッケージングツール群でも同様に使われる設定ファイルです。このファイルの中には3個の TOML テーブルを置くことが可能です。

  • [build-system] テーブルは、 強く推奨されています 。これを使うことで、どの ビルドバックエンド を使うのか、また、そのプロジェクトをビルドするためには他のどんな依存関係が必要なのかを宣言することができます。

  • [project] テーブルは、依存関係や作者の名前など、そのプロジェクトの基本的なメタデータを指定するためにほとんどのビルドバックエンドが用いるものです。

  • [tool] テーブルは、例えば [tool.hatch][tool.black][tool.mypy] のような、ツールに特化したサブテーブルを持っています。テーブルの内容がツール毎に定義されているので、ここでは簡単に触れるだけに留めます。何を含めておくことができるのかを知るためには、それぞれのツールの説明文書に当たってみてください。

注釈

[build-system][project] の二つのテーブルの間には、大きな差異があります。どんなビルドバックエンドを使うのかに関わりなく前者は常に存在していなければなりません (というのも、どのツールを使うのかをこのテーブルで定義するからです)。後者は、 ほとんどの ビルドバックエンドが理解しますが、一部のバックエンドは異なったフォーマットのものを用います。

これを書いている時点 (2023年11月) では、 [project] テーブルを使わないビルドバックエンドとしては、 Poetry が有名です (代わりに [tool.poetry] テーブルを使います) 。

また、setuptools のビルドバックエンドは、 [project] テーブルと setup.cfg ないし setup.py の中のより古い形の両方をサポートしています。新しく始めるプロジェクトでは [project] テーブルを使用し、 (C 言語拡張をビルドする場合のような) 何かプログラム面での設定が必要とされる場合に限って setup.py を使い続けることが推奨されていますが、依然として setup.cfgsetup.py の形式は正当なものです。 setup.py は非推奨になりましたか? をご覧ください。

ビルドバックエンドを宣言する#

[build-system] テーブルは、使用されるべきビルドバックエンドを指定する build-backend キーを含んでいます。また、 プロジェクトをビルドするのに必要な依存関係のリストである requires キーも含んでいます -- これは典型的にはビルドバクエンドのパッケージだけですが、追加の依存関係を含んでいてもかまいません。さらに、例えば requires = ["setuptools >= 61.0"] のようにバージョンを制限することもできます。

通常は、ビルドバックエンドの説明文書が (ビルドバックエンドを選択する) 示唆するものをコピーするだけでしょう。よく知られたバックエンドでの値をいくつか挙げておきます:

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

静的なメタデータと動的なメタデータ#

このガイド文書の残りの部分は、[project] テーブルに捧げます。

ほとんどの場合、 [project] フィールドに直接に値を書き込むことになるでしょう。例えば: requires-python = ">= 3.8"version = "1.0" などです。

しかしながら、ビルドバックエンドにメタデータを計算させる方が役に立つ場合もあります。例えば: 多くのビルドバックエンドがソースコードや Git タグやその他似たようなものの中にある __version__ 要素を読み取ることができます。そのような場合には、以下の例のような方法を使ってそのフィールドを動的なものにしておくべきです、

[project]
dynamic = ["version"]

フィールドが動的となっている場合、そこを埋めるのはビルドバックエンドの責任です。どうすれば良いのかについては、ビルドバックエンドの説明文書に当たってみてください。

基本的な情報#

名称#

PyPI 上でのプロジェクト名称を書いてください。このフィールドは必須のもので、動的であるとマークできない唯一のフィールドです。

[project]
name = "spam-eggs"

プロジェクトの名称は、ASCII文字・数字・アンダースコア "_"・ハイフン "-"・ピリオド "." だけでできていなければなりません。また、アンダースコア・ハイフン・ピリオドで始まってはなりません。

プロジェクト名の比較では、大文字小文字を区別せず、また、アンダースコア・ハイフン・ピリオドは何文字連続していても同じものとして扱います。例えば、あなたが cool-stuff という名前のプロジェクトを登録したなら、利用者がダウンロードしたり依存関係を宣言したりするのに、次に挙げる綴りのいずれであっても使うことができます: Cool-Stuffcool.stuffCOOL_STUFFCoOl__-.-__sTuFF

version#

プロジェクトのバージョンを書いてください。

[project]
version = "2020.0.0"

(アルファ版のリリース用の) 2020.0.0a1 のようなもっと込み入ったバージョン指定子も可能ですので、 詳細については 仕様 を見てください。

動的であるとマークされることがしばしばではありますが、このフィールドは必須のものです

[project]
dynamic = ["version"]

これによって、 __version__ 要素や Git のタグからバージョンを取得するような使い方が可能になります。 バージョンを1箇所で管理する を見てみてください。

依存関係と要求事項#

dependencies/optional-dependencies#

もしプロジェクトに依存関係があるなら、このように列挙してください:

[project]
dependencies = [
  "httpx",
  "gidgethub[httpx]>4.0.0",
  "django>2.1; os_name != 'nt'",
  "django>2.0; os_name == 'nt'",
]

バージョンに制限をかけるために使えるあらゆるシンタックスについては、 依存関係指定子 を参照してください。

パッケージの特定の機能にのみ必要なものであれば、依存関係のうちのいくつかをオプションにしたい時があるかもしれません。そのような場合には、 optional-dependencies に置きましょう。

[project.optional-dependencies]
gui = ["PyQt5"]
cli = [
  "rich",
  "click",
]

キーのぞれぞれが "余分のパッケージング <packaging extra>" を定義します。上の例では、プロジェクトを例えば GUI サポート付きでインストールするためには pip install your-project-name[gui] を使うことができます。

requires-python#

これによってサポートされている Python の上限のバージョンを宣言することができます [1]

[project]
requires-python = ">= 3.8"

実行可能なスクリプトを作成する#

パッケージの一部としてコマンドをインストールするためには、 [project.scripts] テーブルで宣言してください。

[project.scripts]
spam-cli = "spam:main_cli"

この例では、プロジェクトをインストールした後に spam-cli コマンドが利用できるようになるでしょう。このコマンドを実行することで、 from spam import main_cli; main_cli() と同等のことを行うでしょう。

Windows では、このやり方でパッケージされたスクリプト群はターミナルを必要としますので、グラフィカルなアプリケーションの内部から起動した場合にはターミナルがポップアップすることでしょう。これが起きないようにするためには、 [project.scripts] の代わりに [project.gui-scripts] テープルを使ってください。

[project.gui-scripts]
spam-gui = "spam:main_gui"

そうすることで、コマンドラインからスクリプトを起動してスクリプトがバックグラウンドで走らせることですぐに制御を取り戻すことができるでしょう。

Windows 上では、 [project.scripts][project.gui-scripts] の間の差異だけが実質的に効果を持ちます。

あなたのプロジェクトについて#

authors/maintainers#

これらのフィールドは両方とも名前とメールアドレスの両方またはいずれかで識別される人々のリストを含みます。

[project]
authors = [
  {name = "Pradyun Gedam", email = "pradyun@example.com"},
  {name = "Tzu-Ping Chung", email = "tzu-ping@example.com"},
  {name = "Another person"},
  {email = "different.person@example.com"},
]
maintainers = [
  {name = "Brett Cannon", email = "brett@example.com"}
]

説明 <description>#

これは、PyPI におけるプロジェクトページの "ヘッドライン" () や検索結果 () のような他の場所で表示されるものなので、1行でプロジェクトの説明をしたものであるべきです。

[project]
description = "Lovely Spam! Wonderful Spam!"

readme#

これはプロジェクトの長めの説明文で、PyPI のプロジェクトページに表示されます。典型的には、プロジェクトには README.mdREADME.rst のファイルが存在するものなので、その場合にはここにはそのファイル名だけを書いておけば良いでしょう。

[project]
readme = "README.md"

README のフォーマットは拡張から自動的に検出されます:

次のようにしてフォーマットを明示的に指定することもできます:

[project]
readme = {file = "README.txt", content-type = "text/markdown"}
# or
readme = {file = "README.txt", content-type = "text/x-rst"}

ライセンス#

これは、ふたつの形式を採ります。典型的には LICENSELICENSE.txt というファイル名でライセンスをファイルとして置いておいて、ここにはそのファイルへのリンクを書いて置くこともできます:

[project]
license = {file = "LICENSE"}

あるいは、ライセンスの名称を書いておくことも可能です:

[project]
license = {text = "MIT License"}

標準的でよく知られたライセンスを使うのであれば、このフィールドを使う必要はありません。代わりに、 License:: で始まる 分類子 のうちのひとつを書いておけば良いでしょう。 (一般的な規則としては標準的でよく知られたライセンスを使うことは良い考えです、というのは、混乱を避けるとことにもなるし、中には内部で認可済みのライセンスでなければソフトウェアを使えない組織もあるからです。)

keywords#

これは、 PyPI 上の検索でキーワードが与えられた時にこのプロジェクトをサジェストするのを助けます。

[project]
keywords = ["egg", "bacon", "sausage", "tomatoes", "Lobster Thermidor"]

分類詞 <classifiers>#

プロジェクトに合致する PyPI 分類子 <classifier> のリスト。 可能な分類子のリスト を参照してください。

classifiers = [
  # How mature is this project? Common values are
  #   3 - Alpha
  #   4 - Beta
  #   5 - Production/Stable
  "Development Status :: 4 - Beta",

  # Indicate who your project is intended for
  "Intended Audience :: Developers",
  "Topic :: Software Development :: Build Tools",

  # Pick your license as you wish (see also "license" above)
  "License :: OSI Approved :: MIT License",

  # Specify the Python versions you support here.
  "Programming Language :: Python :: 3",
  "Programming Language :: Python :: 3.6",
  "Programming Language :: Python :: 3.7",
  "Programming Language :: Python :: 3.8",
  "Programming Language :: Python :: 3.9",
]

あるプロジェクトが Python のどのバージョンをサポートしているのかを宣言するために分類子 <classifiers> がよく使われますが、この情報は PyPI 上のプロジェクトを検索し閲覧するためにのみ使われるのであって、プロジェクトをインストールするためには使われません。あるプロジェクトが Python のどのバージョンでならインストールされ得るのかを実際に制約するためには、 requires-python 引数を使ってください。

パッケージが PyPI にアップロードされるのを防ぐには、 Private :: Do Not Upload 分類子を使いましょう。Private :: で始まる分類子を付けられたパッケージを PyPI は常に拒否します。

urls#

プロジェクトに関連のある URL のリストで、PyPI 上のプロジェクトページの左サイドバーに表示されるもの。

[project.urls]
Homepage = "https://example.com"
Documentation = "https://readthedocs.org"
Repository = "https://github.com/me/spam.git"
Issues = "https://github.com/me/spam/issues"
Changelog = "https://github.com/me/spam/blob/master/CHANGELOG.md"

もしキーが空白文字を含んでいるなら、例えば Website = "https://example.com" ではなくて "Official Website" = "https://example.com" のように引用符で囲む必要があります。

先進的なプラグイン#

パッケージの中にはプラグインを使って拡張できるものがあります。例としては、PytestPygments が挙げられます。そのようなプラグインを作成するためには、以下のように [project.entry-points] のサブテーブルで宣言する必要があります:

[project.entry-points."spam.magical"]
tomatoes = "spam:main_tomatoes"

詳しくは、 プラグインガイド を見てください。

完全な例#

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[project]
name = "spam-eggs"
version = "2020.0.0"
dependencies = [
  "httpx",
  "gidgethub[httpx]>4.0.0",
  "django>2.1; os_name != 'nt'",
  "django>2.0; os_name == 'nt'",
]
requires-python = ">=3.8"
authors = [
  {name = "Pradyun Gedam", email = "pradyun@example.com"},
  {name = "Tzu-Ping Chung", email = "tzu-ping@example.com"},
  {name = "Another person"},
  {email = "different.person@example.com"},
]
maintainers = [
  {name = "Brett Cannon", email = "brett@example.com"}
]
description = "Lovely Spam! Wonderful Spam!"
readme = "README.rst"
license = {file = "LICENSE.txt"}
keywords = ["egg", "bacon", "sausage", "tomatoes", "Lobster Thermidor"]
classifiers = [
  "Development Status :: 4 - Beta",
  "Programming Language :: Python"
]

[project.optional-dependencies]
gui = ["PyQt5"]
cli = [
  "rich",
  "click",
]

[project.urls]
Homepage = "https://example.com"
Documentation = "https://readthedocs.org"
Repository = "https://github.com/me/spam.git"
"Bug Tracker" = "https://github.com/me/spam/issues"
Changelog = "https://github.com/me/spam/blob/master/CHANGELOG.md"

[project.scripts]
spam-cli = "spam:main_cli"

[project.gui-scripts]
spam-gui = "spam:main_gui"

[project.entry-points."spam.magical"]
tomatoes = "spam:main_tomatoes"