PEP 665 – 애플리케이션의 재현성을 위해 Python 의존성을 나열하는 파일 형식
- Author:
- Brett Cannon <brett at python.org>, Pradyun Gedam <pradyunsg at gmail.com>, Tzu-ping Chung <uranusjr at gmail.com>
- PEP-Delegate:
- Paul Moore <p.f.moore at gmail.com>
- Discussions-To:
- Discourse thread
- Status:
- Rejected
- Type:
- Standards Track
- Topic:
- Packaging
- Created:
- 29-Jul-2021
- Post-History:
- 29-Jul-2021, 03-Nov-2021, 25-Nov-2021
- Superseded-By:
- 751
- Resolution:
- Discourse message
번역·라이선스 안내
이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain or CC0-1.0, whichever is more permissive 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판
Note
이 PEP는 소스 배포 지원 부족으로 커뮤니티의 미온적인 반응을 받아 거부되었습니다.
초록
이 PEP는 애플리케이션에 필요한 Python 패키지 설치 요구사항 목록과 지정된 요구사항 간의 관계를 지정하는 파일 형식을 명시합니다. 요구사항 목록은 설치 대상에 대해 빠짐없는 것으로 간주되므로, 설치 대상 플랫폼과 파일 자체를 넘어서는 어떠한 정보도 필요로 하지 않습니다. 이 파일 형식은 서로 다른 플랫폼에서 요구사항을 설치할 수 있을 만큼 유연하므로, 동일한 파일을 사용하여 여러 플랫폼에서 재현성을 확보할 수 있습니다.
용어
이 PEP의 주제에 관한 논의를 원활하게 진행하려면 정의에 합의해야 하는 용어가 여러 개 있습니다.
package은 의존성으로 설치하고 import 시스템을 통해 사용하는 것입니다. PyPI의 패키지가 그 예입니다.
application또는 app은 다른 외부 코드가 import 시스템을 통해 직접 의존하지 않는 최종 제품입니다(즉, 독립 실행형입니다). 데스크톱 애플리케이션, 명령줄 도구 등이 애플리케이션의 예입니다.
lock file은 앱에 설치할 패키지를 기록합니다. 전통적으로 설치할 패키지의 정확한 버전은 잠금 파일에 지정되지만, 지정된 패키지가 특정 플랫폼에 항상 설치되는 것은 아니며(후술하는 필터링 논리에 따름), 이를 통해 잠금 파일이 여러 플랫폼에서의 재현성을 설명할 수 있습니다. 그 예로는 npm_의 package-lock.json, Poetry_의 Poetry.lock 등이 있습니다.
Locking은 앱이 의존하는 패키지의 입력을 받아 잠금 파일을 생성하는 작업입니다.
locker는 잠금 파일을 생성하는 도구입니다.
installer는 잠금 파일을 읽어 잠금 파일에 지정된 항목을 설치합니다.
동기
애플리케이션은 몇 가지 이유로 재현 가능한 설치를 원합니다(패키지 개발, Python 애플리케이션 외부의 의존성 잠금을 처리하는 대규모 시스템과의 통합, 또는 엄격하고 재현 가능한 설치보다 flexible 설치 요구사항이 선호되는 기타 상황은 여기서 고려하지 않습니다).
첫째, 재현성은 개발을 용이하게 합니다. 특정 플랫폼에서 여러분과 동료 개발자 모두가 결국 동일한 파일을 사용하게 되면, 모두 애플리케이션에 대해 동일한 경험을 목표로 개발하고 있음을 보장할 수 있습니다. 또한 사용자가 여러분이 예상하는 것과 동일한 파일을 설치하도록 하여, 여러분이 사용자를 위해 개발한 것과 동일한 경험을 보장하고자 합니다.
둘째, 여러 플랫폼에서 설치되는 항목을 재현할 수 있어야 합니다. 운영 체제, CPU 등 간의 Python 이식성 덕분에 단일 플랫폼으로 제한되지 않는 애플리케이션을 만드는 것은 매우 쉽고, 그러한 애플리케이션을 만드는 것이 바람직한 경우도 많습니다. 따라서 플랫폼 간 패키지 의존성의 차이를 허용할 만큼 유연하면서도, 각각의 특정 플랫폼에서 일관성과 재현성을 유지할 수 있어야 합니다.
셋째, 재현성은 보안을 강화합니다. 설치되는 파일을 정확히 통제하면 악의적인 행위자가 애플리케이션에 유해한 코드를 몰래 삽입하려는 시도(예: 일부 공급망 공격)를 하지 못하도록 할 수 있습니다. 항상 재현 가능한 설치로 이어지는 잠금 파일을 사용하면 특정 위험을 완전히 피할 수 있습니다.
넷째로, wheel file형식에 의존하면 빌드 도구 자체가 재현성을 지원하도록 요구하지 않고도 재현성을 확보할 수 있습니다. 휠은 정적이며 설치 과정의 일부로 코드를 실행하지 않으므로, 휠은 항상 재현 가능한 결과를 제공합니다. 이를 소스 배포판(일명 sdist) 또는 소스 트리와 비교해 보십시오. 이러한 방식은 코드 실행이 본질적으로 포함되어 있으므로 해당 빌드 도구가 재현성을 지원하는 경우에만 재현 가능한 설치로 이어집니다. 안타깝게도 대다수의 빌드 도구는 재현 가능한 빌드를 지원하지 않으므로, 이 PEP는 패키지 형식으로 휠만 지원하여 해당 문제를 완화하는 데 도움을 줍니다.
현재의 해결책이 제시된 목표를 충족하지 못하므로, 이 PEP는 잠금 파일에 대한 표준을 제안합니다. 오늘날 잠금 파일 표준에 가장 가까운 것은 pip의 requirements file format입니다. 안타깝게도 해당 형식은 본질적으로 재현 가능한 설치로 이어지지 않습니다(요구 사항 파일과 설치 프로그램 자체에서 모두 선택적 기능을 필요로 하며, 이에 대해서는 뒤에서 설명합니다).
여러 도구가 독립적으로 자체 잠금 파일 형식을 만들었다는 사실을 통해 커뮤니티 자체도 잠금 파일의 필요성을 보여 주었습니다.
안타깝게도 이러한 도구는 모두 서로 다른 잠금 파일 형식을 사용합니다. 이는 이러한 도구를 둘러싼 도구가 고유해야 한다는 의미입니다. 이는 사용자의 애플리케이션 코드를 받아들일 때 최대한 유연하기를 원하면서도 또 다른 잠금 파일 형식에 대한 지원을 추가하는 데 사용할 수 있는 개발 리소스에는 한계가 있는 코드 편집기와 호스팅 제공업체 같은 도구에 영향을 줍니다. 표준화된 형식을 사용하면 도구가 하나의 대상에 작업을 집중할 수 있으며, 잠금 파일 형식 외부에서 개발자가 내린 워크플로 결정이 호스팅 제공업체 등에는 문제가 되지 않도록 할 수 있습니다.
다른 프로그래밍 언어 커뮤니티도 이 문제에 대한 자체 해결책을 개발하여 잠금 파일의 유용성을 보여 주었습니다. 이러한 커뮤니티에는 다음이 포함됩니다.
지난 10년 동안 프로그래밍 언어의 추세는 잠금 파일 솔루션을 제공하는 방향으로 향해 온 것으로 보입니다.
근거
파일 형식
잠금 파일의 변경 사항을 감사할 때 파일 형식을 diff로 쉽게 읽을 수 있기를 원했습니다. 따라서 PEP 518 및 pyproject.toml 덕분에 TOML 파일 형식을 사용하기로 결정했습니다.
설계 단계부터 보안 고려
잠금 파일 표준에 가장 가까운 것으로 requirements file format을 볼 때, 보안 측면에서 해당 파일 형식에는 몇 가지 문제가 있습니다. 첫째, 파일 형식에서는 패키지의 정확한 버전을 지정하도록 단순히 요구하지 않습니다. 이것이 requirements 파일 사용자가 이를 관리하도록 돕기 위해 pip-tools 같은 도구가 존재하는 이유입니다.
둘째, 특정 의존성에 대해 --hash 인자를 사용하여 설치할 수 있는 파일을 지정하도록 선택해야 합니다. pip-tools에서도 --generate-hashes CLI 인자를 지정해야 하므로 이는 선택 사항입니다. 확인할 해시가 없는 의존성이 없도록 하려면 pip에 --require-hashes를 지정해야 합니다.
셋째, 설치할 수 있는 파일을 제어하더라도 다른 패키지가 설치되는 것을 막지는 못합니다. 의존성이 requirements 파일에 나열되어 있지 않으면 pip은 해당 요구를 충족할 파일을 기꺼이 찾아 나섭니다. requirements 파일 외부에서 의도하지 않은 의존성 해결이 이루어지지 않도록 pip에 --no-deps를 인자로 지정해야 합니다.
넷째, 이 형식은 source distribution file을 설치할 수 있도록 합니다(“sdist”라고도 합니다). 본질적으로 sdist를 설치하려면 임의의 Python 코드를 실행해야 하므로, 어떤 파일이 설치될 수 있는지 제어할 수 없습니다. --only-binary :all:를 지정해야만 pip가 각 패키지에 대해 wheel file만 사용하도록 보장할 수 있습니다.
요약하면, requirements 파일이 제안된 방식만큼 안전하려면 사용자는 항상 다음 단계를 수행해야 합니다:
- pip-tools와 해당 명령
pip-compile --generate-hashes을 사용하십시오. pip install --require-hashes --no-deps --only-binary :all:를 사용하여 requirements 파일을 설치하십시오.
특히 이러한 모든 플래그와 pip-tools가 제공하는 설치 대상의 구체성 및 포괄성은 requirements 파일에서 선택 사항입니다.
따라서 이 PEP에서 제안된 방식은 설계상 안전하며 일부 공급망 공격에 대응합니다. 설치에 사용할 파일의 해시는 필수입니다. 파일 시스템에 배치될 파일을 명확하게 정의하려면 오직 휠에서만 설치할 수 있습니다. 설치 프로그램은 반드시 주어진 플랫폼에 대해 잠금 파일에서 결정론적인 설치가 이루어지도록 해야 합니다. 이 모든 것은 재현 가능한 설치로 이어지며, 잠금 파일과 그 파일에 나열된 내용을 감사했다면 이를 신뢰할 수 있다고 판단할 수 있습니다.
플랫폼 간 호환성
PDM 및 Poetry와 같은, 이미 잠금 파일을 보유한 다양한 프로젝트는 플랫폼 간 호환이 가능한 잠금 파일을 제공합니다. 이를 통해 하나의 잠금 파일을 여러 플랫폼에서 사용할 수 있으며, 각 플랫폼에서 일관되고 명확하게 설치하면서도 어디서나 설치되는 최상위 요구 사항을 정확히 동일하게 유지할 수 있습니다.
이것이 유용한 이유를 설명하기 위해 PyWeek_와 관련된 예를 들어 보겠습니다(일주일 동안 진행되는 게임 개발 대회입니다). 여러분은 Linux에서 개발하고 있고, 함께 작업하기로 한 사람은 macOS를 사용한다고 가정하십시오. 이제 심사위원들은 Windows를 사용한다고 가정하십시오. 플랫폼별 요구 사항(예: Windows에서 패키지에 도우미 패키지가 필요한 경우)을 허용하면서도 모두가 동일한 최상위 의존성을 사용하도록 하려면 어떻게 해야 하겠습니까?
플랫폼 간 호환이 가능한 잠금 파일을 사용하면 모든 플랫폼에서 핵심 요구 사항이 일관되게 충족되도록 할 수 있습니다. 그러면 같은 플랫폼의 모든 사용자가 동일한 재현 가능한 설치를 사용하도록 할 수도 있습니다.
간단한 설치 프로그램
잠금 파일 생성기와 설치 프로그램 사이의 관심사 분리를 통해 설치 프로그램이 수행해야 할 작업을 훨씬 단순하게 만들 수 있습니다. 따라서 설치 프로그램을 더 쉽게 작성할 수 있을 뿐 아니라, 설치 프로그램이 명확하고 재현 가능한 설치를 올바르게 생성하도록 보장하는 데도 도움이 됩니다.
설치 프로그램은 설치를 생성하는 데 필요한 연산량과 에너지를 줄일 수도 있습니다. 이는 더 빠른 설치뿐 아니라 에너지 소비 측면에서도 유익합니다. 설치 프로그램은 잠금 파일 생성기보다 더 자주 실행될 것으로 예상되기 때문입니다.
이는 설치 도구에 도움이 되도록 잠금 파일 생성 도구가 사전에 더 많은 작업을 수행해야 하는 설계로 이어졌습니다. 또한 모호성을 피하기 위해 잠금 파일에서 패키지 의존성의 복잡성을 더 단순하고 이해하기 쉽게 만들 수 있음을 의미합니다.
사양
세부 사항
잠금 파일은 반드시 TOML 파일 형식을 사용해야 합니다. 이는 PEP 518에서 pyproject.toml에 채택된 덕분에 Python 패키징 생태계에 또 다른 파일 형식이 필요하지 않게 할 뿐만 아니라, 잠금 파일을 사람이 더 쉽게 읽을 수 있도록 만드는 데에도 도움이 됩니다.
잠금 파일은 반드시 파일 이름이 .pylock.toml로 끝나야 합니다. .toml부분은 파일 형식을 명확하게 구분하며, 코드 편집기와 같은 도구가 해당 파일을 적절히 지원하도록 돕습니다. .pylock부분은 사용자가 보유한 다른 TOML 파일과 해당 파일을 구분하므로, 도구가 일반적인 TOML 파일이 아니라 Python 잠금 파일에 특화된 기능을 생성하기 위한 논리를 더 쉽게 만들 수 있습니다.
다음 섹션은 TOML 파일 데이터 형식의 최상위 키입니다. required로 나열되지 않은 모든 필드는 선택 사항으로 간주됩니다.
version
이 필드는 required입니다.
사용 중인 잠금 파일의 버전입니다. 이 키는 core metadata spec의 Metadata-Version 키와 동일한 형식을 따르는 숫자로 구성된 문자열이어야 합니다.
향후 PEP에서 다른 값을 허용할 때까지 값은 반드시 "1.0"으로 설정해야 합니다. 파일 형식에 새로운 optional 키를 도입하면 부 버전을 증가시키는 것이 좋습니다. 새로운 필수 키를 도입하거나 형식을 변경하면 반드시 주 버전을 증가시켜야 합니다. 그 밖의 상황을 처리하는 방법은 PEP별 결정 사항으로 남겨 둡니다.
잠금 파일이 주 버전은 지원되지만 부 버전은 지원되지 않거나 인식되지 않는 버전을 지정하는 경우 설치 도구는 반드시 사용자에게 경고해야 합니다(예: 설치 도구가 "1.0"을 지원하지만 잠금 파일이 "1.1"을 지정하는 경우).
잠금 파일이 지원되지 않는 주 버전을 지정하는 경우 설치 도구는 반드시 오류를 발생시켜야 합니다(예: 설치 도구가 "1.9"을 지원하지만 잠금 파일이 "2.0"을 지정하는 경우).
created-at
이 필드는 required입니다.
잠금 파일이 생성된 시각의 타임스탬프입니다(TOML의 기본 타임스탬프 형식 사용). 모호성을 피하기 위해 반드시 UTC 시간대를 사용하여 기록해야 합니다.
SOURCE_DATE_EPOCH 환경 변수가 설정되어 있으면 잠금 파일 생성 도구는 반드시 이를 타임스탬프로 사용해야 합니다. 이는 잠금 파일 자체의 재현성을 높이는 데 도움이 됩니다.
[tool]
도구는 tool 테이블 아래에 자체 하위 테이블을 만들 수 있습니다. 이 테이블의 규칙은 build system declaration spec의 pyproject.toml 및 해당 [tool] 테이블에 적용되는 규칙과 일치합니다.
[metadata]
이 테이블은 required입니다.
전체 잠금 파일에 적용되는 데이터를 포함하는 테이블입니다.
metadata.marker
dependency specifier spec에 지정된 환경 마커를 포함하는 문자열을 저장하는 키입니다.
잠금 파일이 생성된 환경에 적용되는 제한 사항을 지정하는 환경 마커를 잠금 파일 생성 도구가 지정할 수 있습니다.
설치 프로그램이 지정된 환경 마커를 충족하지 않는 환경에 설치하는 경우, 잠금 파일이 대상 설치 환경을 지원하지 않으므로 오류를 발생시켜야 합니다.
metadata.tag
platform compatibility tags를 지정하는 문자열을 저장하는 키입니다(즉, 휠 태그입니다). 태그는 압축된 태그 집합일 수 있습니다.
설치 프로그램이 지정된 태그(집합)를 충족하지 않는 환경에 설치하는 경우, 잠금 파일이 대상 설치 환경을 지원하지 않으므로 오류를 발생시켜야 합니다.
metadata.requires
이 필드는 필수입니다.
dependency specifier spec을 따르는 문자열 배열입니다. 이 배열은 잠금 파일의 최상위 패키지 종속성을 나타내며, 따라서 종속성 그래프의 루트입니다.
metadata.requires-python
이 잠금 파일에서 지원하는 Python 버전을 지정하는 문자열입니다. core metadata spec에 지정된 Requires-Python 필드와 동일한 형식을 따릅니다.
[[package._name_._version_]]
이 배열은 필수입니다.
패키지 및 버전별로, 설치할 수 있는 (휠) 파일에 대한 항목을 포함하는 배열입니다(_name_ 및 _version_으로 각각 표시됩니다).
잠금 파일 생성 도구는 simple repository API에 따라 프로젝트 이름을 정규화해야 합니다. 설치할 프로젝트의 일부로 추가 기능이 지정된 경우, 추가 기능을 키 이름에 포함하고 사전순으로 정렬해야 합니다.
파일 내에서 프로젝트의 테이블은 다음 순서로 정렬하는 것이 좋습니다:
- 사전순에 따른 프로젝트/키 이름
- version specifiers spec에 따른 패키지 버전, 최신/최고 버전부터 이전/최저 버전 순
- 사전순에 따른 선택적 종속성(추가 기능)
- 아래에서 설명하는
filename필드에 따른 파일 이름
이러한 권장 사항은 도구 실행 간 변경되는 diff를 최소화하는 데 도움이 됩니다.
package._name_._version_.filename
이 필드는 필수입니다.
배열의 항목으로 표시되는 파일의 기본 이름을 나타내는 문자열입니다(즉, os.path.basename()/pathlib.PurePath.name이 나타내는 값입니다). 파일 이름이 파일 이름에서 파생된 휠 태그를 확인하는 데 필요하므로, 이 필드는 설치 프로그램을 단순화하는 데 필요합니다. 또한 배열 항목과 해당 항목이 나타내는 파일의 연결 관계가 항상 명확하도록 보장합니다.
[package._name_._version_.hashes]
이 표는 필수입니다.
package._name_._version_ 표에서 이 항목이 나타내는 파일의 해시를 값으로 하고 해시 알고리즘을 지정하는 키를 갖는 표입니다.
잠금 파일 생성기는 해시를 사전식 순서로 나열해야 합니다. 이는 diff 크기와 해시 값 변경을 간과할 가능성을 최소화하기 위한 것입니다.
설치 프로그램은 지정된 해시 중 하나와 일치하는 파일만 설치해야 합니다.
package._name_._version_.url
파일을 가져올 URL을 나타내는 문자열입니다.
설치 프로그램은 URL에 대해 원하는 모든 스킴을 지원할 수 있습니다. 스킴이 없는 URL은 로컬 파일 경로로 간주해야 합니다(잠금 파일에 대한 상대 경로와 절대 경로 모두 해당합니다). 설치 프로그램은 최소한 HTTPS URL과 로컬 파일 경로를 지원해야 합니다.
지정된 해시와 일치하는 파일을 다른 방법(예: 파일 시스템의 캐시 디렉터리)으로 찾을 수 있다면, 설치 프로그램은 파일을 가져올 때 URL을 사용하지 않을 수 있습니다.
package._name_._version_.direct
설치 프로그램이 direct URL origin of installed distributions spec에 명시된 대로 프로젝트를 “직접” 설치된 것으로 간주해야 하는지를 나타내는 불리언입니다.
키가 true이면 설치 프로그램은 설치를 “직접” 설치로 기록하기 위해 direct URL origin of installed distributions spec을 따라야 합니다.
package._name_._version_.requires-python
이 파일에서 지원되는 Python 버전을 지정하는 문자열입니다. core metadata spec의 Requires-Python필드에 지정된 것과 동일한 형식을 따릅니다.
package._name_._version_.requires
이 파일의 의존성을 나타내며 dependency specifier spec을 따르는 문자열 배열입니다.
예제
version = "1.0"
created-at = 2021-10-19T22:33:45.520739+00:00
[tool]
# Tool-specific table.
[metadata]
requires = ["mousebender", "coveragepy[toml]"]
marker = "sys_platform == 'linux'" # As an example for coverage.
requires-python = ">=3.7"
[[package.attrs."21.2.0"]]
filename = "attrs-21.2.0-py2.py3-none-any.whl"
hashes.sha256 = "149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1"
url = "https://files.pythonhosted.org/packages/20/a9/ba6f1cd1a1517ff022b35acd6a7e4246371dfab08b8e42b829b6d07913cc/attrs-21.2.0-py2.py3-none-any.whl"
requires-python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
[[package.attrs."21.2.0"]]
# If attrs had another wheel file (e.g. that was platform-specific),
# it could be listed here.
[[package."coveragepy[toml]"."6.2.0"]]
filename = "coverage-6.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl"
hashes.sha256 = "c7912d1526299cb04c88288e148c6c87c0df600eca76efd99d84396cfe00ef1d"
url = "https://files.pythonhosted.org/packages/da/64/468ca923e837285bd0b0a60bd9a287945d6b68e325705b66b368c07518b1/coverage-6.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl"
requires-python = ">=3.6"
requires = ["tomli"]
[[package."coveragepy[toml]"."6.2.0"]]
filename = "coverage-6.2-cp310-cp310-musllinux_1_1_x86_64.whl "
hashes.sha256 = "276651978c94a8c5672ea60a2656e95a3cce2a3f31e9fb2d5ebd4c215d095840"
url = "https://files.pythonhosted.org/packages/17/d6/a29f2cccacf2315150c31d8685b4842a6e7609279939a478725219794355/coverage-6.2-cp310-cp310-musllinux_1_1_x86_64.whl"
requires-python = ">=3.6"
requires = ["tomli"]
# More wheel files for `coverage` could be listed for more
# extensive support (i.e. all Linux-based wheels).
[[package.mousebender."2.0.0"]]
filename = "mousebender-2.0.0-py3-none-any.whl"
hashes.sha256 = "a6f9adfbd17bfb0e6bb5de9a27083e01dfb86ed9c3861e04143d9fd6db373f7c"
url = "https://files.pythonhosted.org/packages/f4/b3/f6fdbff6395e9b77b5619160180489410fb2f42f41272994353e7ecf5bdf/mousebender-2.0.0-py3-none-any.whl"
requires-python = ">=3.6"
requires = ["attrs", "packaging"]
[[package.packaging."20.9"]]
filename = "packaging-20.9-py2.py3-none-any.whl"
hashes.blake-256 = "3e897ea760b4daa42653ece2380531c90f64788d979110a2ab51049d92f408af"
hashes.sha256 = "67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a"
url = "https://files.pythonhosted.org/packages/3e/89/7ea760b4daa42653ece2380531c90f64788d979110a2ab51049d92f408af/packaging-20.9-py2.py3-none-any.whl"
requires-python = ">=3.6"
requires = ["pyparsing"]
[[package.pyparsing."2.4.7"]]
filename = "pyparsing-2.4.7-py2.py3-none-any.whl"
hashes.sha256 = "ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"
url = "https://files.pythonhosted.org/packages/8a/bb/488841f56197b13700afd5658fc279a2025a39e22449b7cf29864669b15d/pyparsing-2.4.7-py2.py3-none-any.whl"
direct = true # For demonstration purposes.
requires-python = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
[[package.tomli."2.0.0"]]
filename = "tomli-2.0.0-py3-none-any.whl"
hashes.sha256 = "b5bde28da1fed24b9bd1d4d2b8cba62300bfb4ec9a6187a957e8ddb9434c5224"
url = "https://files.pythonhosted.org/packages/e2/9f/5e1557a57a7282f066351086e78f87289a3446c47b2cb5b8b2f614d8fe99/tomli-2.0.0-py3-none-any.whl"
requires-python = ">=3.7"
잠금 파일 생성기에 대한 기대 사항
잠금 파일 생성기는 지정된 플랫폼에서 설치 대상이 되는 패키지의 위상 정렬 결과가, 각 패키지에 대해 설치 대상이 되는 버전이 하나뿐이고 각 패키지에 설치할 호환 가능한 파일이 하나 이상 존재하는 그래프가 되도록 잠금 파일을 생성해야 합니다. 그 결과 지원되는 모든 플랫폼에서 설치 프로그램이 내릴 수 있는 유일한 결정은 설치할 “최적 적합” 휠이 무엇인지가 되는 잠금 파일이 생성됩니다(이는 아래에서 설명합니다).
잠금 파일 생성기는 설치 프로그램에서 이 결과를 강제하기 위해 필요에 따라 metadata.marker, metadata.tag, metadata.requires-python은 물론 requires를 통해 지정된 환경 마커와 requires-python을 통한 Python 버전 요구 사항을 활용해야 합니다. 다시 말해, 잠금 파일에서 사용되는 정보는 잠금 파일 생성기의 입력에서 가공되지 않은 원시 정보일 필요는 없으며, 잠금 파일 생성기의 목표에 도움이 되도록 필요에 따라 변경되어야 합니다.
설치 프로그램에 대한 기대 사항
설치할 항목을 결정하기 위해 예상되는 알고리즘은 다음과 같습니다.
- 잠금 파일의 데이터를 기반으로 의존성 그래프를 구성하되,
metadata.requires를 시작점/루트로 사용합니다. - 지정된 플랫폼에서 지원되지 않는 모든 파일을 제거합니다.
requires에 대한 마커 평가를 기반으로 패키지 간의 관련 없는 모든 간선을 제거합니다.- 패키지 버전이 의존성 그래프의 루트에서 여전히 도달 가능하지만 호환 가능한 파일이 하나도 없으면 오류를 발생시킵니다.
- 남아 있는 모든 패키지에 설치할 버전이 하나만 있는지 확인하고, 그렇지 않으면 오류를 발생시켜야 합니다.
- 남아 있는 각 패키지에 가장 적합한 휠 파일을 설치해야 합니다.
설치 도구는 “가장 적합한 휠 파일”이 무엇인지 결정하기 위해 결정론적 알고리즘을 반드시 따라야 합니다. 이를 위한 간단한 해결책은 휠 파일의 우선순위를 결정하기 위해 packaging project프로젝트와 해당 packaging.tags 모듈에 의존하는 것입니다.
설치 도구는 빈 환경에 설치하는 것을 반드시 지원해야 합니다. 설치 도구는 이미 설치된 패키지가 포함된 환경에 설치하는 것을 지원할 수도 있습니다(이를 지원하기 위해 수반되는 모든 사항도 포함합니다).
(잠재적인) 도구 지원
pip 팀은 이 PEP가 승인되면 이를 지원하는 데 관심이 있다고 말했습니다. pip의 현재 제안은 pip-tools에 대한 필요성을 대체할 수도 있습니다.
PDM_은 이 PEP가 승인되면 이를 지원하겠다고도 밝혔습니다.
Pyflow_는 이 PEP에 대해 “아이디어가 마음에 든다”고 밝혔습니다.
Poetry_는 “Poetry supports sdists files, directory and VCS dependencies which are not supported”이므로 이 PEP를 현재 상태 그대로는 지원하지 않겠다고 밝혔습니다. 종속성에서 발생할 수 있는 상황을 더 잘 반영하기 위해 의도적으로 파일 수준에서 요구 사항을 기록하는 것은 “is contradictory to the design of Poetry”라고 밝혔습니다. 또한 “Poetry exports the information present in the poetry.lock file into another format”이므로 이 PEP의 잠금 파일에 대한 내보내기 지원도 제외되며, sdists와 소스 트리는 Poetry.lock 파일에 포함됩니다. 따라서 Poetry의 잠금 파일에서 이 PEP의 잠금 파일 형식으로 깔끔하게 변환할 수 없습니다.
하위 호환성
잠금 파일에 관한 기존 사양이 없으므로 명시적인 하위 호환성 문제는 없습니다.
자체 잠금 파일을 사용하는 기존 도구의 경우 일부 업데이트가 필요합니다. 대부분은 잠금 파일의 이름은 문서화하지만 내용은 문서화하지 않습니다. 잠금 파일을 버전 관리에 커밋하지 않는 프로젝트는 해당 .gitignore 파일에 해당하는 파일을 업데이트해야 합니다. 잠금 파일을 버전 관리에 커밋하는 프로젝트는 어떤 파일을 커밋할지 업데이트해야 합니다.
pipenv_처럼 잠금 파일 형식을 문서화하는 프로젝트는 잠금 파일 형식을 변경하는 메이저 버전 릴리스가 필요할 가능성이 매우 높습니다.
전환 계획
일반적으로 다음과 같은 경우 이 PEP가 성공한 것으로 간주할 수 있습니다.
- 기존 도구 두 개가 잠금 도구가 됩니다(예: pip-tools, PDM,
pip freeze를 통한 pip). - Pip이 설치 도구가 됩니다.
- 주요 비(非)Python 전용 플랫폼 하나가 파일 형식을 지원합니다(예: 클라우드 제공업체).
이는 상호 운용성, 사용성, 프로그래밍 커뮤니티 및 기업의 수용을 보여 줍니다.
전환 계획의 관점에서 이와 같은 바람직한 결과로 이어질 수 있는 단계는 여러 가지가 있을 수 있습니다. 아래에는 이 PEP가 광범위하게 사용되도록 하는 다소 이상적인 계획이 제시되어 있습니다.
사용성
첫째, 잠금 파일을 생성하는 pip freeze에 상응하는 도구를 개발할 수 있습니다. 설치된 패키지만으로는 잠금 파일을 정적으로 생성하기에 충분한 정보를 제공하지 않지만, 사용자가 로컬 디렉터리와 색인 URL을 제공하여 잠금 파일을 구성할 수 있습니다. 그러면 잠금 파일을 현재 플랫폼으로 제한함으로써 요구 사항 파일보다 더 엄격한 잠금 파일을 만들 수 있습니다. 또한 사용자는 자신의 환경이 재현 가능한지 확인할 수 있습니다.
둘째, 독립 실행형 설치 프로그램을 개발해야 합니다. 설치 프로그램에 대한 요구 사항은 pip가 제공하는 기능보다 훨씬 단순하므로, 독립적으로 개발된 설치 프로그램을 두는 것이 합리적입니다.
셋째, pip-tools가 생성한 고정된 요구 사항 파일을 변환하는 도구를 개발할 수 있습니다. 위에서 설명한 pip freeze에 상응하는 도구와 마찬가지로, 사용자로부터 일부 입력이 필요할 수 있습니다. 그러나 이 도구는 적절한 요구 사항 파일을 보유한 모든 사람을 위한 전환 단계로 기능할 수 있습니다. 또한 pip-tools에 이 PEP를 사용하기 위한 --lockfile 플래그를 추가하기 전에 테스트로 활용할 수도 있습니다.
이 모든 작업은 PEP가 조건부 승인에서 완전한 승인으로 전환되기 전에 요구될 수 있으며, 이를 통해 커뮤니티가 이 PEP가 잠재적으로 유용한지 테스트할 기회도 얻을 수 있습니다.
상호 운용성
이 시점의 목표는 도구 간 상호 운용성을 높이는 것입니다.
첫째, pip가 설치 프로그램이 됩니다. 가장 널리 사용되는 설치 프로그램이 이 형식을 지원하면, 사용자는 잠금 파일을 실제로 사용할 수 있는 도구가 제공된다는 사실을 알고 잠금 도구 측에서 혁신할 수 있습니다.
둘째, pip가 잠금 도구가 됩니다. 다시 말해, pip의 영향력 덕분에 대다수 Python 사용자가 매우 빠르게 이 형식에 접근할 수 있습니다.
셋째, 기존 잠금 파일 형식을 사용하는 프로젝트가 최소한 해당 형식에서 잠금 파일 형식으로 내보내기를 지원합니다(예: PDM 또는 Pyflow). 이는 이 형식이 다른 프로젝트의 요구 사항을 충족한다는 것을 보여 줍니다.
승인
커뮤니티 전반에서 사용할 수 있는 도구가 마련되면, Python 커뮤니티에만 국한되지 않은 도구들이 사용자가 원하는 바를 바탕으로 이 파일 형식을 지원함으로써 승인이 입증될 것입니다.
첫째, 코드 편집기처럼 요구 사항 파일을 처리하는 도구가 잠금 파일에 대해서도 동등한 지원을 제공합니다.
둘째, 클라우드 제공업체처럼 요구 사항 파일을 사용하는 주체도 잠금 파일을 허용합니다.
이 시점에서 이 PEP는 일반적인 승인 측면에서 요구 사항 파일과 대등한 수준까지 충분히 확산되며, 프로젝트들이 자체 잠금 파일을 폐기하고 이 PEP를 채택했다면 그 이상으로 확산될 수도 있습니다.
보안 관련 영향
잠금 파일은 보안 문제를 새로 도입하는 것이 아니라, 오히려 이러한 문제를 해결하는 데 도움이 되어야 합니다. 파일의 해시값을 기록하도록 요구하면, 해시 세부 정보가 기록되므로 잠금 파일이 코드 변조를 방지하는 데 도움이 될 수 있습니다. 휠 파일에만 의존하면 설치될 파일을 미리 알 수 있고 설치 결과를 재현할 수 있습니다. 또한 잠금 파일은 예기치 않은 패키지 업데이트가 설치되는 것을 방지하는 데 도움이 되며, 그러한 업데이트는 악의적일 수도 있습니다.
이를 가르치는 방법
이 PEP의 교육은 일상적으로 사용되는 잠금 파일 생성 도구와 설치 도구에 상당히 의존합니다. 그러나 개념적으로는 잠금 파일이 프로젝트가 작동하기 위해 무엇을 설치해야 하는지 지정한다는 점을 사용자에게 가르칠 수 있습니다. 일관성과 보안의 이점을 강조하여 사용자가 잠금 파일에 관심을 가져야 하는 이유를 이해하도록 해야 합니다.
참조 구현
개념 증명용 잠금 파일 생성 도구는 https://github.com/frostming/pep665_poc 에서 찾을 수 있습니다. 아직 설치 도구는 구현되지 않았지만, 이 PEP의 설계에 따르면 잠금 파일 생성 도구가 구현하기 더 어려운 부분인 것으로 보입니다.
거부된 아이디어
TOML 이외의 파일 형식
JSON_도 잠시 고려되었지만, 다음과 같은 이유 때문입니다:
- TOML이 이미
pyproject.toml에서 사용되고 있음 - TOML이 사람이 읽기에 더 쉬움
- TOML이 더 나은 diff를 생성함
따라서 TOML을 사용하기로 결정했습니다. Python의 표준 라이브러리에 TOML 파서가 없다는 우려가 있었지만, 대부분의 패키징 도구는 pyproject.toml덕분에 이미 TOML 파서를 사용하고 있으므로 이 문제는 치명적인 장애물로 보이지 않았습니다. 과거에도 일부에서는 이러한 우려에 반대하며, 패키징 도구가 의존성 설치를 몹시 꺼리고 패키지를 벤더링할 수 없다고 느낀다면, 서드파티 TOML 파서에 의존해야 한다는 필요성보다 패키징 생태계가 바로잡아야 할 훨씬 더 큰 문제를 안고 있는 것이라고 주장했습니다.
대체 명명 방식
파일을 설치할 디렉터리를 지정하는 방안이 고려되었지만, 결국 사람들이 그 아이디어를 달가워하지 않아 거부되었습니다.
특수한 파일 이름 접미사를 사용하지 말자는 제안도 있었지만, 그렇게 하면 도구가 파일을 검색하기가 너무 어려워진다고 판단했습니다.
단일 잠금 파일 지원
한때 가능한 모든 잠금 정보를 포함하는 단일 잠금 파일만 지원하는 아이디어가 고려되었습니다. 그러나 여러 환경을 지원할 수 있는 잠금 파일 형식과 재현 가능한 빌드를 위한 엄격한 잠금 결과를 모두 포괄하는 데이터 형식을 고안하려 하면 상당히 복잡하고 번거로워질 것이라는 점이 곧 분명해졌습니다.
잠금 파일 디렉터리와 pyproject-lock.toml이라는 이름의 단일 잠금 파일을 모두 지원하는 아이디어도 고려되었습니다. 그러나 단일 잠금 파일의 경우 디렉터리를 생략하여 얻을 수 있는 어떤 단순성도 불필요해 보였습니다. 무엇이 pyproject-lock.toml파일에 해당하고 무엇이 pyproject-lock.d에 들어가야 하는지에 대한 적절한 논리를 정의하려는 것은 불필요하게 복잡해 보였습니다.
의존성 그래프 대신 평면 목록 사용
이 PEP의 첫 번째 버전에서는 잠금 파일에 의존성 그래프라는 개념이 없도록 제안했습니다. 대신 잠금 파일에는 특정 플랫폼에 설치해야 할 항목을 정확히 나열하여, 설치 도구가 무엇을 설치할지 결정할 필요 없이 잠금 파일이 대상 플랫폼에서 작동하는지만 검증하도록 했습니다.
이 아이디어는 잠재적인 PEP 508 환경 마커 조합의 수 때문에 결국 거부되었습니다. 프로젝트가 여러 플랫폼에서 작동하기를 원할 때 잠금 파일 생성 도구가 가능한 모든 조합을 개별 잠금 파일로 생성하도록 하는 것은 지나치다고 판단했습니다.
파일 이름에 휠 태그 사용
metadata.tag 필드를 두는 대신 태그를 파일 이름에 인코딩하자는 제안이 있었습니다. 그러나 metadata.marker 필드가 추가되고 태그가 필요하지 않을 때 어떻게 처리할지에 대한 문제도 있어 이 아이디어는 폐기되었습니다.
requires의 대체 명칭
requires가 된 것의 다른 명칭으로는 installs, needs, dependencies가 있었습니다. 처음에 이 PEP에서는 Python 초보자에게 어떤 용어를 선호하는지 물은 후 needs를 선택했습니다. 그러나 이 PEP의 이전 초안에 대한 피드백을 바탕으로 requires를 용어로 선택했습니다.
PEP 650 수용
PEP 650은 잠금 파일 형식을 표준화하는 대신 설치 도구를 위한 API를 지정하여 이 문제를 해결하려고 했던 이전의 시도였습니다(PEP 517과 유사합니다). initial response는 PEP 650에 대한 응답으로서 미온적이거나 미적지근한 것으로 간주할 수 있습니다. 사람들은 어떤 도구가 PEP를 구현하기 위해 어떤 기능을 제공해야 하는지에 대해 일관되게 혼란스러워하는 듯했습니다. 또한 패키징과 관련된 작업을 수행하려면 Python API를 실행해야 하므로 잠재적으로 오버헤드가 더 커졌습니다.
이 PEP는 API(PEP 621과 유사함) 대신 아티팩트를 중심으로 표준화하는 방식을 선택합니다. 이는 잠금 파일을 만들거나 업데이트하거나, 심지어 잠금 파일에 나열된 패키지를 설치하는 등의 작업에 Python을 특별히 사용할 필요를 없애므로 더 많은 도구 통합을 가능하게 합니다. 또한 종속성 그래프 세부 정보를 사람이 읽을 수 있는 형식으로 작성하도록 하여 더 쉽게 내부를 살펴볼 수 있게 합니다. 또한 사람들이 더 많이 알아야 하는 내용을 표준화하므로 지식을 더 쉽게 공유할 수 있게 합니다(예를 들어 생성되는 아티팩트를 이해하는 측면에서 튜토리얼을 도구 간에 더 쉽게 이식할 수 있습니다). 또한 이는 단순히 다른 언어 커뮤니티가 채택했으며 만족하는 듯한 접근 방식입니다.
이 PEP를 수용하면 PEP 650은 거부됩니다.
파일별 대신 패키지별로 요구 사항 지정
이 PEP의 이전 초안에서는 파일별이 아니라 패키지 수준에서 종속성을 지정했습니다. 이는 전통적으로 패키징 시스템이 작동해 온 방식이지만, 실제로는 항목이 지정되는 방식을 정확히 반영하지 못했습니다. 따라서 이 PEP는 종속성을 실제로 지정할 수 있는 세부 수준을 반영하도록 이후에 업데이트되었습니다.
잠금 파일 생성 도구가 입력을 수집하는 위치 지정
이 PEP는 잠금 파일 생성 도구가 입력을 얻는 방법을 지정하지 않습니다. 처음에는 PEP 621을 부분적으로 재사용하자는 제안이 있었지만, 색인 등의 항목을 지정할 때 잠재적인 입력이 얼마나 유연해야 하는지를 두고 의견이 엇갈렸기 때문에 이는 별도의 PEP에 맡기는 것이 가장 좋다고 결정했습니다.
소스 배포판과 소스 트리를 선택적으로 사용할 수 있는 지원 파일 형식으로 허용
광범위한 논의후, 이 PEP에서는 소스 배포판(즉, sdists)이나 소스 트리를 코드에 허용되는 형식으로 지원하지 않기로 결정했습니다. 이 PEP에 sdists와 소스 트리를 도입하면 sdist 또는 소스 트리를 빌드하기 위해 코드를 실행해야 하므로 재현 가능성과 보안이라는 목표가 즉시 무산됩니다. 또한 sdists와 소스 트리의 동적 빌드 특성으로 인해 설치 도구가 빌드와 설치 양쪽 측면에서 sdists가 동적으로 생성한 모든 요구 사항을 완전히 해결해야 하므로, 적어도 설치 도구의 복잡성이 크게 증가합니다.
이러한 모든 이유로, 이 PEP가 수용되거나 거부된 후 sdists와 소스 트리의 지원 방안에 대해 별도로 논의하는 것이 최선이라고 결정했습니다. 제안된 파일 형식은 버전이 관리되므로 이후 PEP에서 sdists와 소스 트리 지원을 도입하는 것이 가능합니다.
다만 이 PEP와 함께 사용하기 위한 별도의 해결책이 개발되는 것을 이 PEP가 막지는 않는다는 점에 유의해야 합니다. sdists에서 휠 파일을 빌드하고 배포 시 코드와 함께 제공하여 잠금 파일에 포함할 수 있도록 하는 것이 한 가지 방법입니다. 또 다른 방법은 sdists와 소스 트리에 오직 사용하는 요구 사항 파일을 만든 다음, 모든 휠에는 잠금 파일을 사용하는 것입니다.
미해결 문제
없음
감사의 말
PDM의 Frost Ming과 Poetry의 Sébastien Eustace께 PEP 508 요구 사항의 설치 시점 동적 해결에 관한 의견을 제공해 주셔서 감사드립니다.
재현 가능한 빌드가 이 PEP에서 계속 중요한 관심사로 남도록 해 주신 Kushal Das께 감사드립니다.
처음에 사소한 논쟁을 정리하고 needs의 색상을 선택해 주신 Andrea McInnes께 감사드립니다(그 시점에 사람들은 대신 requires의 색상을 지지했습니다).
Copyright
This document is placed in the public domain or under the CC0-1.0-Universal license, whichever is more permissive.