Following system colour scheme Selected dark colour scheme Selected light colour scheme

Python 개선 제안 한국어 번역

PEP 691 – Python 패키지 색인을 위한 JSON 기반 Simple API

Author:
Donald Stufft <donald at stufft.io>, Pradyun Gedam <pradyunsg at gmail.com>, Cooper Lees <me at cooperlees.com>, Dustin Ingram <di at python.org>
PEP-Delegate:
Brett Cannon <brett at python.org>
Discussions-To:
Discourse thread
Status:
Final
Type:
Standards Track
Topic:
Packaging
Created:
04-May-2022
Post-History:
05-May-2022
Resolution:
Discourse message

Table of Contents

번역·라이선스 안내

이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain or CC0-1.0, whichever is more permissive 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판

Important

This PEP is a historical document. The up-to-date, canonical spec, Simple repository API, is maintained on the PyPA specs page.

×

See the PyPA specification update process for how to propose changes.

초록

관련 PEP 503에 정의되었으며 그보다 훨씬 오래전부터 사용되어 온 “단순 저장소 API”는 매우 오랫동안 우리에게 상당히 유용했습니다. 그러나 데이터 교환 메커니즘으로 HTML을 사용하는 데 의존하는 방식에는 몇 가지 단점이 있습니다.

HTML 기반 API에는 두 가지 주요 문제가 있습니다.

  • HTML5는 표준이지만 매우 복잡한 표준이며, 이를 완전히 올바르게 구문 분석하려면 현재 Python 표준 라이브러리(또는 다른 많은 언어의 표준 라이브러리)에 존재하지 않는 복잡한 로직이 필요합니다.

    따라서 기술적으로 유효한 모든 것을 실제로 수용하려면 도구가 대규모 의존성을 가져오거나 표준 라이브러리의 html.parser 라이브러리에 의존해야 합니다. 후자는 더 가볍지만 HTML5를 완전히 지원하지 않을 가능성이 있습니다.

  • HTML5는 주로 사람이 소비할 문서를 표시하기 위한 마크업 언어로 설계되었습니다. 우리가 HTML5를 사용하는 것은 주로 역사적이고 우연한 이유에 따른 것이며, 처음부터 새로 시작한다면 HTML5에 의존하는 API를 설계할 사람은 거의 없을 것입니다.

    사람이 소비하도록 설계된 마크업 형식을 사용하는 데 따른 주요 문제는 HTML 내부에 데이터를 실제로 인코딩할 좋은 방법이 없다는 것입니다. 우리는 이 API에 넣는 데이터를 제한하고 API에 데이터를 욱여넣을 방법을 창의적으로 고안하여 이 문제를 해결해 왔습니다(예를 들어 해시는 URL 프래그먼트로 삽입하고, PEP 592에서 data-yanked 속성을 추가했습니다).

PEP 503은 대체로 이미 사용 중이던 것을 표준화하려는 시도였으므로 API에 큰 변경을 제안하지 않았습니다.

그 후 몇 년 동안 우리는 PyPI의 전체 API를 새롭게 구상할 “API V2”에 대해 정기적으로 논의해 왔습니다. 그러나 시간 제약이 제한적이었기 때문에, 그 노력은 그렇게 하면 좋겠다고 생각하는 것 이상으로는 거의 또는 전혀 진전을 이루지 못했습니다.

이 PEP는 다른 경로를 시도합니다. 전체 API 구조를 근본적으로 변경하는 대신, 사람이 중심인 문서 형식을 사용하는 것보다 소프트웨어가 구문 분석하기 쉬운 형식으로 기존 PEP 503 응답에 포함된 기존 데이터의 새로운 직렬화를 지정합니다.

목표

  • Enable zero configuration discovery. Simple API의 클라이언트는 어떤 형태의 대역 외 통신(구성, 사전 지식 등)에 의존하지 않고 대상 저장소가 이 PEP를 지원하는지 정상적으로 확인할 수 있어야 MUST 합니다. 그러나 개별 클라이언트는 이 API의 사용을 활성화하기 위해 구성을 요구하도록 선택할 수 MAY 있습니다.
  • 클라이언트가 “레거시” HTML 구문 분석 지원을 중단할 수 있도록 합니다. 대부분의 클라이언트가 한동안, 어쩌면 영원히 HTML만 지원하는 저장소를 계속 지원할 것으로 예상되지만, 클라이언트가 새로운 API 형식만 지원하고 더 이상 HTML 구문 분석기를 호출하지 않도록 선택할 수 있어야 합니다.
  • 저장소가 “레거시” HTML 형식 지원을 중단할 수 있도록 합니다. 클라이언트와 마찬가지로 대부분의 저장소가 오랫동안 또는 영원히 HTML 응답을 계속 지원할 것으로 예상됩니다. 저장소가 새로운 형식만 지원하도록 선택할 수 있어야 합니다.
  • 기존 HTML 전용 클라이언트를 완전히 지원합니다. API에 엄격한 PEP 503 API로 접근하는 기존 클라이언트를 중단시켜서는 안 됩니다. 유일한 예외는 저장소 자체가 더 이상 HTML 형식을 지원하지 않도록 선택한 경우입니다.
  • 추가 HTTP 요청을 최소화합니다. 이 API를 사용하더라도 설치 관리자가 작동하는 데 필요한 HTTP 요청 수가 크게 증가해서는 안 됩니다. 이상적으로는 추가 요청이 0개 필요해야 하지만, 필요한 경우 요청을 한두 개 추가할 수 있습니다(의존성별이 아닌 전체 기준).
  • 추가적인 고유 응답을 최소화합니다. PyPI와 같은 대규모 저장소가 응답을 캐시하는 방식의 특성상, 이 PEP는 저장소가 생성할 수 있는 추가적인 고유 응답의 수를 상당히 많거나 조합적으로 많게 만들어서는 안 됩니다.
  • Supports TUF. 이 PEP는 TUF가 지원할 수 있는 범위 내에서 기능할 수 있어야 MUST 하며 (PEP 458), TUF를 사용하여 보안이 적용될 수 있어야 합니다.
  • 클라이언트에는 표준 라이브러리 또는 소규모 외부 의존성만 요구합니다. API 응답 구문 분석에는 이상적으로 표준 라이브러리 외에는 아무것도 필요하지 않아야 하지만, 작고 순수 Python으로 작성된 의존성을 요구하는 것은 허용됩니다.

사양

표준 라이브러리만으로 응답을 구문 분석할 수 있도록, 이 PEP는 파일 자체와 PEP 503의 HTML 응답을 제외한 모든 응답을 JSON 으로 직렬화하도록 지정합니다.

제로 구성 검색을 활성화하고 추가 HTTP 요청 수를 최소화하기 위해, 이 PEP는 PEP 503을 확장하여 파일 자체를 제외한 모든 API 엔드포인트가 HTTP 콘텐츠 협상을 사용하도록 합니다. 이를 통해 클라이언트와 서버가 제공할 올바른 직렬화 형식, 즉 HTML 또는 JSON을 선택할 수 있습니다.

버전 관리

버전 관리는 PEP 629 형식(Major.Minor)을 따르며, 이 형식에서는 기존 HTML 응답이 1.0으로 정의되어 있습니다. 이 PEP는 API에 새로운 기능을 도입하는 것이 아니라 기존 기능에 대한 다른 직렬화 형식을 설명하므로, 기존 1.0 버전을 변경하지 않고 이를 JSON으로 직렬화하는 방법만 설명합니다.

이 PEP는 PEP 629와 유사하게, 새 형식에 대한 변경으로 인해 기존 클라이언트가 형식을 의미 있게 이해할 수 있다고 더 이상 기대할 수 없게 된다면 주 버전 번호를 증가해야 합니다.

마찬가지로 형식에서 기능이 추가되거나 제거되지만 기존 클라이언트가 계속해서 해당 형식을 의미 있게 이해할 것으로 예상된다면 부 버전 번호를 MUST 증가시켜야 합니다.

기존 클라이언트가 형식을 의미 있게 이해할 수 없게 만들지 않으며 기능의 추가나 제거에도 해당하지 않는 변경은 버전 번호를 변경하지 않고 발생할 수 있습니다.

이는 의도적으로 모호하게 작성한 것입니다. 이 PEP는 API를 변경하는 향후 PEP가 해당 변경을 조사하여 주 버전이나 부 버전을 증가시켜야 하는지 결정하도록 맡기는 것이 최선이라고 봅니다.

API의 향후 버전에는 해당 버전에서 사용 가능한 직렬화의 일부 집합에서만 표현할 수 있는 항목이 추가될 수 있습니다. 주 버전 내의 모든 직렬화 버전 번호는 SHOULD 서로 동기화된 상태로 유지해야 하지만, 기능이 각 형식으로 직렬화되는 구체적인 방식은 다를 수 있으며 해당 기능이 아예 존재하는지 여부도 다를 수 있습니다.

이 PEP의 의도는 API를 데이터를 반환하는 URL 엔드포인트로 간주해야 하며, 데이터의 해석은 해당 데이터의 버전에 의해 정의된 다음 대상 직렬화 형식으로 직렬화된다고 보는 것입니다.

JSON 직렬화

이 PEP는 이미 존재하는 API에 추가 직렬화 형식만 더하므로 PEP 503의 URL 구조는 여전히 적용됩니다.

이 PEP에서 설명하는 JSON 직렬화 응답에는 다음 제약 조건이 모두 적용됩니다.

  • 모든 JSON 응답은 배열이나 다른 형식이 아닌 JSON 객체여야 항상 합니다.
  • JSON은 URL 형식을 기본적으로 지원하지 않지만, 이 API에서 URL을 나타내는 값은 올바른 위치를 가리키기만 한다면 절대 URL이거나 상대 URL일 수 있습니다. 상대 URL인 경우 HTML에서와 마찬가지로 현재 URL을 기준으로 한 상대 URL입니다.
  • API 응답의 모든 딕셔너리 객체에는 추가 키가 포함될 수 있으며, 클라이언트는 이해하지 못하는 키를 MUST 무시해야 합니다.
  • 모든 JSON 응답에는 응답의 콘텐츠가 아니라 응답 자체와 관련된 정보를 포함하는 meta 키가 있습니다.
  • 모든 JSON 응답에는 PEP 629Major.Minor 버전 번호를 포함하는 문자열인 meta.api-version 키가 있으며, 실패/경고 의미 체계는 PEP 629에서 정의한 것과 같습니다.
  • HTML에만 해당하지 않는 PEP 503의 모든 요구 사항도 여전히 적용됩니다.

프로젝트 목록

이 PEP의 루트 URL /(기본 URL을 나타냄)는 두 개의 키를 가진 JSON 인코딩 딕셔너리가 됩니다.

  • projects: 각 항목이 프로젝트 이름을 나타내는 name 키 하나만 가진 딕셔너리인 배열입니다.
  • meta: 앞서 설명한 일반 응답 메타데이터입니다.

예시는 다음과 같습니다.

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

Note

name 필드는 PEP 503의 필드와 동일하지만, 정규화되지 않은 표시 이름인지 정규화된 이름인지는 지정하지 않습니다. 실제로 이러한 PEP의 구현마다 여기서 서로 다르게 선택하고 있으므로, 이를 정규화되지 않은 이름이나 정규화된 이름 중 하나라고 의존하는 것은 해당 저장소의 구현 세부 사항에 의존하는 것입니다.

Note

projects 키는 배열이므로 어떤 순서로든 정렬되어 있어야 하지만, PEP 503과 이 PEP 어느 쪽도 특정 순서나 요청마다 순서가 일관되어야 한다는 것을 요구하지 않습니다. 개념적으로는 집합으로 생각하는 것이 가장 좋지만, JSON과 HTML 모두 집합을 표현하는 기능이 없습니다.

프로젝트 세부 정보

이 URL의 형식은 /<project>/이며, 여기서 <project>는 해당 프로젝트에 대한 PEP 503 정규화 이름으로 대체됩니다. 따라서 “Silly_Walk”라는 프로젝트의 URL은 /silly-walk/와 같습니다.

이 URL은 세 개의 키를 가진 JSON 인코딩 딕셔너리로 응답해야 합니다.

  • name: 프로젝트의 정규화된 이름입니다.
  • files: 각 항목이 개별 파일을 나타내는 딕셔너리 목록입니다.
  • meta: 앞서 설명한 일반 응답 메타데이터입니다.

각 개별 파일 딕셔너리에는 다음 키가 있습니다.

  • filename: 나타내는 파일의 파일 이름입니다.
  • url: 해당 파일을 가져올 수 있는 URL입니다.
  • hashes: 해시 이름을 파일의 16진수로 인코딩된 다이제스트에 매핑하는 딕셔너리입니다. 여러 해시를 포함할 수 있으며, 여러 해시를 어떻게 처리할지는 클라이언트가 결정합니다(모두 또는 일부를 검증하거나, 전혀 검증하지 않을 수 있습니다). 이러한 해시 이름은 항상 소문자로 정규화해야 합니다.

    파일에 사용할 수 있는 해시가 없는 경우에도 hashes 딕셔너리는 반드시 MUST 존재해야 하지만, 보안성이 있고 항상 사용 가능하다고 보장되는 해시를 하나 이상 포함하는 것이 HIGHLY 권장됩니다.

    기본적으로 hashlib를 통해 사용할 수 있는 모든 해시 알고리즘(구체적으로는 추가 매개변수가 필요하지 않고 hashlib.new()에 전달할 수 있는 알고리즘)을 hashes 딕셔너리의 키로 사용할 수 있습니다. hashlib.algorithms_guaranteed의 안전한 알고리즘을 하나 이상 항상 포함해야 합니다. 이 PEP에서는 특히 sha256을 권장합니다.

  • requires-python: PEP 345에 지정된 Requires-Python 메타데이터 필드를 노출하는 선택적 키입니다. 이 키가 있으면 설치 도구는 요구 사항을 충족하지 않는 Python 버전에 설치할 때 해당 다운로드를 무시해야 합니다.

    관련 PEP 503data-requires-python과 달리, requires-python 키에는 JSON에서 자연스럽게 수행되는 것 외에 특별한 이스케이프 처리가 필요하지 않습니다.

  • dist-info-metadata: PEP 658에 지정된 것과 동일한 위치({file_url}.metadata)를 통해 이 파일의 메타데이터를 사용할 수 있음을 나타내는 선택적 키입니다. 이 키가 있으면 파일에 연결된 메타데이터 파일이 있는지를 나타내는 불리언이거나, 해시 이름을 메타데이터 해시의 16진수로 인코딩된 다이제스트에 매핑하는 딕셔너리여야 합니다.

    불리언 대신 해시 딕셔너리인 경우 이 키에도 hashes 키와 동일한 모든 요구 사항과 권장 사항이 적용됩니다.

    이 키가 없으면 메타데이터 파일이 존재할 수도 있고 존재하지 않을 수도 있습니다. 키 값이 참이면 메타데이터 파일이 존재하고, 거짓이면 존재하지 않습니다.

    가능하다면 서버가 메타데이터 파일의 해시를 제공하는 것이 권장됩니다.

  • gpg-sig: 파일에 연결된 GPG 서명이 있는지를 나타내는 불리언 역할을 하는 선택적 키입니다. 서명 파일의 URL은 PEP 503에 지정된 형식({file_url}.asc)을 따릅니다. 이 키가 없으면 서명이 존재할 수도 있고 존재하지 않을 수도 있습니다.
  • yanked: 파일이 철회되었는지를 나타내는 불리언이거나, 특정 이유로 파일이 철회되었음을 나타내는 비어 있지 않은 임의의 문자열일 수 있는 선택적 키입니다. yanked 키가 존재하고 참인 값이면 url 필드가 가리키는 파일이 PEP 592에 따른 “Yanked” 상태임을 나타내는 것으로 해석해야 합니다.

예시는 다음과 같습니다.

{
  "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
    }
  ]
}

Note

files 키는 배열이므로 어떤 형태로든 순서가 있어야 하지만, PEP 503이나 이 PEP는 특정 순서나 요청마다 순서가 일관적이어야 한다고 요구하지 않습니다. 개념적으로는 이를 집합으로 생각하는 것이 가장 좋지만, JSON과 HTML 모두 집합을 표현하는 기능이 없습니다.

콘텐츠 유형

이 PEP는 Simple API의 모든 응답이 응답의 내용(Simple 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

위 항목 외에도 latest라는 특수한 “메타” 버전을 지원하며, 이는 클라이언트가 해당 버전이 무엇인지 미리 알 필요 없이 절대적으로 최신 버전을 요청할 수 있도록 합니다. 그러나 클라이언트는 지원하는 버전을 명시적으로 지정하는 것이 좋습니다.

기존 PEP 503 API 응답이 text/html 콘텐츠 유형을 사용할 것으로 예상하는 기존 클라이언트를 지원하기 위해, 이 PEP에서는 text/htmlapplication/vnd.pypi.simple.v1+html 콘텐츠 유형의 별칭으로 추가 정의합니다.

버전 + 형식 선택

이제 가능한 직렬화가 여러 가지이므로, 클라이언트가 이해할 수 있는 직렬화 형식을 나타낼 수 있도록 하는 메커니즘이 필요합니다. 또한 클라이언트가 이전 API 버전을 기대하고 있는 상황을 방해하지 않고 API에 새로운 주요 버전을 추가할 수 있다면 유용할 것입니다.

이를 위해 이 PEP에서는 HTTP의 서버 주도 콘텐츠 협상 사용을 표준화합니다.

이 PEP에서 서버 주도 콘텐츠 협상의 전체 내용을 상세히 설명하지는 않지만, 대략적인 흐름은 다음과 같습니다.

  1. 클라이언트는 이해할 수 있는 모든 버전+형식 콘텐츠 유형을 나열한 Accept 헤더가 포함된 HTTP 요청을 보냅니다.
  2. 서버는 해당 헤더를 검사하고 나열된 콘텐츠 유형 중 하나를 선택한 다음, 선택한 콘텐츠 유형을 사용하여 응답을 반환합니다(Accept헤더가 없는 경우를 Accept: */*로 간주합니다).
  3. 서버가 Accept헤더의 콘텐츠 유형을 하나도 지원하지 않는 경우, 응답 방법에 대해 다음 3가지 옵션 중 하나를 선택할 수 있습니다.
    1. 클라이언트가 요청한 것과 다른 기본 콘텐츠 유형을 선택하고 해당 유형으로 응답을 반환합니다.
    2. 요청된 콘텐츠 유형을 사용할 수 없으며 서버가 응답할 기본 콘텐츠 유형을 선택할 수 없거나 선택하지 않으려 한다는 것을 나타내기 위해 HTTP 406 Not Acceptable 응답을 반환합니다. 요청된 콘텐츠 유형을 사용할 수 있었지만, 서버가 응답할 기본 콘텐츠 유형을 선택할 수 없었거나 선택하지 않으려 했습니다.
    3. 선택할 수 있었던 모든 가능한 응답의 목록을 포함하는 HTTP 300 Multiple Choices 응답을 반환합니다. 선택할 수 있었던 모든 가능한 응답입니다.
  4. 클라이언트는 응답을 해석하고 서버가 반환했을 수 있는 여러 유형의 응답을 처리합니다.

이 PEP는 서버가 반환할 수 없는 콘텐츠 유형을 처리할 때 어떤 선택을 해야 하는지 지정하지 않으며, 클라이언트는 해당 클라이언트에 가장 합리적인 방식으로 가능한 모든 응답을 처리할 준비가 되어 있어야 합니다.

그러나 300 Multiple Choices 응답을 해석하는 표준 형식이 없으므로, 클라이언트가 다른 콘텐츠 유형을 이해하고 요청할 방법이 없다는 점에서 이 PEP는 서버가 해당 옵션을 사용하는 것을 강력히 권장하지 않습니다. 또한 클라이언트가 다른 콘텐츠 유형을 이해할 수 있을 가능성도 낮으므로, 기껏해야 이 응답은 406 Not Acceptable 오류와 동일하게 처리될 가능성이 높습니다.

이 PEP는 메타 버전 latest를 사용하는 경우 서버가 응답에 포함된 실제 버전에 해당하는 콘텐츠 유형으로 반드시 응답하도록 요구합니다 (즉, Accept: application/vnd.pypi.simple.latest+json 요청이 v1.x 응답을 반환한다면 Content-Typeapplication/vnd.pypi.simple.v1+json이어야 합니다).

Accept 헤더는 클라이언트가 이해하고 처리할 수 있는 콘텐츠 유형을 쉼표로 구분한 목록입니다. 요청하는 각 콘텐츠 유형에 대해 세 가지 형식을 지원합니다:

  • $type/$subtype
  • $type/*
  • */*

버전+형식을 선택하는 데 사용하기에는 $type/$subtype이 가장 유용합니다. 원하는 버전과 형식을 실제로 지정할 수 있는 유일한 방법이기 때문입니다.

Accept 헤더에 나열된 콘텐츠 유형의 순서에는 특별한 의미가 없으며, 서버는 응답할 때 이들 모두를 동일하게 유효한 것으로 간주해야 합니다. SHOULD합니다. 클라이언트가 특정 콘텐츠 유형을 다른 유형보다 선호한다고 지정하려는 경우, Accept 헤더의 quality value 구문을 사용할 수 있습니다.

이를 통해 클라이언트는 Accept 헤더의 특정 항목에 우선순위를 지정할 수 있으며, ;q= 뒤에 0 이상 1 이하의 값을 최대 소수점 이하 3자리까지 덧붙이면 됩니다. 이 값을 해석할 때 품질 값이 더 높은 항목이 더 낮은 항목보다 우선하며, 품질 값이 없는 항목은 기본적으로 품질 값 1을 사용합니다.

그러나 클라이언트는 요청한 우선순위와 관계없이 서버가 요청한 콘텐츠 유형 중 어떤 것이든 선택할 수 있으며, 심지어 요청하지 않은 콘텐츠 유형을 반환할 수도 있다는 점을 유념해야 합니다.

클라이언트가 API 요청으로 받은 응답의 콘텐츠 유형을 확인할 수 있도록, 이 PEP는 서버가 응답의 콘텐츠 유형을 나타내는 Content-Type 헤더를 항상 포함하도록 요구합니다. 이는 기술적으로 하위 호환성이 없는 변경이지만, 실제로는 pip has been enforcing this requirement 하므로 실제 문제가 발생할 위험은 낮습니다.

클라이언트가 작동할 수 있는 방식의 예는 다음과 같습니다.

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 헤더에서 원하지 않는 콘텐츠 유형을 제거하고 해당 유형을 수신하면 오류로 처리하면 됩니다.

대체 협상 메커니즘

HTTP의 콘텐츠 협상은 클라이언트가 이해할 수 있는 HTTP 응답을 받도록 클라이언트와 서버가 조정하는 표준적인 방법으로 간주되지만, 이 메커니즘만으로는 충분하지 않을 수 있는 상황이 있습니다. 이러한 경우에는 이 PEP의 대체 협상 메커니즘을 선택적으로 대신 사용할 수 있습니다.

URL 매개변수

Simple API를 구현하는 서버는 클라이언트가 URL의 특정 버전을 요청할 수 있도록 format이라는 URL 매개변수를 지원하도록 선택할 수 있습니다.

format 매개변수의 값은 유효한 콘텐츠 유형 중 하나이어야 합니다. 여러 콘텐츠 유형, 와일드카드, 품질 값 등을 전달하는 것은 지원되지 않습니다.

이 매개변수의 지원은 선택 사항이며, 클라이언트는 API와 상호 작용할 때 이에 SHOULD NOT 의존해야 합니다. 이 협상 메커니즘은 브라우저에서 사람이 API를 더 쉽게 탐색하거나, 문서 또는 메모에서 특정 버전+형식으로 연결할 수 있도록 하기 위한 것입니다.

이 매개변수를 지원하지 않는 서버는 해당 매개변수가 있으면 오류를 반환하거나, 단순히 매개변수의 존재를 무시할 수 있습니다.

서버가 이 매개변수를 구현하는 경우, 클라이언트의 Accept 헤더에 있는 어떤 값보다도 이 매개변수를 우선해야 SHOULD 하며, 서버가 요청된 형식을 지원하지 않는 경우 Accept 헤더로 대체하거나, 일반적인 서버 주도 콘텐츠 협상이 사용하는 오류 조건(예: 406 Not Available, 303 Multiple Choices) 중 하나를 선택하거나, 반환할 기본 유형을 선택할 수 있습니다.

엔드포인트 구성

이 옵션은 기술적으로 특별한 옵션이 전혀 아니며, 콘텐츠 협상을 사용하고 서버가 사용 가능한 콘텐츠 유형 중 기본 유형을 선택하도록 허용한 데 따른 자연스러운 결과일 뿐입니다.

서버가 서버 주도 콘텐츠 협상을 구현할 의사가 없거나 구현할 수 없으며, 대신 사용자가 원하는 버전을 선택하도록 클라이언트를 명시적으로 구성하게 하려는 경우에도 그러한 구성이 지원됩니다.

이를 활성화하려면 서버는 지원하려는 각 버전+형식에 대해 여러 엔드포인트(예를 들어 /simple/v1+html/ 및/또는 /simple/v1+json/)를 만들어야 합니다. 해당 엔드포인트에서 하나의 콘텐츠 유형(또는 콘텐츠 유형의 일부 집합)만 지원하는 저장소 사본을 호스팅할 수 있습니다. 클라이언트가 Accept 헤더를 사용하여 요청하면 서버는 이를 무시하고 해당 엔드포인트에 대응하는 콘텐츠 유형을 반환할 수 있습니다.

특정 구성을 요구하려는 클라이언트는 특정 저장소 URL이 어떤 버전+형식으로 구성되었는지 추적하고, 해당 서버에 요청할 때 올바른 콘텐츠 유형만 포함하는 Accept 헤더를 전송할 수 있습니다.

TUF 지원 - PEP 458

PEP 458은 모든 API 응답이 해시 가능해야 하며 저장소 루트에 상대적인 경로로 고유하게 식별될 수 있어야 한다고 요구합니다. Simple API 저장소의 경우 대상 경로는 API의 Root입니다(예: PyPI에서는 /simple/). TUF 클라이언트는 서로 다른 해시를 갖는 여러 표현이 하나의 대상에 존재할 수 있다는 사실을 처리할 수 없으므로, 표준 HTTP 클라이언트를 직접 사용하는 대신 TUF 클라이언트를 사용하여 API에 액세스할 때 문제가 발생합니다.

PEP 458은 Simple API의 대상 경로가 무엇이어야 하는지 지정하지 않지만, TUF는 대상 경로가 “파일과 같은” 형태여야 한다고 요구합니다. 즉, simple/PROJECT/와 같은 경로는 기술적으로 디렉터리를 가리키므로 허용되지 않습니다.

다행히도 대상 경로는 Simple API에서 가져오는 URL과 실제로 일치할 필요가 없으며, 가져오기 코드가 실제로 가져와야 할 URL로 변환하는 방법을 알고 있는 기호일 뿐이어도 됩니다. 이와 같은 방식은 Accept 헤더와 같은 실제 HTTP 요청의 다른 측면에도 적용될 수 있습니다.

궁극적으로 디렉터리를 파일 이름에 매핑하는 방법을 결정하는 것은 이 PEP의 범위를 벗어나지만(PEP 458의 범위에는 포함됩니다), 이 PEP에서는 이를 PEP 458 메타데이터 내부에 정확히 표현하는 방법에 대한 결정을 유보합니다.

그러나 현재 PEP 458 구현을 시도하는 pip 대상 WIP 브랜치는 simple/PROJECT/index.html과 같은 대상 경로를 사용하는 것으로 보입니다. simple/PROJECT/vnd.pypi.simple.vN.FORMAT과 같은 형식을 사용하여 API 버전과 직렬화 형식을 포함하도록 수정할 수 있습니다. 따라서 v1 HTML 형식은 simple/PROJECT/vnd.pypi.simple.v1.html이 되고 v1 JSON 형식은 simple/PROJECT/vnd.pypi.simple.v1.json이 됩니다.

이 경우 TUF를 통해 상호 작용할 때 text/htmlapplication/vnd.pypi.simple.v1+html의 별칭이므로, 더 명시적인 이름으로 정규화하는 것이 가장 합리적일 것입니다.

마찬가지로 latest 메타버전은 대상에 포함해서는 안 되며, 명시적으로 선언된 버전만 지원해야 합니다.

권장 사항

이 절은 비규범적이며, 이 PEP를 구현하는 작업에 가장 적합한 기본 구현 결정을 PEP 작성자들이 무엇이라고 생각하는지 나타내지만, 이러한 결정을 따라야 한다는 어떠한 요구 사항도 나타내지 않습니다.

이러한 결정은 최대한의 호환성을 유지하면서 API의 최신 버전으로 전환할 수 있는 요청 수를 극대화하도록 선택되었습니다. 또한 API를 사용할 때 클라이언트가 가능한 최선의 선택을 하도록 유도하는 안전장치를 제공하려고 했습니다.

서버는 다음을 수행하는 것이 좋습니다.

  • 합리적으로 가능한 한, 또는 HTML 응답을 사용하는 유의미한 트래픽을 수신하는 동안 최소한 그 기간만큼은 서버 주도 콘텐츠 협상을 사용하여 이 PEP에 설명된 세 가지 콘텐츠 유형을 모두 지원하십시오.
  • 처리할 수 있는 콘텐츠 유형이 하나도 포함되지 않은 Accept 헤더를 수신하면, 서버는 300 Multiple Choice 응답을 반환해서는 안 되며 대신 406 Not Acceptable 응답을 반환해야 합니다.
    • 그러나 엔드포인트 구성을 사용하기로 선택한 경우에는 해당 엔드포인트에 대해 예상되는 콘텐츠 유형의 200 OK 응답을 반환하는 것을 우선하십시오.
  • 허용 가능한 버전을 선택할 때 서버는 클라이언트가 지원하는 가장 높은 버전을 선택해야 하며, 클라이언트 요청의 구체성과 클라이언트가 표시한 품질 우선순위 값을 고려하여 표현력과 기능이 가장 풍부한 직렬화 형식을 선택해야 합니다. 또한 최후의 수단으로만 text/html 콘텐츠 유형을 사용해야 합니다.

클라이언트는 다음을 수행하는 것이 좋습니다.

  • 합리적으로 가능한 한 서버 주도 콘텐츠 협상을 사용하여 이 PEP에 설명된 세 가지 콘텐츠 유형을 모두 지원하십시오.
  • Accept 헤더를 구성할 때 지원하는 모든 콘텐츠 유형을 포함하십시오.

    일반적으로 콘텐츠 유형에 대한 품질 우선순위 값을 포함하지 마십시오. 다만 서버가 고려하기를 원하는 구현별 이유가 있는 경우는 예외입니다(예를 들어 표준 라이브러리 HTML 파서를 사용하고 있어 일부 특수한 경우에 파싱할 수 없는 HTML 응답 유형이 있을까 우려되는 경우).

    이 권고의 한 가지 예외는 요청하는 콘텐츠 유형이 유일한 콘텐츠 유형이 아닌 한, 레거시 text/html 콘텐츠 유형에 ;q=0.01 값을 포함하는 것이 좋습니다.

  • 일반적인 작업 중에는 latest 메타 버전을 사용하는 대신 찾는 버전을 명시적으로 선택하십시오.
  • 응답의 Content-Type을 확인하고 예상한 항목과 일치하는지 확인하십시오.

FAQ

이는 PyPI가 HTML/PEP 503 지원을 중단할 계획이라는 의미입니까?

아니요. PyPI는 현재 PEP 503 또는 HTML 응답에 대한 지원을 중단할 계획이 없습니다.

이 PEP는 저장소에 그러한 작업을 수행할 수 있는 유연성을 제공하지만, 이는 주로 Endpoint Configuration 메커니즘 사용과 같은 기능이 작동할 수 있도록 하고, 클라이언트가 향후 어느 시점에 HTML 지원을 원활하게 중단하는 데 방해가 될 가정을 하지 않도록 하기 위해 존재합니다.

기존 HTML 응답은 PyPI에 유지 관리 부담을 거의 주지 않으며, 이를 제거해야 할 절박한 필요도 없습니다. 이를 제거했을 때 얻는 유일한 실질적인 이점은 CDN에 캐시되는 항목 수를 줄이는 것입니다.

향후 PyPI가 이를 지원 중단하기를 does 원한다면, 그렇게 하는 것은 거의 확실히 PEP의 주제가 되거나, 최소한 공개적이고 개방적인 논의의 주제가 될 것이며, 최종 사용자에게 미치는 영향을 보여 주는 지표를 바탕으로 결정될 것입니다.

X 형식 대신 JSON을 사용하는 이유는 무엇입니까?

JSON 파서는 대부분의 언어에서, 모든 언어는 아니더라도, 널리 사용할 수 있습니다. Python 표준 라이브러리에서도 JSON 파서를 사용할 수 있습니다. 완벽한 형식은 아니지만 충분히 좋습니다.

X 기능을 추가하지 않는 이유는 무엇입니까?

이 PEP의 일반적인 목표는 변경하거나 추가하는 내용을 매우 적게 유지하는 것입니다. 대신 HTML 응답에 포함된 기존 정보를 합리적인 JSON 표현으로 변환하는 데 주로 집중할 것입니다. 여기에는 패키징 도구에 필요한 PEP 658 메타데이터가 포함됩니다.

이 PEP에서 추가되는 유일하게 실질적으로 새로운 기능은 하나의 파일에 여러 해시를 사용할 수 있는 기능입니다. 현재 메커니즘은 하나의 해시로 제한되어 있어 과거에 해시를 (md5에서 sha256으로) 마이그레이션하기가 고통스러웠고, 해시를 딕셔너리로 만들고 여러 해시를 허용하는 비용은 상당히 낮기 때문에 이렇게 했습니다.

이 API는 일반적으로 새 키를 추가하여 더욱 확장할 수 있도록 설계되었으므로, 설치 프로그램에 필요한 새로운 데이터가 생기면 향후 PEP에서 이를 쉽게 제공할 수 있습니다.

URL에 파일 이름이 이미 포함되어 있는데 파일 이름을 포함하는 이유는 무엇입니까?

filename 키를 제거하고 클라이언트가 URL에서 해당 정보를 가져오도록 하면 응답 크기를 줄일 수 있습니다.

현재 이 PEP는 주로 PEP 503이 링크의 앵커 태그를 통해 파일 이름을 사용할 수 있어야 한다고 명시적으로 요구했기 때문에 그렇게 하지 않기로 선택합니다. 다만 이는 대체로 그곳에 무언가가 있어야 했기 때문입니다. 실제로 사용되는 저장소가 URL의 마지막 부분에 항상 파일 이름을 포함하는지, 아니면 앵커 태그의 파일 이름에 의존하는지는 명확하지 않습니다.

또한 짧고 보기 좋은 고유 식별자를 얻을 수 있으므로 사람이 응답을 읽기에도 약간 더 좋습니다.

파일 이름이 URL에 포함되도록 의무화해도 된다는 합리적인 확신을 얻는다면, 이 데이터를 제거하여 JSON 응답의 크기를 줄일 수 있습니다.

파일 이름에서 다른 정보도 분리해 내지 않는 이유는 무엇입니까?

현재 클라이언트는 파일 이름에서 프로젝트 이름, 버전, ABI 태그 등 여러 정보를 파싱해야 합니다. 이러한 정보를 분리하여 파일 객체의 키로 추가할 수 있습니다.

그렇게 하면 API 응답의 크기가 증가하고, API가 어떻게 동작하는지와 관계없이 대부분의 클라이언트는 어차피 파일 이름에서 해당 정보를 파싱할 수 있어야 하므로, 이 PEP에서는 그렇게 하지 않기로 했습니다. 따라서 해당 기능은 클라이언트 내부에 유지하는 것이 합리적입니다.

여러 URL 대신 콘텐츠 협상을 사용하는 이유는 무엇입니까?

이를 구현하는 또 다른 합리적인 방법은 API 경로를 복제하고 JSON임을 나타내는 표식을 URL 자체에 포함하는 것입니다. 예를 들어 URL을 /simple/foo.json, /simple/_index.json 등과 같은 형태로 만드는 것입니다.

이렇게 하면 TUF 통합 및 저장소의 완전한 정적 제공과 같은 일부 작업이 더 간단해집니다(.json 파일은 그대로 작성해 둘 수 있기 때문입니다).

그러나 여기에는 두 가지 상당히 중대한 문제가 있습니다.

  • 현재 URL 구조는 프로젝트 목록을 제공하기 위해 “루트”를 나타내는 URL인 /가 있다는 사실에 의존합니다. JSON과 HTML에 별도의 URL을 사용하려면 두 개의 루트 URL을 마련할 방법을 고안해야 합니다.

    /이 HTML이고 /_index.json이 JSON인 방식은, _index가 유효한 프로젝트 이름이 아니므로 작동할 수 있습니다. 하지만 저장소에서 HTML 지원을 제거하려는 경우 /이 HTML인 방식은 잘 작동하지 않습니다.

    또 다른 방법으로는 기존의 모든 HTML URL을 네임스페이스 아래로 옮기면서 JSON을 위한 새 네임스페이스를 만드는 것이 있습니다. /<project>/가 정의되어 있으므로 이러한 네임스페이스를 유효한 프로젝트 이름으로 사용할 수 없게 해야 합니다. 따라서 /_html//_json/와 같은 방식을 사용할 수 있으며, 네임스페이스가 지정되지 않은 URL은 해당 저장소의 “default”로 리디렉션하면 됩니다. 이는 일반적으로 HTML이며, HTML을 비활성화한 경우에는 JSON입니다.

  • URL을 분리하면 추가 HTTP 요청을 통해 JSON URL의 존재 여부를 확인하지 않고는 저장소가 JSON URL을 지원하는지 구성 없이 검색할 수 있는 좋은 방법이 없습니다.

    이를 가장 단순하게 구현하는 방법은 JSON URL을 요청하고 모든 단일 요청에 대해 HTML URL로 대체하는 것이지만, 이는 성능이 매우 나쁘고 추가 HTTP 요청을 최소화한다는 목표를 위반합니다.

    가장 가능성 높은 구현은 무엇이 지원되는지를 나타내는 일종의 저장소 수준 구성 파일을 만드는 것입니다. 위와 동일한 네임스페이스 문제가 발생하므로 동일한 해결책을 적용할 수 있습니다. /_config.json 같은 파일에 해당 데이터를 저장하고, 클라이언트가 먼저 해당 파일에 HTTP 요청을 보낸 다음 파일이 존재하면 내려받아 분석하여 이 특정 저장소의 기능을 파악할 수 있습니다.

  • Accept를 사용하면 이 필드에 버전 관리도 추가할 수 있습니다.

종합하면, 이 PEP는 이 세 가지 문제가 결합되어 별도의 API 경로를 사용하는 것보다 콘텐츠 협상을 통해 데이터의 가장 적합한 표현을 선택하는 편이 더 바람직하다고 판단합니다.

이는 정적 서버가 더 이상 지원되지 않는다는 의미입니까?

요컨대 그렇지 않습니다. 정적 서버는 이 PEP에서 여전히 (거의) 완전히 지원됩니다.

구체적인 지원 방식은 해당 정적 서버에 따라 달라집니다. 예를 들면 다음과 같습니다.

  • S3: S3는 사용자 지정 콘텐츠 유형을 완전히 지원하지만, 어떠한 형태의 콘텐츠 협상도 지원하지 않습니다. S3에서 호스팅되는 서버를 운영하려면 “Endpoint configuration” 방식의 협상을 사용해야 하며, 사용자는 클라이언트를 명시적으로 구성해야 합니다.
  • GitHub Pages: GitHub Pages는 사용자 지정 콘텐츠 유형을 지원하지 않으므로 현재 S3 방식은 사용할 수 없습니다. 따라서 text/html 저장소만 작동합니다.
  • Apache: Apache는 서버 주도 콘텐츠 협상을 완전히 지원하므로, 사용자 지정 콘텐츠 유형을 특정 확장자에 매핑하도록 구성하기만 하면 됩니다.

text/html처럼 application/json 별칭을 추가하지 않는 이유는 무엇입니까?

이 PEP는 클라이언트와 서버 모두 사용 중인 API 응답 유형을 명시적으로 나타내는 것이 가장 좋다고 판단하며, application/json과 같은 콘텐츠 유형은 명시성의 정반대입니다.

text/html 별칭은 주로 기존 API 사용자가 지금과 동일하게 계속 작동할 수 있도록 하기 위한 타협책으로 존재합니다. application/json 콘텐츠 유형으로 Simple API를 사용하는 기존 클라이언트에 대해서는 그러한 기대가 없습니다.

또한 application/json에는 버전 정보가 없으므로, Simple API의 2.x 버전이 언젠가 등장하면 결정을 내려야 합니다. application/json이 하위 호환성을 유지하여 계속 application/vnd.pypi.simple.v1+json의 별칭으로 남아야 합니까, 아니면 application/vnd.pypi.simple.v2+json의 별칭으로 갱신해야 합니까?

HTML은 레거시 형식으로 남을 것이며 새로운 기능을 any 추가하지 않을 가능성이 높고, 호환성을 깨뜨려야 하는 기능은 더욱 추가하지 않을 것이라는 가정이 있으므로 이 문제는 text/html에는 존재하지 않습니다. 따라서 application/vnd.pypi.simple.v1+html의 별칭으로 사용하는 것은 사실상 application/vnd.pypi.simple.latest+html의 별칭으로 사용하는 것과 같습니다. 1.x이 존재할 유일한 HTML 버전일 가능성이 높기 때문입니다.

application/json 콘텐츠 유형을 추가할 때의 가장 큰 이점은 사용자 지정 콘텐츠 유형을 허용하지 않고 미리 설정된 콘텐츠 유형 중 하나를 선택하도록 요구하는 환경이 있다는 점입니다. 대표적인 예가 GitHub Pages입니다. 이 PEP에서 application/json을 지원하지 않으면 GitHub가 application/vnd.pypi.simple.v1+json 콘텐츠 유형을 추가하지 않는 한 정적 저장소를 더 이상 GitHub Pages에서 호스팅할 수 없습니다.

이 PEP는 현재 해당 콘텐츠 유형 별칭을 추가할 만큼 이점이 크지 않으며, 이를 포함하면 상황을 잘 모르는 사람이 실수로 선택하기를 기다리는 풋건이 될 가능성이 높다고 판단합니다. 특히 향후 언제든 추가할 수 있지만, 무언가를 제거하는 일은 훨씬 더 어렵기 때문입니다.

application/vnd.pypi.simple.v1+html을 추가하는 이유는 무엇입니까?

이 PEP는 API의 HTML 버전이 레거시가 될 것으로 예상하므로, application/vnd.pypi.simple.v1+html 콘텐츠 유형을 추가하지 않고 해당 용도로 text/html만 사용하는 방안도 고려할 수 있습니다.

이 PEP에서는 새로운 콘텐츠 유형을 추가하는 것이 전반적으로 더 낫다고 결정했습니다. 이렇게 하면 레거시 형식도 더 자체적으로 설명 가능해지고, 두 형식이 서로 더 일관성을 갖게 되기 때문입니다. 전반적으로 +html 버전이 존재하지 않는다면 오히려 더 혼란스럽다고 생각합니다.

v1.1이나 v2.0이 아니라 왜 v1.0입니까?

이 PEP는 기존 v1.0 API를 읽을 수 있었던 클라이언트와 여전히 완전히 하위 호환되므로, 이러한 변경이 적용된 후에도 해당 클라이언트는 API를 계속 읽을 수 있습니다. 관련 PEP 629에서 주 버전을 올리기 위한 조건은 다음과 같습니다.

주 버전을 올리는 것은 기존 클라이언트가 더 이상 API를 의미 있게 사용할 수 없을 것으로 예상되는 하위 호환성이 없는 변경임을 알리는 데 사용합니다.

이 PEP의 변경 사항은 그 기준을 충족하지 않습니다. 기존 클라이언트가 더 이상 API를 의미 있게 사용할 수 없을 것으로 예상될 만한 방식으로 변경된 사항은 없습니다.

따라서 버전은 여전히 v1.x 계열에 속해야 합니다.

버전을 v1.1로 할지 v1.0으로 할지는 더 흥미로운 문제이며, 이를 살펴보는 방법에는 몇 가지가 있습니다.

  • API에 새로운 기능을 공개했습니다(프로젝트 페이지의 프로젝트 이름, 여러 해시). 이는 마이너 버전을 올려야 한다는 신호입니다.
  • 새로운 기능은 JSON 직렬화 내부에만 완전히 존재합니다. 따라서 현재 HTML 1.0 페이지를 요청하는 클라이언트는 어떤 경우에도 새로운 기능을 볼 수 없으며, 이러한 클라이언트에게는 사실상 여전히 v1.0입니다.
  • 아직 어떤 주요 클라이언트도 PEP 629 지원을 구현하지 않았습니다. 따라서 마이너 버전 번호는 현재로서는 대체로 학술적인 문제에 불과합니다. 마이너 버전 번호는 클라이언트가 최종 사용자에게 피드백을 제공할 수 있도록 존재하기 때문입니다.

위의 두 번째와 세 번째 요점 때문에 첫 번째 요점은 다소 무의미해집니다. 따라서 모든 것을 그냥 v1.0이라고 부르고 향후 버전 업데이트를 더 엄격하게 적용하는 편이 더 합리적입니다.

부록 1: 다룰 사용 사례 조사

이는 새로운 API의 최초 잠재 사용자 두 곳인 pip, PyPI, bandersnatch의 유지 관리자 간 논의를 통해 수행했습니다. 이들이 현재 Simple + JSON API를 사용하는 방식 또는 현재 사용할 계획은 다음과 같습니다.

  • pip:
    • 특정 릴리스의 모든 파일 목록
    • 각 개별 아티팩트의 메타데이터:
      • 철회되었습니까? (data-yanked)
      • python-requires는 무엇입니까? (data-python-requires)
      • 이 파일의 해시는 무엇입니까? (현재는 URL에 해시가 포함되어 있음)
      • 전체 메타데이터 (data-dist-info-metadata)
      • [보너스] 가능한 경우 선언된 종속성은 무엇입니까(list-of-strings, 사용할 수 없으면 null)?
  • bandersnatch - 현재는 레거시 JSON API + XMLRPC만 사용합니다.
    • PyPI에서 복사하는 대신 Simple HTML을 생성합니다.
      • 새로운 API를 사용하면 이 방식이 변경되어 PyPI에서 이러한 API 자산을 그대로 가져올 수도 있습니다.
    • 특정 릴리스의 모든 파일 목록입니다.
      • 다운로드할 릴리스 파일의 URL을 계산합니다.
    • 각 개별 아티팩트의 메타데이터입니다.
      • 현재 미러 저장소(disk/S3)에 JSON을 기록합니다.
        • 필요한 메타데이터 사용(Package class를 통해):
          • metadata["info"]
          • metadata["last_serial"]
          • metadata["releases"]
            • 다이제스트
            • URL
    • XML-RPC 호출(폐기하고 싶지만 Simple API에 포함해서는 안 된다고 생각합니다)
      • [보너스] 직렬 번호 X 이후(또는 전체)의 패키지 가져오기
        • XML-RPC 호출: changelog_since_serial
      • [보너스] 직렬 번호가 있는 모든 패키지 가져오기
        • XML-RPC 호출: list_packages_with_serial

부록 2: 개략적인 기저 데이터 모델

이는 서버, 클라이언트 또는 와이어 형식과 완벽하게 일치하도록 의도된 것이 아닙니다. 오히려 이는 개념적 모델이며, PEP 503, PEP 592, PEP 629, PEP 658 및 현재 이 PEP인 PEP 691을 거치며 발전해 온 저장소 API의 기저 추상 모델을 더욱 명시적으로 나타내도록 코드로 작성한 것입니다.

기존 HTML과 이러한 모델의 새로운 JSON 직렬화는 이러한 기저 개념 모델이 실제 와이어 형식에 어떻게 매핑되는지를 나타냅니다.

서버 또는 클라이언트가 이 데이터를 어떻게 모델링할지는 이 PEP의 범위에 포함되지 않습니다.

@dataclass
class Meta:
    api_version: Literal["1.0"]


@dataclass
class Project:
    # Normalized or Non-Normalized Name
    name: str
    # Computed in JSON, Included in HTML
    url: str | None


@dataclass
class File:
    filename: str
    url: str
    # Limited to a len() of 1 in HTML
    hashes: dict[str, str]
    gpg_sig: bool | None
    requires_python: str | None


@dataclass
class PEP592File(File):
    yanked: bool | str

@dataclass
class PEP658File(PEP592File):
    # Limited to a len() of 1 in HTML
    dist_info_metadata: bool | dict[str, str]


# Simple Index page (/simple/)
@dataclass
class PEP503_Index:
    projects: set[Project]


@dataclass
class PEP629_Index(PEP503_Index):
    meta: Meta


@dataclass
class Index(PEP629_Index):
    pass


# Simple Detail page (/simple/$PROJECT/)
@dataclass
class PEP503_Detail:
    files: set[File]


@dataclass
class PEP592_Detail(PEP503_Detail):
    files: set[PEP592File]


@dataclass
class PEP629_Detail(PEP592_Detail):
    meta: Meta


@dataclass
class PEP658_Detail(PEP629_Detail):
  files: set[PEP658File]


@dataclass
class PEP691_Detail(PEP658_Detail):
    name: str  # Normalized Name


@dataclass
class Detail(PEP691_Detail):
    pass