シンプルなリポジトリ API#

利用可能なパッケージバージョンを問い合わせたり、インデックスサーバからパッケージを取り出したりするためのインターフェースは、二つの形式: HTML と JSON でもたらされます。

基本となる HTML API <Base HTML API>#

シンプルな API を実装するリポジトリはベースとなる URL によって定義されますが、これは、すべての追加的な URL 群がその下に連なるようなトップレベルの URL です。このような API は、 PyPI のベースとなる URL が https://pypi.org/simple/ であるという事実に即して、 "シンプル <simple>" リポジトリと名付けられます。

注釈

この説明文書でこのあと出てくる URL 群はすべて、このベース URL <base URL> からの相対的なものです (したがって、PyPI に関する URL なら、 /foo/https://pypi.org/simple/foo/ のことです。

リポジトリ内では、ルート URL <root URL> (この文脈ではルート URL <root URL> を表現する / のこと) は、正当な HTML5 ページであってリポジトリ内のプロジェクト毎にひとつのアンカー要素を持っていなければ なりません 。アンカータグのテキストはプロジェクトの名称でなければならず、 href アトリビュートはそのプロジェクトの URL にリンクされていなければ なりません 。例としては:

<!DOCTYPE html>
<html>
  <body>
    <a href="/frob/">frob</a>
    <a href="/spamspamspam/">spamspamspam</a>
  </body>
</html>

ルート URL <root URL> 以下には、リポジトリに含まれる独立のプロジェクトのそれぞれに対応する別の URL があります。この URL のフォーマットは /<project>/ で、 <project> の部分はそのプロジェクトの正規化された名称で置き換えられるので、あるプロジェクトの名称が "HolyGrail" なら、 URL は /holygrail/ のようになるでしょう。この URL は、プロジェクトのファイル一つに対してひとつのアンカーエレメントを伴った正当な HTML5 ページを返さなければなりません。href アトリビュートは、そのファイルのダウンロードリンクの URL で なければならず 、アンカータグのテキストはその URL の最終的なパス構成要素 (ファイル名) に合致して いなければなりません 。この URLは、 URL の断片要素の形でハッシュ値を 含んでいるべき で、その文法は次のようなものです: #<hashname>=<hashvalue> の形で、 <hashname> は (sha256 のように) ハッシュ関数の名前を小文字で表記したものであり、 <hashvalue> は16進数にエンコードされたダイジェスト (ハッシュ値) です。

上記に加えて、以下の制約が API に課せられます:

  • HTML5 ページで応答するすべての URL は / で終わるものでなければならず、リポジトリは / で終わらない URL の最後に / を加えたものへリダイレクト するべき です。

  • URL は、間違いのないロケーションを指し示している限りは絶対パスであっても相対パスであっても構いません。

  • ファイル群が置かれている場所が、リポジトリに対して相対的な場所のどこであるかについては制約条件はありません。

  • 要求されているアンケー要素が存在している限り、API ページ上にどのような他の HTML 要素があっても構いません。

  • リポジトリは、正規化されていない URL を正統で正規化された URL にリダイレクトしても 構いません (例えば、 /Foobar//foobar/ にリダイレクトしてもよい) が、クライアントの側ではこのリダイレクト機能に 依存してはならず 、正規化された URL に対してリクエストを 送らなければなりません

  • レジストリは、 Python の標準ライブラリの hashlib モジュール経由で利用可能であると保証されたハッシュ関数の中から一つを 選択するべきです (現時点では、 md5sha1sha224sha256sha384sha512)。現時点では、 sha256 の使用が推奨されています。

  • 特定の配布物ファイルに GPG 署名があれば、その配布物ファイルの名前に .asc を追加したファイル名で 共存させなければなりません 。だから、 /packages/HolyGrail-1.0.tar.gz が存在していて署名が結びついていれば、そのsと名は /packages/HolyGrail-1.0.tar.gz.asc という名前であるはずです。

  • リポジトリは、 GPG 署名が存在するか否かを示す truefalse を値にとる data-gpg-sig アトリビュートをファイルリンクの中に含んでいても 構いません 。これを行うリポジトリは、全てのリンクについて 行うべきです

  • リポジトリは、ファイルリンクの中に data-requires-python アトリビュートを含んでいても 構いません 。これは、対応するリリース用の Requires-Python メタデータを表示するものです。これが存在するなら、その要求を満足しないバージョンの Python 環境へインストールしている場合には、インストールツール群はダウンロードしたものを 無視するべき です。例えば:

    <a href="..." data-requires-python="&gt;=3">...</a>
    

    アトリビュートの値においては、 < や > は順に &lt;&gt; へと HTML エンコードされなければなりません。

正規化された名称#

この仕様は、 "正規化された" プロジェクト名称の概念を参照しています。 名称正規化仕様 によれば、名称における正当な文字は、 ASCII アルファベット・ ASCII 数字・ .-_ だけです。名称は、 .`-_ がいくつ連続していてもそれを単一の - 文字に置き換え、小文字で統一されているべきです。これは Python の re モジュールで実装されています:

import re

def normalize(name):
    return re.sub(r"[-_.]+", "-", name).lower()

"ヤンク <Yank>" サポートをシンプルな API に追加する#

シンプルなリポジトリの中のリンク群は、値を持たないか任意の文字列を値に取る data-yanked アトリビュートを持っていても 構いませんdata-yanked アトリビュートが存在していれば、この特定のリンクによって指し示されたファイルが "ヤンクされた <Yanked>" ものだと解釈される べきであって 、特定のシナリオ下を除けば一般的にはインストーラによって選択されるべきものではありません。

data-yanked アトリビュートの値は、もし値が存在するなら、そのファイルがヤンクされた理由を表現する任意の文字列です。シンプルなリポジトリ API を処理するツール類は、エンドユーザ向けにその文字列を表示しても 構いません

ヤンクされたアトリビュートは一旦設定されると変更できないというわけではなく、将来の時点で廃止されても構いません (そして、一旦廃止されても再設定することができます) 。そういうことですので、 API ユーザは、ヤンクされたファイルが "ヤンク解除" され (さらに再びヤンクされ) ることに対処でき なければなりません

インストーラ類#

ユーザにとって望ましい経験とは、あるファイルが一旦ヤンクされたら、人間側が今まさにヤンクされたファイルを直接にインストールしようと試みている時、まるでそのファイルが削除されたかのように失敗することです。しかしながら、その人が少し前の時点で試みた場合には、今回はコンピュータが機械的にオリジナルの順序にしたがって今まさにヤンクされたファイルをインストールし、それがヤンクされたファイルなどではなかったかのように振る舞うことです。

インストーラは、ヤンクされたバージョン以外のもので選択上の制約を満足することが可能なら、ヤンクされたリリース群を 無視しなければならず 、要求が全く満たされないことを意味する場合でさえもヤンクされたリリースを 拒否しても構いません 。実装では、上記の意図を汲んだポリシーで、かつ、 "新しい" 依存関係を訳されたリリースやファイルに課すことを避けポリシーを 選択するべきです

これが意味するものは、特定のインストーラの全体的な使い方に一番うまく合致する方法を決定するということで、インストーラの裁量に任されています。

  1. ヤンクされたファイル群は、それが (.* のような範囲を構成するいかなる修正子も付いていない) ===== を使って特定のバージョンに "ピン留め" されたバージョン指定子に合致する唯一のファイルでない限り、常に無視されます。それ以外の場合、このバージョン指定子とのマッチングは、ローカルバージョン・ゼロでのパディング・その他のような事柄について バージョン指定子仕様 にしたがって行われるべきです。

  2. ヤンクされたファイル群は、 (Pipfile.lockpoetry.lock のような) ロックファイルがインストールされるべきものとして指定しているものに合致する唯一のファイル出ない限り、常に無視されます。この場合には、ヤンクされたファイルは、何らかの入力ファイルやコマンドからそのロックファイルを作成したり更新したりする場合には、 使われるべきではありません

あるインストーラがヤンクされたファイルをいつインストールするのかを決めるにあたって、ヤンクされたファイルをインストールすると決定した時にインストーラが警告を 発出するべきです 。そのような警告は、そのファイルがヤンクされた理由についてユーザにより詳しいフィードバックを提供するために、 (もし値があれば) data-yanked アトリビュートの値を 使っても構いません

ミラー#

ミラーは、ヤンクされたファイルを次の二つの方法のうちの一つで扱います:

  1. "アクティブ" なヤンクされたものではないファイル群だけを見せるリポジトリのビューを提供して、シンプルなリポジトリ API から (ヤンクされたファイル群を) 完全に排除する選択をしても構いません。

  2. ヤンクされたファイルも表示するとともに、 data-yanked アトリビュートをもミラーするという選択をしても構いません。

ミラーは、 data-yanked アトリビュートを同時にミラーするのでなければヤンクされたファイルを ミラーしてはいけません

PyPI のシンプルな API にバージョンを付与する#

この仕様は、シンプルな API へのリクエストが成功するたびにそのレスポンスにメタタグを含めるように提案するもので、つまり、 "pypi:repository-version" という名称のアトリビューションを含み、その内容が バージョン指定子仕様 と互換性のあるバージョン番号であるが Major.Minor だけであるようにさらに制約されたものであって、 バージョン指定子仕様 ではサポートされる追加的な機能を一つも含まないものです。

これは、結局こんなふうに見えることでしょう:

<meta name="pypi:repository-version" content="1.0">

リポジトリのバージョンを翻訳する時:

  • メジャーバージョンの増加は、既存のクライアントがその API を意味ある形で使うことができるとはもはや期待できないような後方互換性のない変更のシグナルとして使われます。

  • マイナーバージョンの増加は、既存のクライアントが依然として意味ある形で API を使うことができることが期待されるような、後方互換性を保つ変更のシグナルとして使われます。

将来の仕様の選択は、既存のクライアントが引き続き "意味ある形で" APIを使うことができるであろう、そして、既存の機能に対する追加・修正・削除ができるであろうという幅広い示唆を越えて、特に後方互換性の有無を構成するものに委ねられています。

メジャーバージョンを増加することは考えられず、将来発生する API の進化は API の進化における異なったメカニズムを通じて実現されるであろうというのが、この仕様の期待するところです。しかしながら、

この仕様は、現在の API バージョンを "1.0" と定め、シンプルな API をさらに進化させる将来の仕様がマイナーバージョン番号を増加させるであろうことを期待しています。

クライアント#

シンプルな API と相互作用するクライアントは、応答を受け取るたびにそのリポジトリバージョンをよく 検査するべき で、もし存在しなければバージョン 1.0 であるものと 仮定しなければなりません

想定していたよりも大きなメジャーバージョンに遭遇した時には、クライアントは、ユーザ向けの適切なエラーメッセージと共に ハードフェイルをしなければなりません

想定していたよりも大きなマイナーバージョンに遭遇した時には、クライアントは、適切なメッセージと共にユーザに対して 警告するべき です。

クライアントは、リポジトリがどんな機能を使っているかを判断するために、さらに機能探索 <feature detection> を 継続しても構いません

シンプルな API で配布物のメタデータを提供する#

簡明なリポジトリのプロジェクトページでは、配布物を指し示すアンカータグは、それぞれ、 data-dist-info-metadata アトリビュートを 持っていても構いません 。このアトリビュートが存在していることで、そのアンカータグが提示する配布物が、その配布物が処理されたりインストールされたりしても変更されないであろうコアとなるメタデータのファイルを 持っていなければならない ことを示します。

もし data-dist-info-metadata アトリビュートが存在するなら、リポジトリは、配布物の名称に .metadata を追加したファイル名で配布物のコアとなるメタデータのファイルを配布物と共に 提供しなければなりません 。例えば、 /files/distribution-1.0-py3.none.any.whl が提供されている場所で提供される配布物のコアとなるメタデータは、 /files/distribution-1.0-py3.none.any.whl.metadata という場所に置かれることになるでしょう。これは、 基本となる HTML API の仕様 で GPG 署名のファイルの場所の指定するやり方と似ています。

リポジトリは、 <hashname> がハッシュ関数の小文字で書いた名称で hashvalue が16進数に符号化されたハッシュ値であるとして <hashname>=<hashvalue> という書式を使って、 data-dist-info-metadata アトリビュートの値としてコアとなるメタデータのファイルのハッシュ値を 提供するべきです 。ハッシュ値を利用できない場合には、リポジトリはアトリビュートの値として true使っても構いません

後方互換性#

アンカータグに data-sidt-info-metadata アトリビュートがない場合、ツール群は、配布物をダウンロードするというその時点の動作を反転して、メタデータを調べるという動作に変更することが期待されています。

新しい data-dist-info-metadata アトリビュートをサポートしてない古めのツール群は、このアトリビュートを無視することを期待されており、メタデータを検査するために配布物をダウンロードするという現在の動作を維持することを期待されています。これは、先の data- アトリビュート追加の際に、既存のツール群が期待された動作と似ています。

Python パッケージインデックス向けのJSON ベースのシンプルな API#

標準ライブラリのみを使うレスポンス解析を有効にするためには、この仕様では、 (ファイル群それ自体と 基本となる HTML API の仕様 を別にして) すべてのレスポンスが JSON を用いてシリアライズされているべきであると指定しています。

ゼロコンフィグレーションディスカバリを有効にして追加的な HTTP リクエストの量を最小化するために、この仕様では HTML:ref:基本となる HTML API の仕様 を拡張して、 (ファイル群それ自体を除く) すべての API エンドポイントが HTTP コンテンツネゴシエーションを使ってクライアントおよびサーバが提供するべき正しいシリアライズのフォーマット、即ち HTML もしくは JSON を選択できるようにしています。

バージョニング#

バージョン付与については、 API のバージョン付与に関する仕様 のフォーマット (Major.Minor) を固守していて、既存の HTML レスポンスについては 1.0 と定義されています。この仕様では API に新しい機能を持ち込むことはせず、むしろ、既存の機能のための異なるシリアル化フォーマットを記述しようとしているので、この仕様が既存の 1.0 バージョンに変更を加えることはなく、代わりにどのようにして JSON にシリアライズするのかを述べるに留めています。

API のバージョン付与に関する仕様 と同様に、既存のクライアントが意味のある形でそのフォーマットを理解することをもはや期待できないようになる新しいフォーマットをもたらすような変更があった場合には、メジャーバージョン番号を 増加させなければなりません

同様に、マイナーバージョンは、そのフォーマットで機能の追加・削除はあっても既存のクライアントが意味ある形でそのフォーマットを理解できる状態が継続すると期待されるであろうという時に限って 増加されなければなりません

既存のクライアントがそのフォーマットを意味ある形で理解することができないという結果に陥らないような、かつ、機能の追加や削除に相当しないような変更は、バージョン番号の変更を伴わずに行っても構いません。

これは意図的に曖昧にされています、というのは、この仕様では、メジャーバージョンなりマイナーバージョンなりを増加するべきか否かを考究し決定するべき API での変更をもたらすような将来の仕様に、余地を残すことが最善であると信じられているからです。

API の将来のバージョンでは、そのバージョンで利用可能なシリアル化のサブセットでのみ実現させることができるような何かを追加しても構いません。あらゆるシリアル化のバージョン番号は、メジャーバージョンにおいては、 同期を保つべき ですが、ある機能がそれぞれのフォーマットにシリアル化されるやり方の詳細は、その機能が存在するかしないかを含めて、異なっていても構いません。

この仕様の意図するところは、API はデータを返す URL エンドポイントであって、その解釈は当該データのバージョンによって定義され、その後にターゲットとなるシリアル化フォーマットにシリアライズされるものであると考えられるべきであるということです。

JSON シリアル化#

基本となる HTML API の仕様 からの URL の構造は依然として適用されます、というのは、この仕様は既に存在する API にシリアル化フォーマットを付け足すだけだからです。

後述の制約は、この仕様で記述されたすべての JSON シリアル化応答に適用されます:

  • すべての JSON 応答は、配列やその他の型ではなく 常に JSON オブジェクトであることでしょう。

  • While JSON doesn't natively support a URL type, any value that represents an URL in this API may be either absolute or relative as long as they point to the correct location. If relative, they are relative to the current URL as if it were HTML.

  • API 応答の中の任意の辞書オブジェクトに追加的なキー群を追加しても構いませんし、クライアントは自身が理解しないキー群については 無視しなければなりません

  • あらゆる JSON 応答は、その応答の内容ではなく応答それ自身に関係する情報を含む meta キーを持つことでしょう。

  • すべての JSON 応答は、 API のバージョン付与に関する仕様 で定義されたものと同じ失敗/警告のセマンティクスを持ち、かつ、 API のバージョン付与に関する仕様Major.Minor の形のバージョン番号を文字列として値に取るような meta.api-version キーを持つことでしょう。

  • 基本となる HTML API の仕様 のすべての要求事項のうちの HTML に特化したもの以外は、依然として適用されます。

プロジェクトのリスト#

(ベース URL <base URL> を表現する) この仕様でのルート URL <root URL> / は、 JSON にエンコードされた辞書であって、二つのキーを持つでしょう:

  • projects: 各エントリがディレクトリで単独のキー name を持つような配列で、(そのキーの値は) プロジェクトの名称を表す文字列です。

  • meta: 先述の通り の一般的な応答メタデータ。

ひとつの例として:

{
  "meta": {
    "api-version": "1.0"
  },
  "projects": [
    {"name": "Frob"},
    {"name": "spamspamspam"}
  ]
}

注釈

name フィールドは、基本となる HTML API の仕様 からのものと同じで、正規化していないディスプレイ用名称であるのか正規化済みの名称であるのかを指定していません。実際には、これらの仕様の相異なる実装において異なる選択を行うことがあり、その選択によって非正規化名称か正規化名称かは当該リポジトリの実装の詳細に依存します。

注釈

projects キーは配列なので何らかの種類の順序が要求されますが、 基本となる HTML API の仕様 もこの仕様も特定の順序に並べることを要求しておらず、また、あるリクエストと次のものとで順序が変わらないことも要求していません。心情としては、これが (配列ではなく) 集合であると考えるのが最適ですが、 JSON も HTML も集合を扱う機能を欠いています。

プロジェクトの詳細#

この URL のフォーマットは /<project>/ で、 <project> の部分は 基本となる HTML API の仕様 にしたがって正規化されたプロジェクト名称であり、したがって、 "Silly_Walk" という名称のプロジェクトであれば /silly-walk/ のような URL を持つことになるでしょう。

この URL は、3個のキーを持つJSON にコード化された辞書を返さなければなりません:

  • name: そのプロジェクトの正規化された名前。

  • files: 辞書のリストで、各辞書が個別のファイルを表現しているもの。

  • meta: 先述の通り の一般的な応答メタデータ。

個別のファイルを表す辞書は、それぞれ、次のキーを持ちます:

  • filename: 表現しようとしているファイルのファイル名。

  • url: そこからそのファイルを取得できる URL。

  • hashes: ハッシュ(関数)の名称と、そのファイルの16進数にエンコードされたハッシュ値 (digest) をマッピングした辞書。複数のハッシュを含むことが可能で、その複数のハッシュを使って何をするのか (全部について検証するのか、サブセットについてなのか、全く何もしないのか) についてはクライアントが決めれば良いことになっています。これらのハッシュの名称は、常に小文字に 正規化 されているべきです

    hashes 辞書は、仮にそのファイルについてハッシュがひとつも利用可能でないとしても、 存在していなければなりません し、少なくともひとつのセキュアで利用可能であることが保証されているハッシュが常に含まれていることが とても強く 推奨されています。

    デフォルトでは、 hashlib を経由して利用可能なハッシュアルゴリズム (特に、hashlib.new() に渡すことができて、かつ、追加のパラメータを要求しないもの) は、すべて、ハッシュの辞書のキーとして使うことができます。 :py:`data:`hashlib.algorithms_guaranteed` から選択した少なくともひとつのセキュアなアルゴリズムが、常に、 含まれているべきです 。この仕様の時点では、特に sha256 が推奨されます。

  • requires-python: Requires-Python メタデータフィールドを顕にする オプションの キー。これが存在しているなら、インストーラツール群は、要求事項を満足しない Python のバージョンへインストールしようとする場合、ダウンロードを 無視するべきです

    基本となる HTML API の仕様 の中の data-requires-python とは異なって、 requires-python キーは、JSON が自然に要求することを除けば、特別なエスケープを要求しません。

  • dist-info-metadata: API メタデータの仕様 で指定されたのと同じ場所 ({file_url}.metadata) を通じて当該ファイルに関するメタデータが利用可能であることを示す オプション のキー。これが存在する場合には、そのファイルに対応するメタデータファイルがあるか否かを示すブーリアンか、または、ハッシュ名称からメタデータのハッシュを16進数にエンコードしたダイジェストへの対応をマッピングする辞書か、のいずれかで なければなりません

    これがブーリアンの代わりにハッシュ辞書である時には、 hashes キーが true を値に取る場合と同じ要求事項と推奨事項の全てが、このキーにも課されます。

    このキーがない場合には、メタデータファイルが存在するかもしれませんし、存在しないかもしれません。このキーの値が真である場合はメタデータファイルが存在し、値が偽の場合には存在しません。

    サーバ群は、可能であれば、メタデータファイルのハッシュを利用可能とすることが推奨されています。

  • gig-sig: オプション のキーで、当該ファイルに GPG 署名が付属しているか否かを示すブーリアン。署名ファイルへの URL は、 基本となる HTML API の仕様 で指定されたもの ({file_url}.asc) に従います。このキーが存在しない場合は、署名はあるかもしれませんし、ないかもしれません。

  • yanked: オプション のキーで、当該ファイルがヤンクされたものか否かを示すブーリアンか、または、ファイルがヤンクされるに至った特定の理由を示す任意の空ではない文字列か、いずれかの値を持ちます。 yanked キーが存在して値が真であれば、url フィールドによって指し示されるファイルが API ヤンク仕様 にある通りに "ヤンク" されたことを示します。

ひとつの例として:

{
  "meta": {
    "api-version": "1.0"
  },
  "name": "holygrail",
  "files": [
    {
      "filename": "holygrail-1.0.tar.gz",
      "url": "https://example.com/files/holygrail-1.0.tar.gz",
      "hashes": {"sha256": "...", "blake2b": "..."},
      "requires-python": ">=3.7",
      "yanked": "Had a vulnerability"
    },
    {
      "filename": "holygrail-1.0-py3-none-any.whl",
      "url": "https://example.com/files/holygrail-1.0-py3-none-any.whl",
      "hashes": {"sha256": "...", "blake2b": "..."},
      "requires-python": ">=3.7",
      "dist-info-metadata": true
    }
  ]
}

注釈

files キーは配列なのである種の順序があることが要求される一方で、 基本となる HTML API の仕様 にもこの仕様にも、特定の順序付けも要求されていませんし、あるリクエストと次のそれの間で順序が保たれるということも要求されていません。心情としては、これは集合であると考えるのが最適ですが、JSON にも HTML にも集合を扱う機能は欠落しています。

Content-Types#

この仕様では、シンプル API からの応答はすべて、その応答が何であるか (シンプル API からの応答である) 、どのバージョンの API を表現しているか、また、使われているシリアル化フォーマットは何であるかを記述する標準のコンテント型を持ちます。

このコンテント型の構造はこのようになっていることでしょう:

application/vnd.pypi.simple.$version+format

これらの API からの応答のうちのひとつをクライアントが理解しようと試みる際には、メジャーバージョンだけが継続不可能にするものであるべきなので、メジャーバージョンだけがコンテント型に含まれるでしょうし、それがバージョン番号であることを明確にするために前に v をつけられることになるでしょう。

ということは、既存の 1.0 API においては、コンテント型はこのようになるでしょう:

  • JSON: application/vnd.pypi.simple.v1+json

  • HTML: application/vnd.pypi.simple.v1+html

上記に加えて、特別な "meta" バージョンが latest という名前でサポートされており、その目的はクライアントが完全に最新のバージョンをリクエストすることを許すためであり、そのバージョンが何であるかを前もって知ることを不要にするためです。しかしながら、クライアントは自身がどのバージョンをサポートしているのかを明示的に示すことが推奨されています。

既存の 基本となる HTML API の仕様 の API 応答が text/html コンテント型を使うことを期待している既存のクライアント群をサポートするために、この仕様では、さらに、 text/htmlapplication/vnd.pypi.simple.v1+html コンテント型へのエイリアスであると定義しています。

バージョン + フォーマットの選択#

今や複数の可能なシリアル化が存在するので、クライアント群がどのシリアル化フォーマットであれば理解できるのかを示すことができるようにする必要があります。加えて、以前の API バージョンを期待している既存のクライアント群の妨げとなることなしに、どんな API の可能な新しいメジャーバージョンでも追加することができれば利益があることでしょう。

これを可能にするために、この仕様では、 HTTP の サーバ主導のコンテントネゴシエーション の使用を標準化します。

この仕様では、サーバ主導のコンテントネゴシエーションについて完全にすべてを記述することはありませんが、流れは大まかに次のようになります:

  1. 理解できるコンテント型を全て``Accept`` ヘッダに列挙した HTTP リクエストをクライアントが作成します。

  2. サーバはそのヘッダを検証して、列挙されたコンテント型からひとつを選択して、そのコンテント型を使って応答を返します (Accept ヘッダが欠落している場合は Accept: */* であるものとして扱います) 。

  3. Accept ヘッダにあるコンテント型のいずれもサーバがサポートしていない場合には、どのように応答するかについてサーバは3個の異なるオプションから選択することができます:

    1. クライアントが要求したものではないデフォルトのコンテント型を選択して、それを使って応答を返すこと。

    2. HTTP の 406 Not Acceptable 応答を返すことで、要求されたコンテント型の中に利用可能なものがないことを示し、サーバとしてはデフォルトのコンテント型を選択して応答することができないかしたくないことを示すこと。

    3. HTTP の 300 Multiple Choices 応答を返して、選択できるかもしれないすべての可能な応答のリストを含めておくこと。

  4. サーバが返すかもしれない異なる応答のタイプを取り扱ってクライアントは応答を解釈します。

この仕様は、あるコンテント型を取り扱うことについてサーバが行った選択のどれが (クライアントへ応答を) 返すことができないものであるかを指定しませんし、クライアント側がそのクライアントにとって最も意味を為す方法であればどんなものでも可能な応答の全てを取り扱う準備を しておくべきだ とは指定しません。

しかしながら、 300 Multiple Choices 応答がどのように解釈され得るかについて標準的なフォーマットは存在しないので、クライアント側が理解して要求するべき別のコンテント型を選択する手段がないので、この仕様ではサーバがこのオプションを採用することを強い非推奨にしています。加えて、クライアントが別のコンテント型を 理解できるであろう とは思われず、したがって、この応答は 406 Not Acceptable エラーと同じ扱いを受けるのが精々であろうと思われます。

This spec does require that if the meta version latest is being used, the server MUST respond with the content type for the actual version that is contained in the response (i.e. an Accept: application/vnd.pypi.simple.latest+json request that returns a v1.x response should have a Content-Type of application/vnd.pypi.simple.v1+json).

Accept ヘッダは、クライアントが理解し処理することが可能なコンテント型をカンマで区切られたリストに列挙したものです。まさに要求されているところのコンテント型のそれぞれについて3個の異なるフォーマットをサポートしています:

  • $type/$subtype

  • $type/*

  • */*

バージョン+フォーマット <version+format> の選択に使うには、これらのうちで最も役に立つのは $type/$subtype で、望みのバージョンとフォーマットを実際に指定する唯一の方法だからです。

Accept ヘッダに列挙されたコンテント型の順序は、なんら特定の意味を持たず、サーバ側ではその全てをそれを使って応答するために等しく正当なものと 考えるべきです 。クライアント側が特定のコンテント型を他のものよりも好むことを指定したいならば、 Accept ヘッダの quality バリュー の文法を使っても構いません。

これは、 ;q= とこれに後続する10進数で3桁までの 0 以上 1 以下の値を付加することによって Accept ヘッダ内の特定のエントリの優先度をクライアントが指定することを可能にします。この値を解釈する際には、より高い q 値 (quality 値) のエントリがより低い品質のものよりも優先され、q 値を持たないものはデフォルトで 1 であると解釈されます。

しかしながら、サーバはクライアント側から要請されたコンテント型の どれ を選んでも構わないということ、そして、サーバはクライアントが 要請しなかった コンテント型で返答しても構わないということを、クライアントは心に留めておくべきです。

API へのリクエストから受けとった応答のコンテント型を決定するにあたってクライアントを補助するために、この仕様では、応答のコンテント型を示す Content-Type ヘッダをサーバが常に含めることを要求している。これは、技術的には後方互換性のない変更ですが、実際には pip ` はすでにこの要求事項 を強制していますので、実際に何かを壊すリスクは低いでしょう。

クライアントがどのように運用できるかの例は次のように見えるでしょう:

import email.message
import requests

def parse_content_type(header: str) -> str:
    m = email.message.Message()
    m["content-type"] = header
    return m.get_content_type()

# Construct our list of acceptable content types, we want to prefer
# that we get a v1 response serialized using JSON, however we also
# can support a v1 response serialized using HTML. For compatibility
# we also request text/html, but we prefer it least of all since we
# don't know if it's actually a Simple API response, or just some
# random HTML page that we've gotten due to a misconfiguration.
CONTENT_TYPES = [
    "application/vnd.pypi.simple.v1+json",
    "application/vnd.pypi.simple.v1+html;q=0.2",
    "text/html;q=0.01",  # For legacy compatibility
]
ACCEPT = ", ".join(CONTENT_TYPES)


# Actually make our request to the API, requesting all of the content
# types that we find acceptable, and letting the server select one of
# them out of the list.
resp = requests.get("https://pypi.org/simple/", headers={"Accept": ACCEPT})

# If the server does not support any of the content types you requested,
# AND it has chosen to return a HTTP 406 error instead of a default
# response then this will raise an exception for the 406 error.
resp.raise_for_status()


# Determine what kind of response we've gotten to ensure that it is one
# that we can support, and if it is, dispatch to a function that will
# understand how to interpret that particular version+serialization. If
# we don't understand the content type we've gotten, then we'll raise
# an exception.
content_type = parse_content_type(resp.headers.get("content-type", ""))
match content_type:
    case "application/vnd.pypi.simple.v1+json":
        handle_v1_json(resp)
    case "application/vnd.pypi.simple.v1+html" | "text/html":
        handle_v1_html(resp)
    case _:
        raise Exception(f"Unknown content type: {content_type}")

クライアント側が、 HTML だけを、または JSON だけをサポートしたいと望むなら、 Accept ヘッダから望まないコンテント型を単に取り除くでしょうし、(望まないものを) 受け取ったらエラーにすることでしょう。

代わりの交渉機構 <Negotiation Mechanisms>#

HTTP でのコンテンツネゴシエーションを使うことがクライアントとサーバが協調してクライアントが理解することができる HTTP 応答を得られることを確実にする標準的な方法である一方で、このメカニズムで十分とは言えないような状況が存在します。そのような場合向けに、この仕様には、 オプションとして 代わりに使っても構わない代わりの交渉機構 <alternative negotiation mechanisms> があります。

URL パラメータ#

Servers that implement the Simple API may choose to support a URL parameter named format to allow the clients to request a specific version of the URL.

format パラメータの値は、正当なコンテント型のうちの ひとつ であるべきです。複数のコンテント型を渡すことやワイルドカード、quality 値などはサポートされて いません

このパラメータをサポートすることはオプションで、 API とやりとりするクライアントはこれを 当てにするべきではありません 。このネゴシエーションメカニズムは、人間がブラウザ内で API をより簡単に調査することができるようにすることを意図したもの、ないし、説明文書やノートから特定の version+format にリンクすることができるようにすることを意図したものです。

このパラメータをサポートしないサーバの側では、これの存在時にはエラーを返すことを選択しても構いませんし、単にその存在を無視しても構いません。

サーバがこのパラメータを実装していない時は、クライアントの Accept ヘッダ内の値に何があってもそれに 高い優先度を与えるべき で、要求されたフォーマットをサーバがサポートしていないなら Accept ヘッダにフォールバックすることを選択しても良いし、標準的なサーバ側が駆動するコンテントネゴシエーションが典型的に持っているエラーコンディション (例えば、 406 Not Available303 Multiple Choices 、ないし、応答のデフォルトの型) のどれでもひとつを選択しても構いません。

エンドポイントの設定#

このオプションは、技術的には何ら特別なオプションではなく、コンテントネゴシエーションを使用することに伴う自然な結果であり、利用可能なコンテント型のいずれかを自身のデフォルトとしてサーバが選択することを許すものです。

あるサーバがサーバ主導のコンテントネゴシエーションを実装したくないか実装することができなくて、むしろユーザに対してクライアントが望むバージョンを選択するように明示的に要求するのであれば、それはサポートされているコンフィグレーションのひとつです。

これを有効にするためには、サーバは、複数のエンドポイント (例えば、 /simple/v1+html//simple/v1+json/ のいずれかまたは両方) をサポートしたいと考える version+format のそれぞれにするべきです。このようなエンドポイントのもとでは、コンテント型のひとつ (またはサブセット) をサポートするリポジトリのコピーをホストすることができます。クライアントが Accept ヘッダを使ってリクエストした時に、サーバはそれを無視して当該エンドポイントに対応するコンテント型を返すことができます。

特定のコンフィグレーションを要求したいと願うクライアントにとっては、ある特定のリポジトリ URL にどんな version+format が設定されたかを追跡することができますし、そのサーバへのリクエストを作成する時に正しいコンテント型 だけ を含んだ Accept ヘッダを発出することができます。

TUF サポート - PEP 458#

PEP 458 は、すべての API 応答がハッシュ可能で、リポジトリのルートからの相対パスによって一意に識別できることを要求しています。シンプル API リポジトリでは、ターゲットのパスは、我々の API のルート (例えば、 PyPI における /simple/) です。標準の HTTP クライアントを使う代わりに TUF クライアントを使って API にアクセスする時にはこれが試練を生み出しますが、それというのは、ハッシュが相異なるような複数の異なる表現形式をターゲットが持っているかもしれないという事実を TUF クライアントが扱えないからです。

PEP 458 は、どんなターゲットパスがシンプル API 向けであるべきかについて指定していませんが、 TUF はターベットパスが "ファイルライク <file-like>" であることを要求し、換言すれば、 simple/PROJECT/. のようなパスは技術的にはディレクトリを指し示しているので受け入れ不可能です。

欠点を補う取り柄は、シンプル API から持ってきた URL にターゲットパスが実際に 合致していなければならないということはない ことで、取得する必要のある URL の実際の形への変換の方法を取得したコードが知っていることが魔法の印形になり得ます。これと同じことが、 Accept ヘッダのような実際の HTTP リクエスト他の側面についても当てはまります。

結局、あるディレクトリをファイル名に対応させる方法を解明することはこの仕様のスコープの外にあります (しかし PEP 458 のスコープの中にはあるでしょう) が、この仕様では、 PEP 458 のメタデータの中にこれをどれほど正確に表現するのかについての決断を行うことを遅延させているのです。

しかしながら、 PEP 458 を実装しようと試みる pip に対する現在の WIP ブランチは、 simple/PROJECT/index.html のようなターゲットパスを使っているようです。これは、何か simple/PROJECT/vnd.pypi.simple.vN.Format のようなものを使うことで、 API のバージョンやシリアル化フォーマットを包含するように修正できるかもしれません。それで、 v1 の HTML フォーマットは simple/PROJECT/vnd.pypi.v1.html のように、 v1 の JSON フォーマットは simple/PROJECT/vnd.pypi.simple.v1.json のようになることでしょう。

このケースでは、 text/htmlapplication/vnd.pypi.simple.v1+html へのエイリアスであることから、 TUF を通じて相互作用する時にはもっと明示的な名称に正規化する方が最大の意味をなすことになるでしょう。

latest メタバージョンがターゲットの中に含まれているべきではないのと同様に、明示的に宣言されたバージョンだけがサポートされるべきです。

推奨事項#

この節は 規範的ではなく、この仕様を実装する上での最善のデフォルト実装の決定だと仕様の執筆者たちが信じるものを表現していますが、しかし、これらの決定に合致させるための要求事項としてはいかなるものも 表現していません

これらの決断は、 API の最新のバージョンの上に移行できるような、その一方でなるべく大量の互換性も維持するような要求事項を数の上でなるべく最大化するように選択されています。加えて、クライアント側が最善の選択を行うようにと試みるガードレールを API が提供するように試みてきました。

サーバには次のことが推奨されます:

  • サーバ主導のコンテントネゴシエーションを使って、合理的に可能である限り、もしくは、少なくとも HTML 応答を用いる非自明のトラフィックを受け取る限り、この仕様の中で記述される3個のコンテント型の全てをサポートすること。

  • サーバは、どのように協働すれば良いかを知っているコンテント型をひとつも含まないような Accept ヘッダに遭遇した時、 300 Multiple Choice 応答を返すべきではなく、その代わりに 406 Not Acceptable 応答を返すべきです。

    • しかしながら、このエンドポイント設定を使うことを選択したなら、 できることならそのエンドポイントが期待しているコンテント型で 200 Ok を返したいと考えるべきです。

  • 受け入れ可能なバージョンを選択する時、サーバは、クライアントがサポートする中で、最も表現力が豊かで機能的なシリアル化フォーマットを伴っていて、クライアント側が表現したクオリティ優先度 <quality priority> と同じくクライアントからのリクエストの特質を考慮に入れて、最も高いバージョンを選択するべきであり、 text/html コンテント型の使用は最後の逃避先とするべきです。

クライアント側には次のことが推奨されます:

  • Support all 3 content types described in this spec, using server-driven content negotiation, for as long as they reasonably can.

  • When constructing an Accept header, include all of the content types that you support.

    You should generally not include a quality priority value for your content types, unless you have implementation specific reasons that you want the server to take into account (for example, if you're using the standard library HTML parser and you're worried that there may be some kinds of HTML responses that you're unable to parse in some edge cases).

    The one exception to this recommendation is that it is recommended that you should include a ;q=0.01 value on the legacy text/html content type, unless it is the only content type that you are requesting.

  • Explicitly select what versions they are looking for, rather than using the latest meta version during normal operation.

  • Check the Content-Type of the response and ensure it matches something that you were expecting.

Additional Fields for the Simple API for Package Indexes#

This specification defines version 1.1 of the simple repository API. For the HTML version of the API, there is no change from version 1.0. For the JSON version of the API, the following changes are made:

  • The api-version must specify version 1.1 or later.

  • A new versions key is added at the top level.

  • Two new "file information" keys, size and upload-time, are added to the files data.

  • Keys (at any level) with a leading underscore are reserved as private for index server use. No future standard will assign a meaning to any such key.

The versions and size keys are mandatory. The upload-time key is optional.

バージョン指定子#

An additional key, versions MUST be present at the top level, in addition to the keys name, files and meta defined in the JSON API specification. This key MUST contain a list of version strings specifying all of the project versions uploaded for this project. The value is logically a set, and as such may not contain duplicates, and the order of the values is not significant.

All of the files listed in the files key MUST be associated with one of the versions in the versions key. The versions key MAY contain versions with no associated files (to represent versions with no files uploaded, if the server has such a concept).

Note that because servers may hold "legacy" data from before the adoption of the version specifiers specification (VSS), version strings currently cannot be required to be valid VSS versions, and therefore cannot be assumed to be orderable using the VSS rules. However, servers SHOULD use normalised VSS versions where possible.

Additional file information#

Two new keys are added to the files key.

  • size: This field is mandatory. It MUST contain an integer which is the file size in bytes.

  • upload-time: This field is optional. If present, it MUST contain a valid ISO 8601 date/time string, in the format yyyy-mm-ddThh:mm:ss.ffffffZ, which represents the time the file was uploaded to the index. As indicated by the Z suffix, the upload time MUST use the UTC timezone. The fractional seconds part of the timestamp (the .ffffff part) is optional, and if present may contain up to 6 digits of precision. If a server does not record upload time information for a file, it MAY omit the upload-time key.

Rename dist-info-metadata in the Simple API#

The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL"" in this document are to be interpreted as described in RFC 2119.

Servers#

The the API metadata file specification metadata, when used in the HTML representation of the Simple API, MUST be emitted using the attribute name data-core-metadata, with the supported values remaining the same.

The the API metadata file specification metadata, when used in the the JSON API specification JSON representation of the Simple API, MUST be emitted using the key core-metadata, with the supported values remaining the same.

To support clients that used the previous key names, the HTML representation MAY also be emitted using the data-dist-info-metadata, and if it does so it MUST match the value of data-core-metadata.

クライアント#

Clients consuming any of the HTML representations of the Simple API MUST read the the API metadata file specification metadata from the key data-core-metadata if it is present. They MAY optionally use the legacy data-dist-info-metadata if it is present but data-core-metadata is not.

Clients consuming the JSON representation of the Simple API MUST read the the API metadata file specification metadata from the key core-metadata if it is present. They MAY optionally use the legacy dist-info-metadata key if it is present but core-metadata is not.

歴史#

  • September 2015: initial form of the HTML format, in PEP 503

  • July 2016: Requires-Python metadata, in an update to PEP 503

  • May 2019: "yank" support, in PEP 592

  • July 2020: API versioning convention and metadata, and declaring the HTML format as API v1, in PEP 629

  • May 2021: providing package metadata independently from a package, in PEP 658

  • May 2022: initial form of the JSON format, with a mechanism for clients to choose between them, and declaring both formats as API v1, in PEP 691

  • October 2022: project versions and file size and upload-time in the JSON format, in PEP 700

  • June 2023: renaming the field which provides package metadata independently from a package, in PEP 714