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

Python 개선 제안 한국어 번역

PEP 751 – 설치 재현성을 위한 Python 의존성을 기록하는 파일 형식

Author:
Brett Cannon <brett at python.org>
Status:
Final
Type:
Standards Track
Topic:
Packaging
Created:
24-Jul-2024
Post-History:
25-Jul-2024 30-Oct-2024 15-Jan-2025
Replaces:
665
Resolution:
31-Mar-2025

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, pylock.toml Specification, is maintained on the PyPA specs page.

×

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

초록

이 PEP는 Python 환경에서 재현 가능한 설치를 가능하게 하도록 의존성을 지정하는 새로운 파일 형식을 제안합니다. 이 형식은 사람이 읽을 수 있고 기계가 생성할 수 있도록 설계되었습니다. 이 파일을 사용하는 설치 도구는 설치 시점에 의존성 해결을 수행할 필요 없이 설치할 항목을 계산할 수 있어야 합니다.

동기

현재 가상 환경에 설치해야 할 직접 및 간접 의존성을 지정하는 잠금 파일과 같은 변경 불가능한 기록을 만들기 위한 표준이 없습니다.

커뮤니티에 이 문제를 해결하는 잘 알려진 방법이 적어도 다섯 가지(PDM, pip freeze, pip-tools, Poetry, uv) 있다는 점을 고려하면, 일반적으로 잠금 파일에 대한 수요가 있는 것으로 보입니다.

이러한 도구는 지원하는 잠금 시나리오도 서로 다릅니다. 예를 들어, pip freeze와 pip-tools는 현재 환경을 위한 일회용 잠금 파일만 생성하는 반면, PDM, Poetry, uv는 여러 환경과 사용 사례를 한 번에 잠글 수 있거나 잠그려고 합니다. 일부 도구가 공급망 공격에 직면했을 때 안전한 기본값을 제공하지 않는 것에 대한 우려도 있습니다(예: 파일 해시 포함).

표준이 없다는 점에는 다른 단점도 있습니다. 예를 들어, 잠금 파일을 사용하려는 모든 도구는 지원할 형식을 선택해야 하므로 사용자가 지원받지 못하는 상황이 발생할 수 있습니다(예: Dependabot만 일부 도구를 지원하며, 사용자를 대신하여 의존성 설치를 수행할 수 있는 클라우드 제공업체도 마찬가지입니다). 또한 도구 간 이식성에도 영향을 주어 특정 공급업체에 종속됩니다. 호환성과 상호 운용성이 없으면 잠금 파일을 둘러싼 도구 생태계가 분절되어, 사용자와 도구 모두 처음부터 사용할 잠금 파일 형식을 선택해야 하므로 다른 형식으로 전환하거나 사용하는 데 비용이 발생합니다(예: 잠금 파일 감사 관련 도구). 단일 형식으로 통합하면 이러한 비용과 장벽이 제거됩니다.

커뮤니티에서 표준에 가장 가까운 것은 pip의 requirements files이며, 앞서 언급한 모든 도구가 이를 파일 형식으로 직접 사용하거나 내보냅니다(즉, requirements.txt). 안타깝게도 이 형식은 표준이 아니라 관례에 따라 지원됩니다. 또한 pip의 요구에 매우 맞춰 설계되어 유연성과 사용 편의성이 제한됩니다(예: 맞춤형 파일 형식). 마지막으로 기본적으로 안전하지도 않습니다(예: 파일 해시 지원은 전적으로 선택 기능이며, requirements 파일에 포함된 항목 외부의 다른 의존성을 찾지 않도록 pip에 지시해야 하는 등).

Note

PEP PEP 665에서 제시된 동기의 상당 부분은 이 PEP에도 적용됩니다.

근거

이 PEP에서 제안하는 파일 형식은 사람이 읽을 수 있도록 설계되었습니다. 이는 사람이 파일 내용을 감사하여 원치 않는 의존성이 잠금 파일에 포함되지 않았는지 확인할 수 있도록 하기 위한 것입니다.

또한 이 파일 형식은 설치 시점에 해결기를 필요로 하지 않도록 설계되었습니다. 따라서 잠금 파일을 사용할 때 무엇이 설치될지 판단하는 과정이 크게 단순해집니다. 또한 잠금 파일을 생성하는 것보다 훨씬 더 빈번하게 수행되는 설치가 더 빨라지는 결과로 이어져야 합니다.

파일의 데이터는 Python으로 작성되지 않은 도구에서도 사용할 수 있어야 합니다. 이를 통해 예를 들어 클라우드 호스팅 제공업체가 선호하는 프로그래밍 언어로 설치를 수행하는 자체 도구를 작성할 수 있습니다. 이는 잠금 파일을 작성하는 lockers와 잠금 파일로부터 설치를 수행하는 installers라는 개념을 도입합니다(두 역할은 동일한 도구일 수 있습니다).

파일 형식은 우수한 보안 기본값을 장려해야 합니다. 이 형식은 사람이 직접 작성하도록 의도된 것이 아니므로, 도구가 보안 관련 세부 정보를 제공하도록 하는 것은 합리적이며 비용이 큰 부담도 아닙니다.

잠금 파일로 사용될 때 잠금 파일의 내용은 requirements files 사용 사례의 대다수를 대체할 수 있어야 합니다(예: pip-toolspip freeze가 출력하는 내용). 이는 이 PEP에서 지정하는 파일 형식이 최소한 자체 내부 잠금 파일 형식을 가진 도구의 내보내기 대상으로 사용될 수 있음을 의미합니다.

잠금 파일은 single-usemulti-use일 수 있습니다. 단일 사용 잠금 파일은 requirements.txt 파일과 같은 것으로, 하나의 사용 사례 또는 목적에 사용됩니다(따라서 프로젝트에 서로 다른 사용 사례별로 여러 requirements 파일이 있는 것이 드물지 않습니다). 다중 사용 잠금 파일은 하나의 파일 안에서 여러 사용 사례를 나타내며, 흔히 extrasDependency Groups를 통해 표현됩니다. 따라서 이 PEP는 적절한 경우 extras 및 의존성 그룹을 지정할 수 있도록 Environment Markers에 대한 추가 사항을 지원합니다. 이를 통해 하나의 잠금 파일로 이러한 사례를 지원할 수 있습니다. 이는 잠재적인 잠금 파일 수를 줄일 뿐만 아니라, 하나의 패키지가 모든 사용 사례에서 일관되어야 할 때도 더 쉽게 해 줍니다(여러 단일 사용 잠금 파일을 사용하면 여러 잠금 파일 간의 조정이 필요합니다). 또한 이러한 지원은 이 PEP가 pyproject.toml 파일에 기록할 수 있는 패키지 설치 관련 데이터를 모두 지원함을 의미합니다. 이러한 지원을 통해 일부 도구가 내부 잠금 파일을 완전히 제거하고 이 PEP에서 지정하는 내용에만 의존할 수 있기를 기대합니다.

사양

파일 이름

잠금 파일의 이름은 pylock.toml이어야 하며, 잠금 파일에 이름을 지정하려 하거나 여러 잠금 파일이 존재하는 경우 정규 표현식 r"^pylock\.([^.]+)\.toml$"과 일치해야 합니다. .toml 파일 확장자를 사용하는 것은 편집기에서 구문 강조를 쉽게 하고, 파일 형식이 사람이 읽을 수 있도록 설계되었음을 다시 강조하기 위한 것입니다. 이름이 지정된 파일의 접두사와 접미사는 가능한 경우 쉽게 감지하고 제거할 수 있도록 소문자여야 합니다. 예를 들면 다음과 같습니다.

if len(filename) > 11 and filename.startswith("pylock.") and filename.endswith(".toml"):
    name = filename.removeprefix("pylock.").removesuffix(".toml")

잠금 파일에서 자동으로 설치하는 서비스는 다음을 검색할 것으로 예상됩니다.

  1. 서비스 이름을 포함하며 기본 설치를 수행하는 잠금 파일
  2. 서비스 이름을 가진 의존성 그룹이 있는 다중 사용 pylock.toml
  3. pylock.toml의 기본 설치

예를 들어 “spam”이라는 이름의 클라우드 호스팅 서비스는 먼저 pylock.spam.toml에서 설치하려고 하며, 해당 파일이 없으면 pylock.toml에서 설치한 다음, 존재하는 경우 사용할 “spam”이라는 의존성 그룹을 찾습니다.

잠금 파일은 해당 잠금 파일의 범위에 적절한 디렉터리에 있어야 합니다. 예를 들어 단일 pyproject.toml을 기준으로 잠그는 경우 pylock.toml은 같은 디렉터리에 배치됩니다. 잠금 파일이 모노레포의 여러 프로젝트를 포괄하는 경우 pylock.toml 파일은 잠그는 모든 프로젝트가 포함된 디렉터리에 있을 것으로 예상됩니다.

파일 형식

파일 형식은 TOML_입니다.

도구는 diff 출력의 잡음을 최소화할 수 있도록 잠금 파일을 일관된 방식으로 작성해야 합니다. 최상위 테이블을 포함한 테이블의 키는 일관된 순서로 기록해야 합니다(참고가 필요하다면 이 PEP는 키를 논리적인 순서로 기록하려고 했습니다). 또한 도구는 배열을 일관된 순서로 정렬해야 합니다. 인라인 테이블의 사용 또한 일관되게 유지해야 합니다.

lock-version

  • 형식: 문자열; 값은 "1.0"입니다.
  • 필수?: 예입니다.
  • 영감: Metadata-Version
  • 파일이 준수하는 파일 형식 버전을 기록하십시오.
  • 이 PEP는 초기 버전이자 향후 표준 업데이트로 변경되기 전까지 유효한 유일한 값으로 "1.0"을 지정합니다.
  • 도구가 주 버전은 지원하지만 부 버전은 지원하지 않는 경우, 알 수 없는 키가 발견되면 경고해야 합니다.
  • 도구가 주 버전을 지원하지 않는 경우 오류를 발생시켜야 합니다.

environments

  • 유형: 문자열 배열
  • 필수 여부: 아니요
  • 영감: uv
  • 잠금 파일이 호환되는 것으로 간주되는 Environment Markers 목록입니다.
  • 도구는 이해하기 쉽도록 상호 배타적이고 서로 겹치지 않는 환경 마커를 작성해야 합니다.

requires-python

  • 유형: 문자열
  • 필수 여부: 아니요
  • 영감: PDM, Poetry, uv
  • 잠금 파일이 지원하는 모든 환경에서 호환되는 최소 Python 버전, 즉 잠금 파일에 실행 가능한 최소 Python 버전에 대한 Requires-Python을 지정합니다.

extras

  • 유형: 문자열 배열
  • 필수 여부: 아니요; 기본값은 []입니다.
  • 영감: Provides-Extra (multiple use)
  • 이 잠금 파일이 지원하는 extras 목록입니다.
  • 잠금 파일 생성 도구는 extras 및 dependency groups를 지원하는 잠금 파일 작성을 지원하지 않도록 선택할 수 있습니다(즉, 도구가 일회용 잠금 파일 내보내기만 지원할 수 있습니다).
  • extras를 지원하는 도구는 dependency groups도 지원해야 합니다.
  • 도구는 잠금 파일을 생성하는 데 사용된 입력에 extras가 없었음을 나타내도록 이 키를 빈 배열로 명시적으로 설정해야 합니다(예: pyproject.toml 파일에 [project.optional-dependencies] 테이블이 없었던 경우). 이를 통해 잠금 파일이 겉보기에는 일회용으로만 보이더라도 실질적으로는 다용도임을 나타냅니다.

dependency-groups

  • 유형: 문자열 배열
  • 필수 여부: 아니요; 기본값은 []입니다.
  • 영감: Arbitrary tool configuration: the [tool] table
  • 이 잠금 파일이 공개적으로 지원하는 Dependency Groups 목록입니다(즉, 사용자가 도구의 UI를 통해 지정할 수 있을 것으로 예상되는 dependency groups입니다).
  • 잠금 파일 생성 도구는 extras 및 dependency groups를 지원하는 잠금 파일 작성을 지원하지 않도록 선택할 수 있습니다(즉, 도구가 일회용 잠금 파일 내보내기만 지원할 수 있습니다).
  • dependency groups를 지원하는 도구는 extras도 지원해야 합니다.
  • 도구는 잠금 파일을 생성하는 데 사용된 입력에 dependency groups가 없었음을 나타내도록 이 키를 빈 배열로 명시적으로 설정해야 합니다(예: pyproject.toml 파일에 [dependency-groups] 테이블이 없었던 경우). 이를 통해 잠금 파일이 겉보기에는 일회용으로만 보이더라도 실질적으로는 다용도임을 나타냅니다.

default-groups

  • 유형: 문자열 배열
  • 필수 여부: 아니요; 기본값은 []입니다
  • 참고: Poetry, PDM
  • 기본적으로 설치되어야 하는 항목을 나타내기 위한 합성 의존성 그룹의 이름입니다(예: project.dependencies가 암묵적으로 나타내는 항목).
  • packages.marker가 이러한 그룹의 존재를 필요로 하는 상황에서 사용하도록 되어 있습니다.
  • 이 키에 나열된 그룹은 dependency-groups에 나열해서는 안 됩니다. 이러한 그룹은 이름으로 사용자에게 직접 노출되는 것이 아니라 설치 관리자의 UI를 통해 노출되도록 되어 있기 때문입니다.

created-by

  • 유형: 문자열
  • 필수 여부: 예
  • 참고: 잠금 파일 이름에 도구 이름을 포함하는 도구
  • 잠금 파일을 생성하는 데 사용된 도구의 이름을 기록합니다.
  • 도구는 어떤 입력이 잠금 파일을 생성하는 데 사용되었는지 추론할 수 있도록 충분한 세부 정보를 [tool] 테이블에 기록할 수 있습니다.
  • 도구가 Python 패키지로 제공되는 경우, 도구를 쉽게 찾을 수 있도록 도구의 정규화된 이름을 기록해야 합니다.

[[packages]]

  • 유형: 테이블 배열
  • 필수 여부: 예
  • 참고: PDM, Poetry, uv
  • 설치될 수도 있는 모든 패키지를 포함하는 배열입니다.
  • 패키지는 서로 다른 데이터와 함께 여러 번 나열될 수 있지만, 설치할 모든 패키지는 설치 시점에 하나의 항목으로 좁혀져야 합니다.
packages.name
  • 유형: 문자열
  • 필수 여부: 예
  • 참고: Name
  • 정규화된 패키지의 이름입니다(Name normalization).
packages.version
  • 유형: 문자열
  • 필수 여부: 아니요
  • 참고: Version
  • 패키지의 버전입니다.
  • 버전이 안정적이라고 알려진 경우(즉, sdist 또는 wheels가 지정된 경우) 버전을 지정해야 합니다.
  • 사용된 코드와 일치한다고 보장할 수 없는 경우(즉, source tree가 사용된 경우) 버전을 포함해서는 안 됩니다.
packages.marker
  • 유형: 문자열
  • 필수 여부: 아니요
  • 영감: PDM
  • 패키지를 설치해야 하는 시점을 지정하는 환경 마커입니다.
packages.requires-python
[[packages.dependencies]]
  • 유형: 테이블 배열
  • 필수 여부: 아니요
  • 영감: PDM, Poetry, uv
  • 이 패키지의 직접 종속성인 [[packages]]의 다른 항목을 기록합니다.
  • 각 항목은 키별 비교를 수행할 때 모호함 없이 해당 패키지를 찾을 수 있도록, 어떤 다른 패키지 항목에 대응하는지 식별하는 데 필요한 최소한의 정보를 포함하는 테이블입니다(예를 들어 spam패키지에 두 항목이 있는 경우 {name = "spam", version = "1.0.0"}처럼 버전 번호를 포함하거나 {name = "spam", vcs = { url = "..."}처럼 소스로 지정할 수 있습니다).
  • 도구는 설치를 수행할 때 이 정보를 사용해서는 안 됩니다. 이 정보는 감사 목적으로만 제공됩니다.
[packages.vcs]
  • 유형: 테이블
  • 필수 여부: 아니요; packages.directory, packages.archive, packages.sdist, packages.wheels와 상호 배타적입니다
  • 영감: Direct URL Data Structure
  • 포함된 소스 트리에 대한 버전 관리 시스템 세부 정보를 기록합니다.
  • 도구는 잠금 및 설치 관점 모두에서 버전 관리 시스템을 지원하지 않도록 선택할 수 있습니다.
  • 도구는 사용 가능한 VCS 유형의 일부만 지원하도록 선택할 수 있습니다.
  • 도구는 사용자가 버전 관리 시스템 사용 여부를 선택할 수 있는 방법을 제공해야 합니다.
  • 버전 관리 시스템에서 설치하는 것은 직접 URL 참조에서 비롯된 것으로 간주합니다.
packages.vcs.type
  • 유형: 문자열; 지원되는 값은 Registered VCS에 지정되어 있습니다
  • 필수 여부: 예
  • 영감: VCS URLs
  • 사용되는 버전 관리 시스템의 유형입니다.
packages.vcs.url
  • 유형: 문자열
  • 필수?: path가 지정되지 않은 경우
  • 참고: VCS URLs
  • 소스 트리의 URL입니다.
packages.vcs.path
  • 유형: 문자열
  • 필수?: url이 지정되지 않은 경우
  • 참고: VCS URLs
  • 소스 트리의 로컬 디렉터리 경로입니다.
  • 상대 경로를 사용하는 경우 이 파일의 위치를 기준으로 한 상대 경로여야 합니다.
  • 경로가 상대 경로인 경우 이식성을 위해 POSIX 스타일 경로 구분자를 명시적으로 사용할 수 있습니다.
packages.vcs.requested-revision
  • 유형: 문자열
  • 필수?: 아니요
  • 참고: VCS URLs
  • 사용자가 요청한 브랜치/태그/참조/커밋/리비전 등입니다.
  • 이는 순전히 정보 제공용이며 Direct URL Data Structure를 작성하는 데 도움을 주기 위한 것입니다. 저장소를 체크아웃하는 데 사용해서는 안 됩니다.
packages.vcs.commit-id
  • 유형: 문자열
  • 필수?: 예
  • 참고: VCS URLs
  • 설치할 정확한 커밋/리비전 번호입니다.
  • VCS가 커밋 해시 기반 리비전 식별자를 지원하는 경우, 소스 코드의 변경 불가능한 버전을 참조하도록 해당 커밋 해시를 커밋 ID로 사용해야 합니다.
packages.vcs.subdirectory
  • 유형: 문자열
  • 필수?: 아니요
  • 참고: Projects in subdirectories
  • 프로젝트의 루트가 위치한 소스 트리 내의 하위 디렉터리입니다(예: pyproject.toml 파일의 위치).
  • 경로는 소스 트리 구조의 루트를 기준으로 한 상대 경로여야 합니다.
[packages.directory]
  • 유형: 테이블
  • 필수?: 아니요. packages.vcs, packages.archive, packages.sdistpackages.wheels와 상호 배타적입니다.
  • 영감: Local directories
  • 해당 디렉터리에 포함된 소스 트리의 로컬 디렉터리 세부 정보를 기록합니다.
  • 도구는 잠금 및 설치 관점 모두에서 로컬 디렉터리 지원을 선택적으로 제공하지 않을 수 있습니다.
  • 도구는 사용자가 로컬 디렉터리 사용 여부를 선택할 수 있는 방법을 제공해야 합니다.
  • 디렉터리에서 설치하는 것은 직접 URL 참조에서 비롯된 것으로 간주합니다.
packages.directory.path
  • 유형: 문자열
  • 필수 여부: 예
  • 영감: Local directories
  • 소스 트리가 위치한 로컬 디렉터리입니다.
  • 경로가 상대 경로인 경우 잠금 파일의 위치를 기준으로 하는 상대 경로여야 합니다.
  • 경로가 상대 경로인 경우 이식성을 위해 POSIX 스타일 경로 구분자를 사용할 수 있습니다.
packages.directory.editable
  • 유형: 불리언
  • 필수 여부: 아니요; 기본값은 false입니다
  • 영감: Local directories
  • 잠금 시점에 소스 트리가 편집 가능 설치였는지를 나타내는 플래그입니다.
  • 사용자 작업이나 컨텍스트에 따라 편집 가능 설치가 불필요하거나 바람직하지 않은 경우 설치 도구는 이 플래그를 무시할 수 있습니다(예: 개발 목적으로 마운트되지 않고 대신 운영 환경에 배포되어 읽기 전용으로 취급될 컨테이너 이미지).
packages.directory.subdirectory

packages.vcs.subdirectory를 참조하십시오.

[packages.archive]
  • 유형: 테이블
  • 필수 여부: 아니요
  • 영감: Archive URLs
  • 설치할 아카이브 파일에 대한 직접 참조입니다(소스 트리를 포함하는 다른 아카이브 형식뿐 아니라 휠 및 sdist도 포함할 수 있습니다).
  • 도구는 잠금 및 설치 관점 모두에서 아카이브 파일 지원을 선택적으로 제공하지 않을 수 있습니다.
  • 도구는 사용자가 아카이브 파일 사용 여부를 선택할 수 있는 방법을 제공해야 합니다.
  • 아카이브 파일에서 설치하는 것은 직접 URL 참조에서 비롯된 것으로 간주합니다.
packages.archive.url

packages.vcs.url을 참조하십시오.

packages.archive.path

packages.vcs.path를 참조하십시오.

packages.archive.size
  • 유형: 정수
  • 필수 여부: 아니요
  • 영감: uv, Simple repository API
  • 아카이브 파일의 크기입니다.
  • 가능한 경우 도구는 파일 크기를 제공해야 합니다(예: 파일 크기를 HEAD HTTP 요청의 Content-Length 헤더를 통해 확인할 수 있는 경우).
packages.archive.upload-time
  • 유형: datetime
  • 필수 여부: 아니요
  • 영감: Simple repository API
  • 파일이 업로드된 시간입니다.
  • 날짜와 시간은 UTC로 기록해야 합니다.
[packages.archive.hashes]
  • 유형: 문자열 테이블
  • 필수 여부: 예
  • 영감: PDM, Poetry, uv, Simple repository API
  • 키가 해시 알고리즘이고 값이 해시 값인, 파일의 알려진 해시 값을 나열하는 테이블입니다.
  • 테이블에는 하나 이상의 항목이 포함되어야 합니다.
  • 해시 알고리즘 키는 소문자여야 합니다.
  • 관련 hashlib.algorithms_guaranteed에 있는 안전한 알고리즘을 하나 이상 항상 포함해야 합니다(작성 시점에는 특히 sha256을 권장합니다.
packages.archive.subdirectory

packages.vcs.subdirectory를 참조하십시오.

packages.index
  • 유형: 문자열
  • 필수 여부: 아니요
  • 영감: uv
  • sdist 및/또는 휠을 찾은 Simple repository API의 패키지 색인에 대한 기본 URL입니다(예: https://pypi.org/simple/).
  • 가능한 경우 이는 software bill of materials를 생성하는 데 도움을 주고(SBOM이라고도 함), URL이 더 이상 유효하지 않을 때 파일을 찾는 데 도움을 주도록 지정해야 합니다.
  • 특정 파일에 기록된 URL이 더 이상 유효하지 않은 경우(예: 404 HTTP 오류 코드를 반환하는 경우), 도구는 색인에서 설치하는 기능을 지원할 수 있습니다.
[packages.sdist]
  • 유형: 테이블
  • 필수 여부: 아니요; packages.vcs, packages.directorypackages.archive와 상호 배타적입니다
  • 영감: uv
  • 패키지의 Source distribution file name에 대한 세부 정보입니다.
  • 도구는 잠금 및 설치 관점 모두에서 sdist 파일을 지원하지 않도록 선택할 수 있습니다.
  • 도구는 사용자가 sdist 파일 사용 여부를 선택할 방법을 제공해야 합니다.
packages.sdist.name
  • 유형: 문자열
  • 필수 여부: 아니요. path/ url의 마지막 구성 요소가 동일한 값인 경우에는 필요하지 않습니다.
  • 영감: PDM, Poetry, uv
  • 파일 이름은 Source distribution file name 파일의 이름입니다.
packages.sdist.upload-time

packages.archive.upload-time을 참조하십시오.

packages.sdist.url

packages.archive.url을 참조하십시오.

packages.sdist.path

packages.archive.path을 참조하십시오.

packages.sdist.size

packages.archive.size을 참조하십시오.

packages.sdist.hashes

packages.archive.hashes을 참조하십시오.

[[packages.wheels]]
  • 유형: 테이블 배열
  • 필수 여부: 아니요. packages.vcs, packages.directorypackages.archive와 상호 배타적입니다.
  • 영감: PDM, Poetry, uv
  • 패키지에 대해 Binary distribution format에서 지정한 휠 파일을 기록하기 위한 것입니다.
  • 도구는 잠금 및 설치 관점 모두에서 휠 파일을 반드시 지원해야 합니다.
packages.wheels.name
  • 유형: 문자열
  • 필수 여부: 아니요. path/ url의 마지막 구성 요소가 동일한 값인 경우에는 필요하지 않습니다.
  • 영감: PDM, Poetry, uv
  • 파일 이름은 Binary distribution format 파일의 이름입니다.
packages.wheels.upload-time

packages.archive.upload-time을 참조하십시오.

packages.wheels.url

packages.archive.url을 참조하십시오.

packages.wheels.path

packages.archive.path를 참조하십시오.

packages.wheels.size

packages.archive.size를 참조하십시오.

packages.wheels.hashes

packages.archive.hashes를 참조하십시오.

[[packages.attestation-identities]]
  • 유형: 테이블 배열
  • 필수 여부: 아니요
  • 영감: Provenance objects
  • 이 패키지에 기록된 모든 파일의 증명을 기록합니다.
  • 사용 가능한 경우 도구는 발견된 증명 식별자를 포함해야 합니다.
  • 게시자별 키는 Index hosted attestations의 명세에 따라 테이블에 있는 그대로(즉, 최상위 수준에) 포함해야 합니다.
packages.attestation-identities.kind
  • 유형: 문자열
  • 필수 여부: 예
  • 영감: Provenance objects
  • 신뢰할 수 있는 게시자의 고유 식별자입니다.
[packages.tool]
  • 유형: 테이블
  • 필수 여부: 아니요
  • 영감: Arbitrary tool configuration: the [tool] table
  • 이는 pyproject.toml specification에서의 [tool]테이블과 유사하게 사용되지만, 잠금 파일 수준이 아니라 패키지 버전 수준에서 사용됩니다([tool]을 통해 잠금 파일 수준에서도 사용할 수 있습니다).
  • 테이블에 기록되는 데이터는 폐기 가능해야 합니다(즉, 설치에 영향을 주어서는 안 됩니다).

[tool]

마커 표현식 구문 추가 사항

이 PEP는 [[packages]]의 항목에 대한 extras 및 의존성 그룹 관계를 packages.marker에 표현할 수 있도록 Environment Markers 명세에 추가할 것을 제안합니다. 이 PEP에 기술된 추가 사항은 이 PEP에서 정의한 잠금 파일의 컨텍스트에만 적용되며, 마커 구문이 사용되는 다른 컨텍스트(예: METADATA, pyproject.toml)에는 적용되지 않습니다.

먼저 두 개의 새로운 마커인 extrasdependency_groups를 도입합니다. 이들은 각각 설치가 요청된 extras와 의존성 그룹을 나타냅니다.

diff --git a/source/specifications/dependency-specifiers.rst b/source/specifications/dependency-specifiers.rst
index 06897da2..c9ab247f 100644
--- a/source/specifications/dependency-specifiers.rst
+++ b/source/specifications/dependency-specifiers.rst
@@ -87,7 +87,7 @@ environments::
                      'platform_system' | 'platform_version' |
                      'platform_machine' | 'platform_python_implementation' |
                      'implementation_name' | 'implementation_version' |
-                     'extra' # ONLY when defined by a containing layer
+                     '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

이는 다른 컨텍스트에서 동일한 구문 파서를 사용하는 것을 배제하지 않으며, 새 마커가 컨텍스트에 따라 유효한 것으로 간주되는 경우에만 적용됩니다.

둘째, 마커 명세를 변경하여 값에 집합을 사용할 수 있도록 합니다(문자열과 버전에 대한 현재 지원에 더하여). 이 PEP에서 도입된 새로운 마커만 해당 값에 집합을 사용할 수 있으며, 기본값은 빈 집합입니다. 이는 특히 집합 리터럴을 허용하도록 명세를 업데이트하지 않습니다.

셋째, 집합과 관련된 연산을 허용하도록 마커 표현식 구문 명세를 업데이트합니다.

diff --git a/source/specifications/dependency-specifiers.rst b/source/specifications/dependency-specifiers.rst
index 06897da2..ac29d796 100644
--- a/source/specifications/dependency-specifiers.rst
+++ b/source/specifications/dependency-specifiers.rst
@@ -196,15 +196,16 @@ safely evaluate it without running arbitrary code that could become a security
vulnerability. Markers were first standardised in :pep:`345`. This document
fixes some issues that were observed in the design described in :pep:`426`.

-Comparisons in marker expressions are typed by the comparison operator.  The
-<marker_op> operators that are not in <version_cmp> perform the same as they
-do for strings in Python. The <version_cmp> operators use the version comparison
-rules of the :ref:`Version specifier specification <version-specifiers>`
-when those are defined (that is when both sides have a valid
-version specifier). If there is no defined behaviour of this specification
-and the operator exists in Python, then the operator falls back to
-the Python behaviour. Otherwise an error should be raised. e.g. the following
-will result in  errors::
+Comparisons in marker expressions are typed by the comparison operator and the
+type of the marker value. The <marker_op> operators that are not in
+<version_cmp> perform the same as they do for strings or sets in Python based on
+whether the marker value is a string or set itself. The <version_cmp> operators
+use the version comparison rules of the
+:ref:`Version specifier specification <version-specifiers>` when those are
+defined (that is when both sides have a valid version specifier). If there is no
+defined behaviour of this specification and the operator exists in Python, then
+the operator falls back to the Python behaviour for the types involved.
+Otherwise an error should be raised. e.g. the following will result in errors::

    "dog" ~= "fred"
    python_version ~= "surprise"

넷째, 잠금 파일 외부에서 extrasdependency_groups를 사용하는 것은 오류로 간주합니다(Core metadata specifications 외부에서 extra를 사용하는 경우와 유사합니다).

diff --git a/source/specifications/dependency-specifiers.rst b/source/specifications/dependency-specifiers.rst
index 06897da2..2914ef66 100644
--- a/source/specifications/dependency-specifiers.rst
+++ b/source/specifications/dependency-specifiers.rst
@@ -235,6 +235,11 @@ no current specification for this. Regardless, outside of a context where this
special handling is taking place, the "extra" variable should result in an
error like all other unknown variables.

+The "extras" and "dependency_groups" variables are also special. They are used
+to specify any requested extras or dependency groups when installing from a lock
+file. Outside of the context of lock files, these two variables should result in
+an error like all other unknown variables.
+
.. list-table::
    :header-rows: 1

이러한 변경 사항은 packages.extras/ packages.dependency-groups 및 마커 표현식의 불리언 논리 지원과 함께, 요청된 extras 및 의존성 그룹에 따라 패키지를 설치해야 하는 경우에 대한 임의의 포괄적인 요구 사항을 표현할 수 있도록 합니다. 예를 들어 잠금 파일에 extras = ["extra-1", "extra-2"]가 있는 경우, 다음과 같은 때 패키지를 설치할지 지정할 수 있습니다.

  • 모든 extras가 지정된 경우 ('extra-1' in extras or 'extra-2' in extras)
  • “extra-1”만 지정된 경우 ('extra-1' in extras and 'extra-2' not in extras)
  • 어떤 extras도 지정되지 않은 경우 ('extra-1' not in extras and 'extra-2' not in extras)

(이 목록은 가능한 모든 불리언 논리 표현식을 포괄하지 않습니다.)

동일한 유연성이 의존성 그룹에도 적용됩니다.

사용자가 설치하려는 extras 및/또는 의존성 그룹을 도구에 알리는 방법은 도구에 따라 결정됩니다. 설치 도구는 이 PEP에서 제안하는 마커 표현식 구문 추가 사항을 반드시 지원해야 합니다. 잠금 도구는 제안된 마커 표현식 구문 추가 사항을 활용하는 잠금 파일 작성을 지원할 수 있습니다(즉, 잠금 도구는 일회성 잠금 파일 작성만 지원하도록 선택할 수 있습니다).

예제

lock-version = '1.0'
environments = ["sys_platform == 'win32'", "sys_platform == 'linux'"]
requires-python = '==3.12'
created-by = 'mousebender'

[[packages]]
name = 'attrs'
version = '25.1.0'
requires-python = '>=3.8'
wheels = [
  {name = 'attrs-25.1.0-py3-none-any.whl', upload-time = 2025-01-25T11:30:10.164985+00:00, url = 'https://files.pythonhosted.org/packages/fc/30/d4986a882011f9df997a55e6becd864812ccfcd821d64aac8570ee39f719/attrs-25.1.0-py3-none-any.whl', size = 63152, hashes = {sha256 = 'c75a69e28a550a7e93789579c22aa26b0f5b83b75dc4e08fe092980051e1090a'}},
]
[[packages.attestation-identities]]
environment = 'release-pypi'
kind = 'GitHub'
repository = 'python-attrs/attrs'
workflow = 'pypi-package.yml'

[[packages]]
name = 'cattrs'
version = '24.1.2'
requires-python = '>=3.8'
dependencies = [
    {name = 'attrs'},
]
wheels = [
  {name = 'cattrs-24.1.2-py3-none-any.whl', upload-time = 2024-09-22T14:58:34.812643+00:00, url = 'https://files.pythonhosted.org/packages/c8/d5/867e75361fc45f6de75fe277dd085627a9db5ebb511a87f27dc1396b5351/cattrs-24.1.2-py3-none-any.whl', size = 66446, hashes = {sha256 = '67c7495b760168d931a10233f979b28dc04daf853b30752246f4f8471c6d68d0'}},
]

[[packages]]
name = 'numpy'
version = '2.2.3'
requires-python = '>=3.10'
wheels = [
  {name = 'numpy-2.2.3-cp312-cp312-win_amd64.whl', upload-time = 2025-02-13T16:51:21.821880+00:00, url = 'https://files.pythonhosted.org/packages/42/6e/55580a538116d16ae7c9aa17d4edd56e83f42126cb1dfe7a684da7925d2c/numpy-2.2.3-cp312-cp312-win_amd64.whl', size = 12626357, hashes = {sha256 = '83807d445817326b4bcdaaaf8e8e9f1753da04341eceec705c001ff342002e5d'}},
  {name = 'numpy-2.2.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl', upload-time = 2025-02-13T16:50:00.079662+00:00, url = 'https://files.pythonhosted.org/packages/39/04/78d2e7402fb479d893953fb78fa7045f7deb635ec095b6b4f0260223091a/numpy-2.2.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl', size = 16116679, hashes = {sha256 = '3b787adbf04b0db1967798dba8da1af07e387908ed1553a0d6e74c084d1ceafe'}},
]

[tool.mousebender]
command = ['.', 'lock', '--platform', 'cpython3.12-windows-x64', '--platform', 'cpython3.12-manylinux2014-x64', 'cattrs', 'numpy']
run-on = 2025-03-06T12:28:57.760769

설치

다음은 잠금 파일에서 설치하기 위해 수행할 단계를 설명합니다(요구 사항은 규정적이지만, 일반적인 단계와 순서는 제안 사항입니다).

  1. 설치할 extras와 의존성 그룹을 수집하고, 각각 마커 평가를 위해 extrasdependency_groups를 설정합니다.
    1. extras는 기본적으로 빈 집합으로 설정해야 합니다.
    2. dependency_groups는 기본적으로 default-groups에서 생성한 집합이어야 합니다.
  2. lock-version으로 지정된 메타데이터 버전이 지원되는지 확인합니다. 적절한 경우 오류 또는 경고를 발생해야 합니다.
  3. requires-python이 지정된 경우 설치 대상 환경이 해당 요구 사항을 충족하는지 확인합니다. 충족하지 않으면 반드시 오류를 발생해야 합니다.
  4. environments가 지정된 경우 환경 마커 표현식 중 하나 이상이 충족되는지 확인합니다. 어떤 표현식도 충족되지 않으면 반드시 오류를 발생해야 합니다.
  5. [[packages]]에 나열된 각 패키지에 대해 다음을 수행합니다.
    1. marker가 지정된 경우 충족되는지 확인합니다. 충족되지 않으면 다음 패키지로 건너뜁니다.
    2. requires-python이 지정된 경우 충족되는지 확인합니다. 충족되지 않으면 반드시 오류를 발생해야 합니다.
    3. 설치 대상으로 지정된 패키지의 다른 충돌 인스턴스가 없는지 확인하십시오. 그렇지 않으면 모호성에 대한 오류를 반드시 발생시켜야 합니다.
    4. 패키지의 소스가 적절하게 지정되었는지 확인하십시오(즉, 패키지 항목에 충돌하는 소스가 없어야 합니다). 문제가 발견되면 반드시 오류를 발생시켜야 합니다.
    5. 패키지를 설치할 패키지 집합에 추가하십시오.
  6. 설치할 각 패키지에 대해 다음을 수행하십시오.
    • vcs가 설정된 경우:
      1. commit-id에 지정된 커밋 ID로 저장소를 복제하십시오.
      2. subdirectory를 고려하여 패키지를 빌드하십시오.
      3. 설치하십시오.
    • 그렇지 않고 directory가 설정된 경우:
      1. subdirectory를 고려하여 패키지를 빌드하십시오.
      2. 설치하십시오.
    • 그렇지 않고 archive가 설정된 경우:
      1. 파일을 가져오십시오.
      2. 파일 크기와 해시를 검증하십시오.
      3. subdirectory를 고려하여 패키지를 빌드하십시오.
      4. 설치하십시오.
    • 그렇지 않고 wheels에 대한 항목이 있는 경우:
      1. name을 기반으로 적절한 휠 파일을 찾으십시오. 찾지 못하면 sdist로 넘어가거나 프로젝트의 소스가 없다는 오류를 반드시 발생시켜야 합니다.
      2. 파일을 가져오십시오.
        • path가 설정된 경우 이를 사용하십시오.
        • url이 설정된 경우 이를 사용하도록 시도하십시오. 선택적으로 도구는 packages.index 또는 도구별 메커니즘을 사용하여 선택된 휠 파일을 다운로드할 수 있습니다(도구는 사용 가능한 항목에 따라 다운로드할 휠 파일을 변경하려고 시도해서는 안 됩니다. 재현성을 위해 설치할 파일은 오프라인 방식으로 결정해야 합니다).
      3. 파일 크기와 해시를 검증하십시오.
      4. 설치하십시오.
    • 그렇지 않고 wheel파일을 찾지 못했거나 sdist만 설정된 경우:
      1. 파일을 가져오십시오.
        • path가 설정된 경우 이를 사용하십시오.
        • url이 설정된 경우 이를 사용하도록 시도하십시오. 도구는 packages.index 또는 도구별 메커니즘을 사용하여 파일을 다운로드할 수 있습니다.
      2. 파일 크기와 해시를 검증하십시오.
      3. 패키지를 빌드하십시오.
      4. 설치하십시오.

requirements.txt 파일과의 의미적 차이

형식을 무시하면, 이 PEP에서 제안하는 잠금 파일과 requirements file를 통해 가능한 잠금 파일 사이에는 몇 가지 차이점이 있습니다.

차이점 중 일부는 보안과 관련이 있습니다. 해시를 요구하고, 파일 크기와 파일이 발견된 위치(색인과 파일 자체의 위치 모두)를 기록하면 잠금 대상이 된 파일을 감사하고 검증하는 데 도움이 됩니다. 이를 해시를 선택적으로 포함할 수 있지만 사용자가 선택해야 하는 기능이며 우회할 수도 있는 요구 사항 파일과 비교해 보십시오. 파일의 업로드 시간과 파일을 찾을 수 있는 위치를 선택적으로 포함하는 점도 다릅니다.

파일 전체에 대해 지원되는 Python 버전과 환경을 명시하는 것도 이 PEP만의 특징입니다. 이는 요구 사항 파일이 특정 플랫폼을 대상으로 하는 시점을 알 수 없는 문제를 완화하기 위한 것입니다.

[tool] 테이블에 해당하는 직접적인 요소가 요구 사항 파일에는 없습니다. 요구 사항 파일도 주석을 지원하지만, TOML로 작성되는 [tool] 테이블과 달리 본질적으로 구조화되어 있지는 않습니다.

요구 사항 파일의 주석에 잠금 파일의 내용을 감사하고 이해하는 데 유용한 세부 정보를 기록할 수 있지만, 그러한 내용을 기록하기 위한 구조화된 지원을 제공하면 감사를 더 쉽게 수행할 수 있습니다. 패키지에 필요한 Python 버전을 미리 기록하면 이 작업에 도움이 되며, 설치가 실패할 경우 더 일찍 오류를 발생시킬 수도 있습니다. 휠 파일 이름을 URL 또는 경로와 별도로 기록하는 것도 휠 파일 목록을 더 쉽게 읽도록 하기 위한 것이며, 파일을 이해하고 감사할 때 유용한 정보를 인코딩합니다. sdist 파일 이름을 기록하는 것도 같은 이유입니다.

이 PEP는 다중 사용 잠금 파일을 지원하는 반면, 요구 사항 파일은 단일 사용 파일입니다.

이 PEP는 다음과 같은 이유로 요구 사항 파일을 완전히 대체하지는 않습니다.

하위 호환성

기존 잠금 파일 형식이 없으므로 Python 패키징 표준 측면에서 명시적인 하위 호환성 문제는 없습니다.

패키징 도구 자체에 대해서는 이 PEP를 지원할지와 어떤 방식으로 지원할지(즉, 내보내기 대상 형식으로 사용할지, 잠금 파일을 기록하는 기본 방식으로 사용할지)가 도구별 결정 사항이 됩니다.

보안 영향

보안을 우선하는 태도에서 출발하는 잠금 파일 형식을 표준화하면 전반적인 패키징 설치를 더 안전하게 만드는 데 도움이 되기를 기대합니다. 그러나 이 PEP가 발생할 수 있는 모든 보안 문제를 해결하는 것은 아닙니다.

잠금 파일이 변조될 가능성이 한 가지 우려 사항입니다. 잠금 파일을 소스 제어에 보관하지 않고 적절히 감사하지 않으면 악의적인 행위자가 파일을 악의적인 방식으로 변경할 수 있습니다(예: 패키지의 악성 코드 버전을 가리키도록 변경할 수 있습니다). 사용자를 대신하여 설치를 수행하는 클라우드 제공자 등으로 전송되는 중에도 변조가 발생할 수 있습니다. 두 경우 모두 잠금 파일 내부의 [tool] 항목에 서명을 포함하거나 잠금 파일 자체와 별도의 사이드 채널을 통해 잠금 파일에 서명함으로써 완화할 수 있습니다.

이 PEP는 사용자가 잘못된 패키지를 설치하지 못하도록 방지하는 어떤 기능도 제공하지 않습니다. 패키지 포함 여부를 감사하는 데 도움이 되는 많은 세부 정보를 포함하지만, 오타 도용을 통한 이름 혼동 공격 등을 막을 수 있는 메커니즘은 없습니다. 도구가 이를 지원하기 위한 일부 UX를 제공할 수 있습니다(예: 패키지의 다운로드 횟수를 제공할 수 있습니다).

이 내용을 가르치는 방법

사용자가 어떤 패키지를 설치해 달라고 요청할 때 해당 패키지에 자체 의존성이 있을 수 있고, 그 의존성에도 의존성이 있을 수 있으며, 이런 식으로 계속 이어질 수 있다는 점을 사용자에게 알려야 합니다. 요청한 패키지를 설치하는 과정에서 무엇이 설치되는지 기록해 두지 않으면, 사용자도 모르는 사이에 패키지 버전 등의 사항이 변경될 수 있습니다. 기반이 되는 의존성의 변경으로 인해 코드가 의도치 않게 손상될 수 있습니다. 잠금 파일은 설치된 항목을 기록해 두어 나중에 정확히 동일한 항목을 설치할 수 있는 방법을 제공함으로써 이 문제를 해결하는 데 도움이 됩니다.

설치할 항목을 기록해 두는 것은 다른 사람과 협업할 때도 도움이 됩니다. 잠금 파일의 내용에 합의하면 모든 사람이 동일한 패키지를 설치하게 됩니다. 이는 프로젝트에 참여하는 모든 사람이 설치하지 않은 특정 버전에서만 사용할 수 있는 API 등에 누구도 의존하지 않도록 하는 데 도움이 됩니다.

잠금 파일은 항상 동일한 파일을 설치하고 누군가 몰래 끼워 넣었을 수 있는 악성 파일을 설치하지 않도록 하여 보안에도 도움이 됩니다. 또한 의존성을 더 신중하게 업그레이드하여 변경이 의도된 것이며 악의적인 행위자가 몰래 끼워 넣은 것이 아님을 보장할 수 있게 합니다.

잠금 파일은 특정 환경만 지원할 수 있습니다. 설치 대상 환경에 설치해야 하는 항목은 다른 환경에 설치해야 하는 항목과 다를 수 있습니다. 다만 일부 잠금 파일은 범용적으로 동작하려고 하며, 가능한 모든 환경에서 작동합니다(sdist 및 소스 트리 설치가 성공한다는 전제하에).

잠금 파일은 일회용이거나 다회용일 수 있습니다. 일회용 잠금 파일은 단일 사용 사례를 위한 것입니다. 다회용 잠금 파일은 엑스트라와 의존성 그룹에 따라 여러 사용 사례에 사용할 수 있습니다. 다회용 잠금 파일이 가능한지는 사용자가 사용하는 도구가 결정합니다. 잠금 파일을 처리하는 모든 도구는 최소한 일회용 잠금 파일을 지원합니다. 어느 유형의 잠금 파일이 다른 유형보다 더 좋거나 나쁜 것은 아니며, 단일 파일에 기록할 수 있는 정보의 양만 달라집니다.

이 PEP를 따르는 잠금 파일은 사양을 구현하는 모든 설치 도구로 설치할 수 있습니다. 이를 통해 잠금 파일 사용자는 해당 잠금 파일을 만든 사람이 사용한 잠금 파일 생성 도구에 얽매이지 않고 설치를 수행할 수 있습니다. 그러나 다른 잠금 파일 생성 도구를 사용한다고 해서 동일한 결과가 나오는 것은 아닙니다. 잠금할 항목을 결정하는 데 서로 다른 알고리즘을 사용하는 것을 비롯해 여러 가지 이유가 있을 수 있습니다.

참조 구현

일회용 잠금 파일을 구현하는 개념 증명은 https://github.com/brettcannon/mousebender/tree/pep 에서 확인할 수 있습니다. PDMPoetry 같은 다른 도구도 이 PEP의 여러 부분에 대해 의미적으로 유사한 접근 방식을 구현합니다.

거부된 아이디어

설치 목적의 의존성 그래프 기록

이 PEP의 이전 버전에서는 설치할 패키지 집합 대신 패키지의 의존성 그래프를 기록했습니다. 의존성 그래프를 기록하면 더 많은 정보를 얻을 뿐만 아니라, 기능을 본질적으로 더 많이 지원하여 유연성도 높일 수 있다는 발상이었습니다(예: 마커를 명시적으로 전파하지 않고 플랫폼별 의존성을 지원하는 것).

그러나 결국 그 비용을 감수할 가치가 없는 복잡성을 추가하는 것으로 판단되었습니다(예: 이 PEP의 목표 달성에 필요하지 않은 세부 사항을 감사하기가 더 어려워졌습니다).

파일 간 일관된 메타데이터를 요구하는 새로운 핵심 메타데이터 버전 지정

한때 파일마다 메타데이터가 달라 정확한 잠금 결과를 얻으려면 패키지와 버전에 대해 릴리스된 모든 파일을 검사해야 하는 문제를 처리하기 위해, 패키지의 단일 버전에 해당하는 모든 휠 파일의 메타데이터가 동일하도록 요구하는 새로운 핵심 메타데이터 버전을 도입하자는 의견이 제시되었습니다. 그러나 결국 이는 불필요한 것으로 판단되었습니다. 이 PEP가 성능상의 이유로 사람들이 파일을 일관되게 만들도록 압력을 가하거나, 색인이 휠 파일 자체와 분리된 모든 메타데이터를 제공하도록 압력을 가할 것이기 때문입니다. 또한 쉽게 시행할 수 있는 메커니즘도 없으므로, 커뮤니티의 기대만으로도 새로운 메타데이터 버전을 도입하는 것만큼 효과가 있을 것입니다.

설치 프로그램이 의존성 해결을 수행하도록 하기

이 PEP가 작성될 당시 Poetry가 작동하던 방식에 더 가까운 형식을 지원하기 위해, 잠금 도구가 어떤 가능한 상황에서든 설치가 작동하는 데 필요할 수 있는 패키지와 해당 버전을 사실상 기록한 다음 설치 프로그램이 설치할 항목을 해결하도록 하자는 의견이 제시되었습니다. 그러나 이는 주어진 상황에서 어떤 패키지가 설치될 수 있는지 파악하는 데 훨씬 더 많은 정신적 노력이 필요하게 만들어 잠금 파일 감사 작업을 복잡하게 합니다. 또한 Poetry 개발자 중 한 명은 suggested이 PEP의 패키지 잠금 접근법에서 표현되는 마커가 Poetry의 요구를 충족하기에 충분할 수 있다고 제안했습니다. 설치 프로그램이 해결을 수행하지 않도록 하면 이들의 구현도 단순해지고, 복잡성을 잠금 도구에 집중시킬 수 있습니다.

최소 해시 알고리즘 지원 요구

파일에 기준 해시 알고리즘을 요구하자는 의견이 제시되었습니다. 다른 Python 패키징 사양에서는 특정 해시 알고리즘 지원을 요구하지 않으므로 이는 거부되었습니다. 또한 제안된 최소 해시 알고리즘이 결국 오래되었거나 안전하지 않은 제안이 되어 추가 업데이트가 필요할 수도 있습니다. 항상 최적의 알고리즘을 사용하도록 장려하기 위해 기준을 제공하지 않습니다. 이를 통해 도구가 해당 해시 알고리즘의 보안상 영향을 고려하지 않고 단순히 기준 알고리즘을 기본값으로 사용하는 일을 방지할 수 있습니다.

파일 이름 지정

파일 이름으로 *.pylock.toml 사용

잠금 파일 용도의 식별자 뒤에 파일 이름의 고정 부분인 pylock을 배치하자는 의견이 제시되었습니다. 그러나 이렇게 하지 않기로 결정했습니다. 디렉터리 내용을 볼 때 잠금 파일이 용도만을 기준으로 정렬되어 디렉터리 곳곳에 흩어지는 대신 함께 정렬되도록 하기 위해서입니다.

파일 이름으로 *.pylock 사용

.toml을 파일 확장자로 사용하지 않고 대신 확장자 자체를 .pylock으로 만들자는 의견이 제시되었습니다. 그러나 코드 편집기가 파일 확장자에 대한 특별한 지식 없이도 잠금 파일에 구문 강조를 제공하는 방법을 알 수 있도록 이를 채택하지 않기로 결정했습니다.

파일에 대한 이름 지정 규칙을 두지 않기

잠금 파일 이름에 대한 요구 사항이나 지침을 두지 않는 방안도 고려되었지만 결국 거부되었습니다. 표준화된 이름 지정 규칙을 두면 사람과 코드 편집기 모두 잠금 파일을 쉽게 식별할 수 있습니다. 이는 예를 들어 도구가 사용 가능한 모든 잠금 파일을 파악하려 할 때 검색을 용이하게 합니다.

파일 형식

TOML 대신 JSON 사용

기계가 작성할 수 있는 형식을 갖추는 것이 이 PEP의 목표였으므로 JSON을 사용하자는 의견이 제시되었습니다. 그러나 JSON은 TOML보다 사람이 읽기 어렵고, 형식 변경을 정당화할 만큼 기계 작성 가능성 측면을 충분히 개선하지도 못한다고 판단되었습니다.

TOML 대신 YAML 사용

일부는 YAML이 TOML보다 기계 작성 가능성과 사람 가독성 요구 사항을 더 잘 충족한다고 주장했습니다. 그러나 이는 주관적인 문제이고 pyproject.toml이 이미 Python 패키징 표준에서 사용하는 사람이 작성할 수 있는 파일로 존재했으므로, TOML을 계속 사용하는 것이 더 중요하다고 판단되었습니다.

기타 키

파일 전체에 단일 해시 알고리즘 사용

이 PEP의 이전 버전에서는 파일마다 여러 알고리즘을 사용하는 대신 파일별로 단일 해시 알고리즘을 지정하도록 제안했습니다. 단일 알고리즘을 지정하면 특정 해시 알고리즘의 사용이 의무화된 경우 파일을 감사하는 데 도움이 될 것이라고 생각했습니다.

결국 이 아이디어에 일부 반대 의견이 있었습니다. 일반적으로 반대 의견은 대규모 휠 파일(예: PyTorch)을 다시 해시하는 비용을 중심으로 제기되었습니다. 또한 설치 프로그램을 대신하여 해시 결정을 미리 내리면 사용자가 이에 동의하지 않을 수 있다는 우려도 있었습니다. 결국 유연성을 확보하고 사람들이 각자의 판단에 따라 잠금 파일을 감사하도록 하는 편이 더 낫다고 여겨졌습니다.

잠금 파일 자체의 내용 해시

파일의 바이트 내용을 해시하고 그 해시 값을 파일 자체에 저장하자는 제안이 한때 있었습니다. 잠금 파일의 변경 사항을 병합할 때 병합 충돌을 방지하려면 매번 해시 값을 다시 계산해야 하므로, 병합을 더 쉽게 하기 위해 이 제안은 삭제되었습니다.

파일의 의미론적 내용을 해시하자는 제안도 있었지만, 동일한 병합 충돌 문제가 발생하게 됩니다.

어떤 내용을 해시했는지와 관계없이, 이러한 해시가 필요하다면 두 접근 방식 모두 파일 외부에 해시 값을 저장할 수 있습니다.

잠금 파일 생성 날짜 기록

잠금 파일이 잠재적으로 얼마나 오래되어 최신 상태가 아닐 수 있는지 알 수 있도록, 이전 제안에서는 잠금 파일의 생성 날짜를 기록하도록 제안했습니다. 그러나 파일 내용의 해시를 저장할 때와 같은 병합 충돌상의 이유로 이 아이디어는 철회되었습니다.

검색에 사용된 패키지 색인 기록

잠금 파일을 생성하는 데 사용된 패키지 색인을 기록하는 방안이 검토되었습니다. 그러나 결국 불필요한 기록 관리로 여겨져 거부되었습니다.

sdist의 빌드 요구 사항 잠금

이 PEP의 이전 버전에서는 packages.build-requires 키 아래에 sdist의 빌드 요구 사항을 잠그려고 했습니다. 안타깝게도 이 방식이 어떻게 작동할 것으로 예상되는지에 대해 충분히 많은 사람을 혼란스럽게 했고, 여러 가지 예외적인 경우의 문제도 충분히 존재했으므로 이 PEP에서 처음부터 구현을 시도할 가치가 없다고 판단했습니다. 대신 향후 PEP에서 해결책을 제안할 수 있습니다.

전용 direct

이전 버전에는 무언가를 direct URL reference에서 비롯된 것으로 간주해야 하는 경우를 표시하기 위한 전용 packages.direct 키가 있었습니다. 그러나 direct URL reference가 발생할 수 있는 경우는 명시적으로 세 가지(VCS, 디렉터리, 아카이브)뿐입니다. 세 경우 모두 [[packages]]에 명시되어 있으므로 이 키를 설정하는 것은 기술적으로 불필요했습니다.

이 키를 사용하지 않을 때의 유일한 단점은 이제 packages.archive에 해당하는 휠과 sdist에 있습니다. 별도 키를 사용하거나 packages.sdist 또는 packages.wheels의 일부로 지정하면, 아카이브 파일이 sdist인지 휠인지 잠금 파일 자체에서 식별할 수 있습니다. 현재로서는 설치 프로그램이 이 세부 사항을 직접 추론해야 합니다.

단순화

패키지 버전 기록 삭제

패키지 버전은 선택 사항입니다. sdist 또는 wheel 파일을 사용할 때만 안정적으로 기록할 수 있기 때문입니다. 또한 두 소스 모두 파일 이름에 버전을 기록하므로 기술적으로 중복됩니다.

그러나 논의에서 버전 번호가 감사에 충분히 유용하므로 별도로 명시해야 한다고 결정되었습니다.

sdist 및/또는 휠의 위치를 지정해야 한다는 요구 사항을 없애십시오.

적어도 한 사람은 자신의 작업에서 모든 sdist와 휠의 URL이 불안정하다고 언급했습니다. 따라서 파일을 이전에 어디에서 찾았는지와 관계없이 설치할 때 모든 파일을 검색해야 합니다. 파일의 URL 또는 경로를 제공해야 한다는 요구 사항을 없앴다면, 알려진 잘못된 정보를 기록하는 문제를 해결하는 데 도움이 되었을 것입니다.

제공된 URL 외의 다른 방법으로 파일을 찾도록 도구에 허용하기로 결정하면서 URL을 선택 사항으로 만들 필요가 없어졌습니다.

파일 크기와 해시를 요구하는 것을 없애십시오.

적어도 한 사람은 자신의 작업에서 내부 파일을 사용하여 모든 휠과 sdist를 수정한다고 말했습니다. 이는 기록된 해시와 파일 크기가 모두 잘못된다는 의미입니다. 파일 크기와 해시를 선택 사항으로 만들면(매우 높은 가능성으로 옵트아웃 메커니즘을 통해), 해당 작업을 계속하여 이 PEP의 요구 사항을 충족하는 잠금 파일을 생성할 수 있습니다.

이 결정은 보안을 지나치게 약화한다고 판단되었습니다. 또한 대체 위치에서 파일을 설치할 수 없게 합니다.

sdist 파일 이름 기록을 없애십시오.

URL/경로 요구 사항, 패키지 버전 및 해시를 없애는 것과는 양립할 수 없지만, sdist 파일 이름을 기록하는 것은 기술적으로 전혀 필요하지 않습니다(현재 파일 이름 기록은 선택 사항입니다). 파일 이름에는 프로젝트 이름과 버전만 인코딩되므로, 패키지 버전이 제공되면 파일에 관한 새로운 정보가 전달되지 않습니다. 또한 위치가 기록되어 있다면 파일 이름과 관계없이 파일을 가져올 수 있습니다.

그러나 기록된 파일 위치를 더 이상 사용할 수 없을 때 적절한 파일을 찾으려면 파일 이름을 기록하는 것이 도움이 될 수 있습니다(PEP 625 덕분에 이제 sdist 파일 이름이 표준화되었지만, 이는 2020년 이후에만 적용되었으므로 이름만으로는 추측하기 어려운 이전 sdist가 많이 있습니다).

단순성을 위해 sdist 파일 이름을 요구하기로 결정되었습니다.

packages.wheels를 테이블로 만드십시오.

휠 파일 세부 정보를 파일 이름을 키로 하는 테이블로 작성하는 방법을 생각해 볼 수 있습니다. 예를 들면 다음과 같습니다.

[[packages]]
name = "attrs"
version = "23.2.0"
requires-python = ">=3.7"
index = "https://pypi.org/simple/"

[packages.wheels]
"attrs-23.2.0-py3-none-any.whl" = {upload-time = 2023-12-31T06:30:30.772444Z, url = "https://files.pythonhosted.org/packages/e0/44/827b2a91a5816512fcaf3cc4ebc465ccd5d598c45cefa6703fcf4a79018f/attrs-23.2.0-py3-none-any.whl", size = 60752, hashes = {sha256 = "99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"}}

[[packages]]
name = "numpy"
version = "2.0.1"
requires-python = ">=3.9"
index = "https://pypi.org/simple/"

[packages.wheels]
"numpy-2.0.1-cp312-cp312-macosx_10_9_x86_64.whl" = {upload-time = 2024-07-21T13:37:15.810939Z, url = "https://files.pythonhosted.org/packages/64/1c/401489a7e92c30db413362756c313b9353fb47565015986c55582593e2ae/numpy-2.0.1-cp312-cp312-macosx_10_9_x86_64.whl", size = 20965374, hashes = {sha256 = "6bf4e6f4a2a2e26655717a1983ef6324f2664d7011f6ef7482e8c0b3d51e82ac"}}
"numpy-2.0.1-cp312-cp312-macosx_11_0_arm64.whl" = {upload-time = 2024-07-21T13:37:36.460324Z, url = "https://files.pythonhosted.org/packages/08/61/460fb524bb2d1a8bd4bbcb33d9b0971f9837fdedcfda8478d4c8f5cfd7ee/numpy-2.0.1-cp312-cp312-macosx_11_0_arm64.whl", size = 13102536, hashes = {sha256 = "7d6fddc5fe258d3328cd8e3d7d3e02234c5d70e01ebe377a6ab92adb14039cb4"}}
"numpy-2.0.1-cp312-cp312-macosx_14_0_arm64.whl" = {upload-time = 2024-07-21T13:37:46.601144Z, url = "https://files.pythonhosted.org/packages/c2/da/3d8debb409bc97045b559f408d2b8cefa6a077a73df14dbf4d8780d976b1/numpy-2.0.1-cp312-cp312-macosx_14_0_arm64.whl", size = 5037809, hashes = {sha256 = "5daab361be6ddeb299a918a7c0864fa8618af66019138263247af405018b04e1"}}
"numpy-2.0.1-cp312-cp312-macosx_14_0_x86_64.whl" = {upload-time = 2024-07-21T13:37:58.784393Z, url = "https://files.pythonhosted.org/packages/6d/59/85160bf5f4af6264a7c5149ab07be9c8db2b0eb064794f8a7bf6d/numpy-2.0.1-cp312-cp312-macosx_14_0_x86_64.whl", size = 6631813, hashes = {sha256 = "ea2326a4dca88e4a274ba3a4405eb6c6467d3ffbd8c7d38632502eaae3820587"}}
"numpy-2.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" = {upload-time = 2024-07-21T13:38:19.714559Z, url = "https://files.pythonhosted.org/packages/5e/e3/944b77e2742fece7da8dfba6f7ef7dccdd163d1a613f7027f4d5b/numpy-2.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", size = 13623742, hashes = {sha256 = "529af13c5f4b7a932fb0e1911d3a75da204eff023ee5e0e79c1751564221a5c8"}}
"numpy-2.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" = {upload-time = 2024-07-21T13:38:48.972569Z, url = "https://files.pythonhosted.org/packages/2c/f3/61eee37decb58e7cb29940f19a1464b8608f2cab8a8616aba75fd/numpy-2.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", size = 19242336, hashes = {sha256 = "6790654cb13eab303d8402354fabd47472b24635700f631f041bd0b65e37298a"}}
"numpy-2.0.1-cp312-cp312-musllinux_1_1_x86_64.whl" = {upload-time = 2024-07-21T13:39:19.213811Z, url = "https://files.pythonhosted.org/packages/77/b5/c74cc436114c1de5912cdb475145245f6e645a6a1a29b5d08c774/numpy-2.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", size = 19637264, hashes = {sha256 = "cbab9fc9c391700e3e1287666dfd82d8666d10e69a6c4a09ab97574c0b7ee0a7"}}
"numpy-2.0.1-cp312-cp312-musllinux_1_2_aarch64.whl" = {upload-time = 2024-07-21T13:39:41.812321Z, url = "https://files.pythonhosted.org/packages/da/89/c8856e12e0b3f6af371ccb90d604600923b08050c58f0cd26eac9/numpy-2.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", size = 14108911, hashes = {sha256 = "99d0d92a5e3613c33a5f01db206a33f8fdf3d71f2912b0de1739894668b7a93b"}}
"numpy-2.0.1-cp312-cp312-win32.whl" = {upload-time = 2024-07-21T13:39:52.932102Z, url = "https://files.pythonhosted.org/packages/15/96/310c6f6d146518479b0a6ee6eb92a537954ec3b1acfa2894d1347/numpy-2.0.1-cp312-cp312-win32.whl", size = 6171379, hashes = {sha256 = "173a00b9995f73b79eb0191129f2455f1e34c203f559dd118636858cc452a1bf"}}
"numpy-2.0.1-cp312-cp312-win_amd64.whl" = {upload-time = 2024-07-21T13:40:17.532627Z, url = "https://files.pythonhosted.org/packages/b5/59/f6ad378ad85ed9c2785f271b39c3e5b6412c66e810d2c60934c9f/numpy-2.0.1-cp312-cp312-win_amd64.whl", size = 16255757, hashes = {sha256 = "bb2124fdc6e62baae159ebcfa368708867eb56806804d005860b6007388df171"}}

그러나 일반적으로 사람들은 이를 이 PEP가 채택한 방식보다 선호하지 않았습니다.

자기 참조

[tool] 테이블을 제거하십시오.

[tool] 테이블은 pyproject.toml 파일에 매우 유용한 것으로 밝혀졌기 때문에 포함되었습니다. 이 PEP에 유사한 유연성을 제공하는 것은 비슷한 이점이 실현되기를 기대하기 때문입니다.

그러나 일부 사람들은 이러한 테이블이 도구에 지나치게 매력적으로 작용하여 도구별로 특화되고 다른 도구에서는 사용할 수 없는 파일을 만들게 될 것을 우려했습니다. 이로 인해 설치, 감사 등을 수행하려는 도구에 문제가 발생할 수 있습니다. [tool] 테이블의 어떤 세부 정보가 중요한지 알 수 없기 때문입니다.

절충안으로, 이 PEP는 [tool] 에 기록되는 세부 정보가 폐기 가능해야 하며 패키지 설치에 영향을 주지 않아야 한다고 명시합니다.

파일에 대한 요구 사항 입력을 나열하십시오.

현재 파일에는 파일에 대한 입력으로 사용된 요구 사항이 기록되어 있지 않습니다. 이는 단순성을 위한 것이며, 예상하지 못한 방식으로 파일을 명시적으로 제한하지 않기 위한 것입니다(예를 들어, 포괄적인 요구 사항 집합을 작성하는 방법을 정하지 않고도, 처음 생성한 후 요구 사항이 다른 새 플랫폼에 맞게 파일을 업데이트하는 경우입니다).

그러나 원래 요구 사항이 어떤 방식으로든 기록되어 있다면 파일을 감사하거나 파일을 재생성할 때 도움이 될 수 있습니다. 파일에 여러 요구 사항이 사용된 경우 이는 단일 문자열이거나 문자열 배열일 수 있습니다.

결국 도구가 잠금 파일을 구성하는 데 사용한 입력을 일반적인 방식으로 수집하려는 시도는 너무 복잡하다고 판단되었습니다.

감사

종속 패키지 기록

패키지의 종속 패키지를 기록하는 것은 해당 패키지를 설치하는 데 필요하지 않습니다. 따라서 [tool]을 통해 포함할 수 있으므로 PEP에서 제외되었습니다.

그러나 한 패키지가 다른 패키지에 얼마나 중요한지 아는 것이 유용할 수 있습니다. 이 정보는 pip-tools에 포함되어 있으므로, 이를 포함하는 선례가 있습니다. 종속 패키지를 기록하는 데 유연한 방식을 사용할 수 있습니다. 예를 들어, 파일에 있는 동일한 패키지의 다른 항목과 구별할 수 있을 만큼 자세한 정보를 기록하는 방식입니다(uv_에서 영감을 받았습니다).

그러나 결국에는 의존성을 기록하는 것이 기록해야 할 더 나은 대상이라고 결정되었습니다.

감사의 말

discuss.python.org에서 진행된 토론에 참여한 모든 분께 감사드립니다. 또한 이 PEP의 초안이 공개되기 전에 피드백을 제공해 주신 Randy Döring, Seth Michael Larson, Paul Moore, Ofek Lev께 감사드립니다. 각 프로젝트를 대표하여 피드백을 제공해 주신 Poetry의 Randy Döring, uv의 Charlie Marsh, PDM의 Frost Ming께 감사드립니다.