プロジェクトのメタデータを宣言する

PEP 621 では、パッケージング関連のツールが使用するために、あるプロジェクトの コアとなるメタデータpyproject.toml ファイルにどのように書けば良いかを指定しています。使われるファイルフォーマットの基準となる仕様として、以下のようなものを定義しています。

仕様

メタデータにはふたつの種類があります: 静的 なものと 動的 なものです。静的なメタデータは pyproject.toml ファイルで直接指定されていて、ツール側では指定したり変更したりできません (これは、例えばメタデータが参照するファイルの内容のような、メタデータによって 参照 されるデータを含みます)。動的なメタデータは dynamic キー (この仕様内で後で定義します) を経由して一覧化されていて、ツール側が後から提供することになるでしょう。

この仕様で定義されるキーは、 pyproject.toml ファイルの中の [project] という名前のテーブルに収容されていなければなりません。いかなるツールもこのテーブルにこの仕様で定義されていないキーを追加してはなりません。自身の設定を pyproject.toml ファイルに記録しておきたいと願うツールは、 ビルド時の依存関係を宣言するための仕様 で定義されている通りに [tool] テーブルを使うことができます。 [project] テーブルが欠落している場合は、ビルド用のバックエンドがすべてのキーを動的に提供するであろうということを暗黙理に意味しています。

必ず静的に定義しなければならない必須のキーは次の通り:

  • 名称

必須フィールドだが、静的に指定しても動的に指定しても いずれでも構わない キーは以下の通り:

  • version

他の全てのキーは必須ではないものと解釈され、これらは静的に指定しても動的にリストしても未指定のままにしていても構いません。

[project] テーブルで許容されるキーの完全なリストは次のとおりです:

  • 著者 <authors>

  • 分類詞 <classifiers>

  • 依存関係 <dependencies

  • 説明

  • dynamic

  • entry-points

  • gui スクリプト <gui-scripts>

  • keywords

  • ライセンス

  • 保守者 <maintainers>

  • 名称

  • optional-dependencies

  • readme

  • requires-python

  • scripts

  • urls

  • version

名称

プロジェクトの名前。

内部的な一貫性を保つために、ツール側では読み取ったらすぐに、この名前を 正規化 するべきです。

[project]
name = "spam"

version

PEP 440 でサポートされた通りのプロジェクトのバージョン。

ユーザは正規化済みのバージョンを指定するようにするべきです。

[project]
version = "2020.0.0"

説明

プロジェクトを要約する記述。

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

readme

プロジェクトの説明全体 (すなわち README)。

このキーは文字列かテーブルを受け付けます。もし文字列なら、完全な説明を含むテキストファイルの位置を pyproject.toml からの相対パスで示したものです。ツールの側ではこのファイルが UTF-8 でエンコードされているものと想定しなければなりません。ファイルパスが大文字小文字を問わず .md 拡張子で終わっている場合は、ツールはそのファイルの content-type が text/markdown であるものと仮定しなければなりません。ファイルパスが大文字小文字を問わず .rst で終わっている場合は、ツールは content-type が text/x-rst であるものと仮定しなければなりません。この PEP で指定するよりも多くの拡張子をツールが認識するなら、そのようなツールは、このキーを dynamic であると指定していなくても、ユーザのために content-type を推測しても構いません。content-type が与えられていない場合には、全ての認識できない拡張子についてツールはエラーを発生させなければなりません。

readme キーはその値がテーブルでも構いません。 file キーは、完全な説明を含むファイルへの pyproject.toml ファイルからの相対パスを表現する文字列を値として持ちます。 text キーは、完全な説明そのものである文字列を値に持ちます。これらのキーは排他的にいずれかひとつしか使えないので、もしメタデータがこれら両方のキーを同時に指定していたらツールはエラーを発生させなければなりません。

readme キーに指定されたテーブルには、完全な説明の content-type を指定する文字列を値とする content-type キーも含まれています。メタデータがこのキーをテーブルの中で指定していない場合には、ツールはエラーを発生させなければなりません。メタデータで charset パラメータが指定されていない場合には、 UTF-8 であるものと想定されます。ツールは各ツールが独自に選択した他のエンコーディングをサポートしても構いません。 コアとなるメタデータ によってサポートされている content-type に変換することができるのであれば、ツールはそのような代替 content-type をサポートしても構いません。そうでなければ、ツールはサポートしていない content-type に対してエラーを発生させなければなりません。

[project]
# A single pyproject.toml file can only have one of the following.
readme = "README.md"
readme = "README.rst"
readme = {file = "README.txt", content-type = "text/markdown"}

requires-python

プロジェクトが要求する Python のバージョン。

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

ライセンス

テーブルには二つのキーのうちのいずれか一つを書くことができます。 file キーは、 pyproject.toml からプロジェクトのライセンス情報を含むファイルへの相対パスを値とする文字列です。ツールの側では、そのファイルのエンコーディングが UTF-8 であるものと仮定しなければなりません。 text キーは、プロジェクトのライセンス条項そのものである文字列を値に取ります。これらのキーは相互に排他的で、従って、両方のキーが指定されているメタデータについてツールの側ではエラーを発生させなければなりません。

[project]
# A single pyproject.toml file can only have one of the following.
license = {file = "LICENSE"}
license = {text = "MIT License"}

authors/maintainers

プロジェクトの "作者" であると考えられる人々ないし組織。正確な意味はさまざまに解釈可能です — 元々のまたは主要な作者でも構わないし、現在の保守者やパッケージのオーナでも構いません。

"maintainers" キーは "authors" キーに似ていて、その正確な意味はさまざまに解釈可能です。

これらのキーは、 nameemail のふたつのキーを伴ったテーブルの配列を受け入れます。両方の値は文字列でなければなりません。 name の値は、電子メールアドレスにおける正当な名前 (すなわち、 RFC 822 における電子メールアドレスのアドレス部分に前置できる名前なら何でも可) で、コンマを含まないものでなければなりません。 email の値は、正当な電子メールアドレスでなければなりません。これらのキーは共に必須ではありませんが、少なくともいずれかのキーがテーブル内で指定されていなければなりません。

データを使って コアとなるメタデータ に書き込むやり方は次の通りです:

  1. name だけが与えられた場合には、その値を Author なり Maintainer なりに書き込みます。

  2. email だけの場合には、その値を Author-email なり Maintainer-email なりに書き込みます。

  3. emailname の両方が与えられた場合には、 {name} <{email}> のフォーマットで Author-email なり Maintainer-email なりに書き込みます。

  4. 複数の値がある場合はコンマで区切るべきです。

[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@python.org"}
]

keywords

プロジェクトに関するキーワード。

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

分類詞 <classifiers>

プロジェクトに適合する Trove 分類子。

classifiers = [
  "Development Status :: 4 - Beta",
  "Programming Language :: Python"
]

urls

URL のテーブルで、 URL に付けられたラベルがキーで URL そのものが値になっているもの。

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

エントリポイント

みっつのテーブルがエントリポイントに関係しています。 [project.scripts] テーブルは、 エントリポイント仕様 の中の console_scripts グループに対応しています。テーブル内のキーはエントリポイントの名前であり、値は参照されるオブジェクトです。

[project.gui-scripts] テーブルは、 エントリポイント仕様 の中の gui_scripts グループに対応します。そのフォーマットは [project.scripts] と同じです。

[project.entry-points] テーブルは、テーブルの集合体です。それぞれのサブテーブルの名前は、ひとつのエントリポイントグループです。キーと値の意味するところは [project.scripts] と同じです。ユーザはネストしたサブテーブルを作ってはならず、代わりにエントリポイントグループを1段階の深さに保つようにしなければなりません。

メタデータの中に [project.entry-points.console_scripts] もしくは [project.entry-points.gui_scripts] というテーブルが定義されている場合は、それぞれ [project.scripts][project.gui-scripts] と混同してしまうといけないので、ビルド時のバックエンドがエラーを発生させなければなりません。

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

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

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

dependencies/optional-dependencies

(必須ではない) プロジェクトの依存関係。

dependencies には、文字列の配列が値であるようなキーです。それぞれの文字列がそのプロジェクトの依存関係を表現していて、正当な PEP 508 の文字列としてフォーマットされていなければなりません。それぞれの文字列は、 Requires-Dist エントリに直接にマップしています。

optional-dependencies は、それぞれのキーが追加物で、その値が文字列の配列であるようなテーブルです。文字列の配列は正当な PEP 508 の文字列でなければなりません。キーは Provides-Extra としてみた時に正当な値でなければなりません。従って、配列の中のそれぞれの値は、一致する Provides-Extra メタデータに対応する Requires-Dist のエントリになります。

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

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

dynamic

この PEP に列挙されたキーのどれを意図的に指定しないままにすることで他のツールが動的にそのようなメタデータを準備することができる/しようとするかを規定します。後述するツールによる設定に比較して、どのメタデータが目的を持って未指定にされていて未指定のままであることを期待されているのかについて明確に描き出します。

  • ビルド用のバックエンドは、静的に指定されたメタデータ (つまり dynamic 内に列挙されたキーではないメタデータ) を尊重しなければなりません。

  • メタデータで dynamic 内に name が指定されている場合には、ビルド用バックエンドがエラーを発生させなければなりません。

  • コアとなるメタデータ の仕様において、あるキーが "必須である" ものとして挙げられている場合には、メタデータはそのキーを静的に指定するか、または、 dynamic 内に指定するかしなければなりません (どちらでもない場合にはビルドバックエンドがエラーを発生させなければなりません、すなわち、必須のフィールドが [project] テーブルの中にどんな形でも存在していないということは不可能であるべきです)。

  • コアとなるメタデータ の仕様で、あるキーを "必須ではない" ものとして挙げている場合には、後でビルド用バックエンドがそのキー用のデータを提供するという期待が持てるのであればメタデータではそのキーを dynamic の中に挙げても構いません。

  • メタデータ内で、あるキーが静的に指定されていて、かつ、 dynamic にも挙げてある場合には、ビルド用バックエンドはエラーを発生させなければなりません。

  • メタデータ内で、あるキーを dynamic の中に挙げなかった場合は、ビルド用バックエンドがユーザに代わって必要なメタデータを挿入することはできません (すなわち、ツールがメタデータを挿入できるのは dynamic の中だけであり、かつ、ユーザがそうするようにオプトインしていなければならないということです) 。

  • あるキーが dynamic の中で指定されたメタデータで、しかし、ビルド用バックエンドがそこに挿入するべきデータを決定することができない時は、ビルド用バックエンドはエラーを発生させなければなりません (正確な値であると判断した場合はデータを省略することが許容されます) 。

dynamic = ["version", "description", "optional-dependencies"]

[project]
name = "spam"
version = "2020.0.0"
description = "Lovely Spam! Wonderful Spam!"
readme = "README.rst"
requires-python = ">=3.8"
license = {file = "LICENSE.txt"}
keywords = ["egg", "bacon", "sausage", "tomatoes", "Lobster Thermidor"]
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@python.org"}
]
classifiers = [
  "Development Status :: 4 - Beta",
  "Programming Language :: Python"
]

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

# dynamic = ["version", "description"]

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