依存関係指定子

この文書では、他のプロジェクトへの依存関係を指定するのに使われていた書式を定義します。定義された言語は、簡潔な行ベースの書式で、元々は pip の requirements ファイルで使われていた書式から転用されました。

依存関係 <dependency> の任務は、 pip [1] のようなツールがインストールするべき正しいパッケージを探し出すことができるようにすることです。これは時には大変に曖昧で名称を指定するだけであったり、別の時には非常に限定的でインストールするべき特定のファイルを参照したりします。場合によっては、依存関係 <dependency> がひとつのプラットフォームでのみ妥当であったり、いくつかのバージョンだけが受け入れ可能であったりするので、(依存関係 <dependency> を記述する) 言語としてはこれらすべてのケースを記述できるものでなければなりません。

依存関係指定子を処理するにあたってツール類が厳密であるべきか寛容であるべきかは、より大きなエコシステムの中でそのツールが担う役割に大きく依存します。

  • 公開を行うツール類やインデックスサーバは、時間経過に伴って公開された指定子が首尾一貫したものになるように、それらが新リリースを処理する際には厳密であるべきです

  • ロッキングとインストール用のツール類は、まず間違いなく道理にかなっていないような依存関係指定子を含むかもしれない古めのパッケージ群の利用を許して、その処理において寛容でもかまいません。

仕様

例

この言語のすべての機能を、名前に基づいた参照とともに示します:

requests [security,tests] >= 2.8.1, == 2.8.* ; python_version < "3.7"

最低限の URL に基づいた参照:

pip @ https://github.com/pypa/pip/archive/1.3.1.zip#sha1=da9234ee9982d4bbb3c72346a6de940a148ea686

概念

依存関係の指定では、常に、配布物の名前を指定します。名前で指定された配布物で特定の追加機能を有効にするように依存関係を拡張するような追加物 <extra> を含んでいても構いません。インストールされたバージョンをバージョンリミットで制御することもできますし、特定のアーティファクトをインストールするために URL を与えることもできます。依存関係は最終的に環境マーカを用いて条件別に作成することもできます。

文法

最初に文法について簡単に触れた後、それぞれの節の意味論 <semantics> について深く掘り下げることにしましょう。

配布物の仕様は ASCII テキストで書かれています。厳密な文法としては parsley [2] の文法を使っています。この仕様は、コメントや継続による複数行サポートやその他の機能の枠組みを与えるもっと大きなシステムの中に組み込まれることを期待しています。

役に立つ構文解析ツリーを構成するための注釈機能を含む完全な文法は、この説明文書の末尾に置きました。

バージョン指定子仕様 の規則に従ってバージョンを指定しても構いません。(ノート: URI は std-66 で定義されています):

version_cmp   = wsp* '<=' | '<' | '!=' | '===' | '==' | '>=' | '>' | '~='
version       = wsp* ( letterOrDigit | '-' | '_' | '.' | '*' | '+' | '!' )+
version_one   = version_cmp version wsp*
version_many  = version_one (',' version_one)* (',' wsp*)?
versionspec   = ( '(' version_many ')' ) | version_many
urlspec       = '@' wsp* <URI_reference>

環境マーカを使うことで、ある仕様が特定の環境でのみ有効であることを示すことができます:

marker_op     = version_cmp | (wsp+ 'in' wsp+) | (wsp+ 'not' wsp+ 'in' wsp+)
python_str_c  = (wsp | letter | digit | '(' | ')' | '.' | '{' | '}' |
                 '-' | '_' | '*' | '#' | ':' | ';' | ',' | '/' | '?' |
                 '[' | ']' | '!' | '~' | '`' | '@' | '$' | '%' | '^' |
                 '&' | '=' | '+' | '|' | '<' | '>' )
dquote        = '"'
squote        = '\\''
python_str    = (squote (python_str_c | dquote)* squote |
                 dquote (python_str_c | squote)* dquote)
env_var       = ('python_version' | 'python_full_version' |
                 'os_name' | 'sys_platform' | 'platform_release' |
                 'platform_system' | 'platform_version' |
                 'platform_machine' | 'platform_python_implementation' |
                 'implementation_name' | 'implementation_version' |
                 'extra' | 'extras' | 'dependency_groups' # ONLY when defined by a containing layer
                 )
marker_var    = wsp* (env_var | python_str)
marker_expr   = marker_var marker_op marker_var
              | wsp* '(' marker wsp* ')'
marker_and    = marker_expr wsp* 'and' marker_expr
              | marker_expr
marker_or     = marker_and wsp* 'or' marker_and
                  | marker_and
marker        = marker_or
quoted_marker = ';' wsp* marker

配布物のうちの必須ではない部分については extras フィールドを使って指定することができます:

identifier_end = letterOrDigit | (('-' | '_' | '.' )* letterOrDigit)
identifier    = letterOrDigit identifier_end*
name          = identifier
extras_list   = identifier (wsp* ',' wsp* identifier)*
extras        = '[' wsp* extras_list? wsp* ']'

私たちに名前に基づいた要求仕様を与える:

name_req      = name wsp* extras? wsp* versionspec? wsp* quoted_marker?

そして、直接参照に用いる要求仕様のための規則はこちら:

url_req       = name wsp* extras? wsp* urlspec (wsp+ quoted_marker?)?

依存関係を指定することができる統一規則への案内はこちら:

specification = wsp* ( url_req | name_req ) wsp*

空白文字 <Whitespace>

行を分割するものではない空白文字には特に意味はなく、ほとんどの場合には必須ではないものです。数少ない例外は、 URL による要求事項の末尾を検出するためのものと、環境マーカ内で内部ユーザが付与する定数です。

名前 <Names>

配布物の名称は コアとなるメタデータ の中で定義されています。名称は、配布物にとっての主要な識別子として振る舞います。

正当な配布物名称は、 名称フォーマット仕様 の中で定義されています。

追加物 <Extras>

追加物とは、配布物の必須ではない部分のことです。配布物では追加物を幾つでも指定することができ、追加物が依存関係の指定場所で使われた 場合 には、それぞれの追加物が配布物の追加的な依存関係を宣言する結果になります。例えば:

requests[security,tests]

追加物 <extras> の依存関係の合併とは、その追加物が添付されている配布物 <distribution> の依存関係と一緒に定義されることです。上に示した例では、結果として requests がインストールされることになり、requests は自身の依存関係を持つので requests の "security" 追加物 <extra> に列挙されたすべての依存関係 (先) もインストールされることになります。

複数の追加物 <extra> が列挙されている場合には、すべての依存関係の合併集合が依存関係になります。

追加物の名称に関する制約事項は、 コアとなるメタデータ仕様 で定義されています。

バージョン指定子

バージョン番号やその比較方法について、詳しくは バージョン指定子仕様 をみてください。バージョン仕様は、配布物のバージョンとして使うことができる範囲を定めています。これは、名前によって参照される配布物にのみ適用されるのであって、URL を通じて指定されるものには該当しません。バージョン番号の比較は、また、環境マーカにおいても使われます。バージョンの周囲にある必須でない括弧は PEP 345 との互換性を保つために存在していますが、そのようなものを生成すべきではなくて受容するだけにとどめるべきです。

環境マーカ

環境マーカは、依存関係の指定においてその依存関係がいつ使われるべきであるかを記述する規則を提供します。例えば、Windows 上で走らせる時に pywin32 を必要とするあるパッケージを考えてみましょう。これは次のように表現することができます:

pywin32; sys_platform == "win32"

マーカ表現は、ある具体的なデプロイメント環境で評価されると真か偽に帰着します。偽と評価された場合には、その依存関係は無視されるべきです。

マーカ言語は Python そのものに触発されたもので、セキュリティ上の脆弱性になりかねない任意コードの実行を伴わずに安全に評価を行うことができるので選ばれました。

マーカは、 PEP 345 で初めて定義され、 PEP 508 で公式に仕様化され、その後は時間の経過とともに改正されました (PEP 508 以降の修正点は、 この仕様の末尾 に記録されています。

マーカフィールドのタイプ

環境マーカーは、以下のタイプのうちのいずれかであるものとしてそれぞれ定義されています:

  • String: このフィールドの内容は、常に内部の詳細を隠蔽した文字列として取り扱われます。

  • Set of strings: このフィールドの内容は、常に内部の詳細を隠蔽した文字列として取り扱われます。対照的に、ユーザが提供した定数は、 (セットリテラル <set literals> はマーカの文法の一部ではないので) 単一の文字列でなければなりません。

  • Version: このフィールドの内容は、常に、正当な バージョン指定子 であることが期待されています。そうでない場合には、公開用のツール類はエラーを発出するべきですが、インストールツール類はこのフィールドを文字列のフィールドとして取り扱うようにフォールバックしても構いません。

  • Version | String: このフィールドの内容は、いくつかのプラットフォームでは正当な バージョン指定子 であることが期待されていますが、他では詳細を隠蔽された文字列であることが期待されています。この区別の詳細はフィールド依存であり、ツール類が実際に区別を行うか否かはツールに依存します。

マーカー比較

マーカ比較表現は、すべて、名指しされたマーカフィールドとユーザが提供した定数との比較であることが期待されます。比較のタイプは、使われた比較演算子によって決定され、名指しされたフィールド

follow 比較操作は、マーカ表現の文法の中で定義されています:

  • == (例えば、 sys_platform == "win32")

  • != (例えば、 sys_platform != "win32")

  • > (例えば、 python_version > "3.10")

  • >= (例えば、 python_version >= "3.10")

  • < (例えば、 python_version < "3.10")

  • <= (例えば、 python_version <= "3.10")

  • ~= (例えば、 python_version ~= "3")

  • === (例えば、, implementation_version === "not.a.valid.version")

  • in (例えば、 "gui" in extras, "SMP" in platform_version)

  • not in (例えば、 "dev" not in dependency_groups)

String フィールド向けには、それらが (大文字小文字を区別し、いかなる種類の値の標準化もない) Python 文字列なので、 == ・ != ・ in ・ not in が定義されています。文字列フィールドで ~= や === を使うことは、明示的に非推奨になっていて、公開用ツール類はエラーを発出するべきであり、インデックスサーバ類はそのような環境マーカを含むアップロードを許可しなくても構いませんが、ロッキングとインストールツール類はその代わりにそれらを == と同一のものであると翻訳しても構いません。文字列フィールドで順序比較 (< ・ <= ・ > ・ >=) を使うことは、 (というのは、パッケージングの文脈において意味論的に意味をなさないので) 明示的に非推奨になっており、公開用ツール類はエラーを発出するべきであり、インデックスサーバ類はそのような環境マーカを含むアップロードを許可しなくても構いませんが、ロッキングとインストールツール類は以下のような動作を実装するべきです:

  • >= と <= を == と同等のものとして取り扱う

  • > と < を常に偽 <False> として取り扱う

Set of String フィールド向けには、セットリテラル用のマーカ文法が存在しないことから、正当な操作は、ユーザが与えた文字列リテラルを左側オペランドとする in と not in の比較だけです。環境マーカが他のいかなる比較演算をこれらのフィールドに対して試みるなら公開ツール類はエラーを発出するべきであり、インデックスサーバ類はそのような環境マーカを含むアップロードを許可しなくても構わないが、ロッキングとインストールツール類はそのような操作を常に偽として取り扱うべきです。

Version フィールド向けには、マーカフィールドのアタとユーザが提供した定数の両方が正当なバージョン指定子としてパースできる場合か、=== 任意的等価演算子が使われた場合かのいずれかの場合に、比較操作が バージョン指定子仕様 によって定義されています。 === 以外の演算子が用いられた時、ユーザが与えた定数が正当なバージョン指定子としてパースできなければ、公開ツール類はエラーを発出するべきであり、インデックスサーバ類はそのような環境マーカを含むアップロードを拒否しても構いませんが、ロッキングとインストールツール類は、マーカフィールドの値かユーザが与えた定数を正当なバージョン指定子としてパースできない場合に、エラーを発出するか、 String フィールドの比較論理にフォールバックするかのいずれかを行っても構いません。Version フィールドでは in や not in の包含関係チェックは正当なものではなく、公開ツール類はエラーを発出すべきであり、インデックスサーバ類はそのような環境マーカを含むアップロードを拒否しても構いませんが、ロッキングとインストールツール類はこれらを常に False として取り扱っても構いません。

Version | String フィールド向けには、 Version フィールド向けのものと同様に比較演算が定義されていますが、 in と not in の包含確認は String フィールド向けのものと同様に定義されています。しかしながら、

マーカ表現を構築する

and や or といった論理演算子を用いてもっと複雑なマーカ表現を構築することができます。 (すべての比較演算子がより高い優先順位を持っているので) オペランドの優先順位を制御するために、必要に応じて括弧を使うこともできます。

例えば:

sys_platform == "ios" or sys_platform == "darwin"
sys_platform == "linux" and "SMP" in platform_version
sys_platform == "darwin" and platform_release >= "12"

(3.4 < python_version < 3.9 のような) Python の比較連結は、環境マーカではサポートされていません (代わりに、そのような表現は二つの個別の比較を and で連結したものとして書かなければなりません) 。

ユーザが与えた定数

ユーザが与えた定数は、常に、 ' または " のいずれかの引用符の中の文字列として与えられます。引用符3個の複数行文字列は許されていません。

ツール類がサポートすることは構いませんが、バックスラッシュによるエスケープは仕様化されていません。これらが複雑性を増すことや、内部構造のわからない文字列または正当なバージョン指定子以外の何ものかとしてユーザが与えた文字列を取り扱う必要性が現時点では知られていないことから、これらは仕様に含まれていません。

同様に、非 ASCII 文字のサポートも仕様化されていませんが、ツール類は (通常は、依存関係指定子を含むファイルまたはストリームのテキストエンコーディングに基づいて) それらを受け入れても構いません。典型的にはユーザが比較を行いたいと願う非 ASCII テキストが環境マーカランタイムの変数に含まれるという形で参照されるランタイムの変数がもっと一般的になれば、将来の時点で再検討されるかもしれません。

未知のマーカフィールド

未知のマーカフィールドへの参照は、インストール用のパッケージのバージョンを資格がないものにするか、または、 True か False かを評価する比較に帰着させるよりも固定された依存関係ツリーに含めるべきです。そういうことですので、未知のマーカフィールドを伴う公開パッケージのバージョンは、 (未知のマーカが False として取り扱われたなら) 明らかにインストールに成功したはずなのに依存関係が欠けているためにランタイムになってから失敗するか、 (未知のマーカが True として取り扱われたなら) 現状のプラットフォームにとって正当ではない依存関係によって潜在的に不可解なインストールが失敗するかに陥るよりも、依存関係を解決する際に無視されるか、または、記述的なインストール失敗を発出するかのいずれかになります。

特定の Python 実装で値を計算することができない変数は、 Version フィールドについては 0 として、その他のすべての変数については (Version | String フィールドを含めて) 空文字列として評価されるべきです。

定義済みの環境マーカフィールド

以下に特記のない限り、マーカ評価環境は、次のマーカフィールドをすべてサポートしていなければなりません:

マーカ <Marker>

Python 同等物

種類

値の例と備考

os_name

os.name

文字列

posix, java

sys_platform

sys.platform

文字列

linux, win32, darwin, java1.8.0_51 (プラットフォームに特有の依存関係を宣言する際、これが最もよく定義されたフィールドであることに注意してください)

platform_machine

platform.machine()

文字列

x86_64 ・ aarch64 ・ AMD64 ・ arm64 (この値がオペレーティングシステムによって提供されたものであるため、相異なるプラットフォームでは同じ CPU アーキテクチャでも異なる文字列を使うかもしれないことに留意してください)

platform_python_implementation

platform.python_implementation()

文字列

CPython ・ PyPY

platform_release

platform.release()

Version | String

3.14.1-x86_64-linode39, 14.5.0, 1.8.0_51 (例えば macOS/darwin 上では、正当なバージョンフィールドであるかもしれません)

platform_system

platform.system()

文字列

Linux, Windows, Java

platform_version

platform.version()

Version | String

#1 SMP Fri Apr 25 13:07:35 EDT 2014 Java HotSpot(TM) 64-Bit Server VM, 25.51-b03, Oracle Corporation Darwin Kernel Version 14.5.0: Wed Jul 29 02:18:53 PDT 2015; root:xnu-2782.40.9~2/RELEASE_X86_64 13 (iOS または Android 上での例のための、おそらく正当なバージョンフィールド)

python_version

'.'.join(platform.python_version_tuple()[:2])

バージョン

3.9, 3.15

python_full_version

platform.python_version()

バージョン

3.10.12, 3.15.0a1

implementation_name

sys.implementation.name

文字列

cpython, pypy

implementation_version

下方の定義を見てください

バージョン

3.10.12, 7.3.17 (順に、CPython と PyPy のための例)

extra

プロジェクトの依存関係のメタデータの中で、オプションの依存関係を示すために使われる。指定子を解釈するコンテクストによって定義される時を除いて、エラー。

特別 <Special> (下記参照)

toml (公開ツール類は、このフィールドの使用を許容するべきです)

extras

ロックファイルの中でオプションの公開依存関係を示すために使われる。指定子を解釈する文脈によって定義された場合を除けば、エラー。

文字列の集合

{"toml"} (公開ツールは、このフィールドの使用を許容するべきではありませんし、インデックスサーバはそのような環境マーカを含むアップロードを受け入れるべきではありません)

dependency_groups

ロックファイルの中でオプションのプロジェクト内部依存関係を示すために使われます。指定子を解釈する文脈で定義された場合を除けば、エラー。

文字列の集合

{"test"} (公開ツール類はこのフィールドの使用を許容するべきではなく、インデックスサーバはこのような環境マーカを含むアップロードを許可するべきではありません)

古めのロッキングとインストールツール類との後方互換性については、 extras と dependency_groups のフィールドは、現状では、 ロックファイル の中の packages.marker フィールドの中で使われる時だけが正当です。これらの比較においては、マーカ評価に使われる extras と dependency_groups の組は、ロックファイルからインストール時には、 その時点で選択されている extras と dependency_groups を参照するのであって、対応するトップレベルのロックファイルフィールド群に列挙されているフルセットの定義済み extras と dependency_groups を参照するのではありません。どの extras と dependency_groups をインストールするのかを選ぶインタフェースはツールに依存します。公開ツールは、その公開依存関係宣言メタデータの中の extras または dependency_groups フィールドへの参照をプロジェクトが試みるのであれば、エラーを発出するべきですし、インデックスサーバはこれらのフィールドを参照するアップロードを受け入れるべきではありません。ロックファイルの処理の外側では、マーカ評価環境は、これらのフィールドを定義する必要はありません。

The extra field is also special, as it expects set-like behaviour, but predates the addition of Set of strings as a defined marker field type. It only supports one operation, extra == "name", which is similar to "name" in extras. extra != "name" SHOULD NOT be used anymore and MAY be rejected or evaluated to false. For dependency marker evaluations, the set of extra names used for these comparisons is the full set of requested extras for that particular package, whether requested directly in a top level dependency declaration, or indirectly in a transitive dependency declaration. Other comparison operations on extra are not defined and publishing tools SHOULD emit an error, index servers MAY disallow uploads containing such environment markers, while locking and installation tools SHOULD evaluate them as False.

より新しい extras フィールドとは異なり、このフィールドを用いる環境マーカは、公開ツールにもインデックスサーバにも受け入れられるべきです。プロジェクトの依存関係宣言向けであることを企図されたマーカ評価環境は extra フィールドの比較の評価を取り扱う必要がありませんが、環境マーカの他の評価は一般的にそのようにする必要がありません。レガシーな extra の比較の文法は、ロックファイルの packages.marker フィールドでは許されませんし、インストールツールはそのような比較を含むロックファイルを正当でないものとして拒否するべきです。

implementation_version マーカ変数は、 sys.implementation.version から派生したものです :

def format_full_version(info):
    version = '{0.major}.{0.minor}.{0.micro}'.format(info)
    kind = info.releaselevel
    if kind != 'final':
        version += kind[0] + str(info.serial)
    return version

if hasattr(sys, 'implementation'):
    implementation_version = format_full_version(sys.implementation.version)
else:
    implementation_version = "0"

完全な文法

完全な parsley 文法:

wsp           = ' ' | '\t'
version_cmp   = wsp* <'<=' | '<' | '!=' | '===' | '==' | '>=' | '>' | '~='>
version       = wsp* <( letterOrDigit | '-' | '_' | '.' | '*' | '+' | '!' )+>
version_one   = version_cmp:op version:v wsp* -> (op, v)
version_many  = version_one:v1 (',' version_one)*:v2 (',' wsp*)? -> [v1] + v2
versionspec   = ('(' version_many:v ')' ->v) | version_many
urlspec       = '@' wsp* <URI_reference>
marker_op     = version_cmp | (wsp* 'in') | (wsp* 'not' wsp+ 'in')
python_str_c  = (wsp | letter | digit | '(' | ')' | '.' | '{' | '}' |
                 '-' | '_' | '*' | '#' | ':' | ';' | ',' | '/' | '?' |
                 '[' | ']' | '!' | '~' | '`' | '@' | '$' | '%' | '^' |
                 '&' | '=' | '+' | '|' | '<' | '>' )
dquote        = '"'
squote        = '\\''
python_str    = (squote <(python_str_c | dquote)*>:s squote |
                 dquote <(python_str_c | squote)*>:s dquote) -> s
env_var       = ('python_version' | 'python_full_version' |
                 'os_name' | 'sys_platform' | 'platform_release' |
                 'platform_system' | 'platform_version' |
                 'platform_machine' | 'platform_python_implementation' |
                 'implementation_name' | 'implementation_version' |
                 'extra' | 'extras' | 'dependency_groups' # ONLY when defined by a containing layer
                 ):varname -> lookup(varname)
marker_var    = wsp* (env_var | python_str)
marker_expr   = marker_var:l marker_op:o marker_var:r -> (o, l, r)
              | wsp* '(' marker:m wsp* ')' -> m
marker_and    = marker_expr:l wsp* 'and' marker_expr:r -> ('and', l, r)
              | marker_expr:m -> m
marker_or     = marker_and:l wsp* 'or' marker_and:r -> ('or', l, r)
                  | marker_and:m -> m
marker        = marker_or
quoted_marker = ';' wsp* marker
identifier_end = letterOrDigit | (('-' | '_' | '.' )* letterOrDigit)
identifier    = < letterOrDigit identifier_end* >
name          = identifier
extras_list   = identifier:i (wsp* ',' wsp* identifier)*:ids -> [i] + ids
extras        = '[' wsp* extras_list?:e wsp* ']' -> e
name_req      = (name:n wsp* extras?:e wsp* versionspec?:v wsp* quoted_marker?:m
                 -> (n, e or [], v or [], m))
url_req       = (name:n wsp* extras?:e wsp* urlspec:v (wsp+ | end) quoted_marker?:m
                 -> (n, e or [], v, m))
specification = wsp* ( url_req | name_req ):s wsp* -> s
# The result is a tuple - name, list-of-extras,
# list-of-version-constraints-or-a-url, marker-ast or None


URI_reference = <URI | relative_ref>
URI           = scheme ':' hier_part ('?' query )? ( '#' fragment)?
hier_part     = ('//' authority path_abempty) | path_absolute | path_rootless | path_empty
absolute_URI  = scheme ':' hier_part ( '?' query )?
relative_ref  = relative_part ( '?' query )? ( '#' fragment )?
relative_part = '//' authority path_abempty | path_absolute | path_noscheme | path_empty
scheme        = letter ( letter | digit | '+' | '-' | '.')*
authority     = ( userinfo '@' )? host ( ':' port )?
userinfo      = ( unreserved | pct_encoded | sub_delims | ':')*
host          = IP_literal | IPv4address | reg_name
port          = digit*
IP_literal    = '[' ( IPv6address | IPvFuture) ']'
IPvFuture     = 'v' hexdig+ '.' ( unreserved | sub_delims | ':')+
IPv6address   = (
                  ( h16 ':'){6} ls32
                  | '::' ( h16 ':'){5} ls32
                  | ( h16 )?  '::' ( h16 ':'){4} ls32
                  | ( ( h16 ':')? h16 )? '::' ( h16 ':'){3} ls32
                  | ( ( h16 ':'){0,2} h16 )? '::' ( h16 ':'){2} ls32
                  | ( ( h16 ':'){0,3} h16 )? '::' h16 ':' ls32
                  | ( ( h16 ':'){0,4} h16 )? '::' ls32
                  | ( ( h16 ':'){0,5} h16 )? '::' h16
                  | ( ( h16 ':'){0,6} h16 )? '::' )
h16           = hexdig{1,4}
ls32          = ( h16 ':' h16) | IPv4address
IPv4address   = dec_octet '.' dec_octet '.' dec_octet '.' dec_octet
nz            = ~'0' digit
dec_octet     = (
                  digit # 0-9
                  | nz digit # 10-99
                  | '1' digit{2} # 100-199
                  | '2' ('0' | '1' | '2' | '3' | '4') digit # 200-249
                  | '25' ('0' | '1' | '2' | '3' | '4' | '5') )# %250-255
reg_name = ( unreserved | pct_encoded | sub_delims)*
path = (
        path_abempty # begins with '/' or is empty
        | path_absolute # begins with '/' but not '//'
        | path_noscheme # begins with a non-colon segment
        | path_rootless # begins with a segment
        | path_empty ) # zero characters
path_abempty  = ( '/' segment)*
path_absolute = '/' ( segment_nz ( '/' segment)* )?
path_noscheme = segment_nz_nc ( '/' segment)*
path_rootless = segment_nz ( '/' segment)*
path_empty    = pchar{0}
segment       = pchar*
segment_nz    = pchar+
segment_nz_nc = ( unreserved | pct_encoded | sub_delims | '@')+
                # non-zero-length segment without any colon ':'
pchar         = unreserved | pct_encoded | sub_delims | ':' | '@'
query         = ( pchar | '/' | '?')*
fragment      = ( pchar | '/' | '?')*
pct_encoded   = '%' hexdig
unreserved    = letter | digit | '-' | '.' | '_' | '~'
reserved      = gen_delims | sub_delims
gen_delims    = ':' | '/' | '?' | '#' | '(' | ')?' | '@'
sub_delims    = '!' | '$' | '&' | '\\'' | '(' | ')' | '*' | '+' | ',' | ';' | '='
hexdig        = digit | 'a' | 'A' | 'b' | 'B' | 'c' | 'C' | 'd' | 'D' | 'e' | 'E' | 'f' | 'F'

テストプログラム - もし grammar 文字列内に文法があれば:

import os
import sys
import platform

from parsley import makeGrammar

grammar = """
    wsp ...
    """
tests = [
    "A",
    "A.B-C_D",
    "aa",
    "name",
    "name<=1",
    "name>=3",
    "name>=3,",
    "name>=3,<2",
    "name@http://foo.com",
    "name [fred,bar] @ http://foo.com ; python_version=='2.7'",
    "name[quux, strange];python_version<'2.7' and platform_version=='2'",
    "name; os_name=='a' or os_name=='b'",
    # Should parse as (a and b) or c
    "name; os_name=='a' and os_name=='b' or os_name=='c'",
    # Overriding precedence -> a and (b or c)
    "name; os_name=='a' and (os_name=='b' or os_name=='c')",
    # should parse as a or (b and c)
    "name; os_name=='a' or os_name=='b' and os_name=='c'",
    # Overriding precedence -> (a or b) and c
    "name; (os_name=='a' or os_name=='b') and os_name=='c'",
    ]

def format_full_version(info):
    version = '{0.major}.{0.minor}.{0.micro}'.format(info)
    kind = info.releaselevel
    if kind != 'final':
        version += kind[0] + str(info.serial)
    return version

if hasattr(sys, 'implementation'):
    implementation_version = format_full_version(sys.implementation.version)
    implementation_name = sys.implementation.name
else:
    implementation_version = '0'
    implementation_name = ''
bindings = {
    'implementation_name': implementation_name,
    'implementation_version': implementation_version,
    'os_name': os.name,
    'platform_machine': platform.machine(),
    'platform_python_implementation': platform.python_implementation(),
    'platform_release': platform.release(),
    'platform_system': platform.system(),
    'platform_version': platform.version(),
    'python_full_version': platform.python_version(),
    'python_version': '.'.join(platform.python_version_tuple()[:2]),
    'sys_platform': sys.platform,
}

compiled = makeGrammar(grammar, {'lookup': bindings.__getitem__})
for test in tests:
    parsed = compiled(test).specification()
    print("%s -> %s" % (test, parsed))

歴史

  • 2015年11月: PEP 508 を通じてこの仕様が承認されました。

  • 2019年7月: python_version の定義は、Python の将来のバージョンが二桁のメジャーバージョンやマイナーバージョンを持つ場合 (例えば 3.10) でもそれを収容できるように、 platform.python_version()[:3] から '.'.join(platform.python_version_tuple()[:2]) へ 変更されました 。 [3]

  • 2022年3月: PEP 685 を通じて、 (コアとなるメタデータ 2.3 およびそれ以降の) 公開時点での余分 <extra> な名称の標準化を規格化しました。

  • 2024年6月: 2022年遅くから使われるようになった Python 実装の動作に合致するように、末尾のカンマを許容する形に version_many の定義が変更された。

  • 2025年4月: pylock.toml の仕様 に extras と dependency_groups のマーカフィールドを追加することが PEP 751 を通じて承認された。

  • 2025年8月: フィールドの仕様と合致させるために示唆されていた名称正当性確認用正規表現が決定された (以前は \Z の代わりに $ で終端していたので、誤って後続する行末コード <newlines> を許容していた)

  • 2025年12月: あらゆる意味での同一性 <Arbitrary equality> の比較がパースされることを許すために、 == より前に === を (評価することを) 文法上で確実にしました。

  • 2026年1月: 環境マーカの比較操作の定義をそれが意味をなすフィールドに対するバージョン番号の比較の意味を厳密にするために修正し、余分 <extra> な名称の制約をより明示的なものにし、順序付き比較の方法が文字列向けに定義されたものになるように調整し、バージョン番号のパースに失敗した時にバージョン番号の比較から文字列としての比較へのフォールバックを行うようにすることをオプションにしました。また、公開ツール類とインストールツール類の間で異なる推奨動作を提供しました。これによって、名ばかりだった仕様にツール類が実際に動作する方法を伴った行をもたらしました。 [4]

  • 2026年1月: PEP 508 から不注意に残されていた、他の文書への古くて役に立たなくなった参照を修正しました。

  • July 2026: Explain that extra != "name" is not (properly) supported (https://discuss.python.org/t/ban-negative-extras-for-extras-marker/108022/24)

参考文献