PEP 631 – PEP 508 기반 pyproject.toml 의존성 명세
- Author:
- Ofek Lev <ofekmeister at gmail.com>
- Sponsor:
- Paul Ganssle <paul at ganssle.io>
- Discussions-To:
- Discourse thread
- Status:
- Superseded
- Type:
- Standards Track
- Topic:
- Packaging
- Created:
- 20-Aug-2020
- Post-History:
- 20-Aug-2020
- Superseded-By:
- 621
- Resolution:
- Discourse message
번역·라이선스 안내
이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain or CC0-1.0, whichever is more permissive 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판
초록
이 PEP는 패키징 관련 도구가 사용할 수 있도록 PEP 621에 정의된 필드를 사용하여 pyproject.toml 파일에 프로젝트의 의존성을 작성하는 방법을 규정합니다.
Note
이 PEP는 승인되었으며 PEP 621에 병합되었습니다.
항목
모든 의존성 항목은 반드시 유효한 PEP 508 문자열이어야 합니다(MUST).
빌드 백엔드는 파싱 오류가 발생하면 로드 시점에 중단해야 합니다(SHOULD).
from packaging.requirements import InvalidRequirement, Requirement
...
try:
Requirement(entry)
except InvalidRequirement:
# exit
명세
의존성
- 형식: 문자열 배열
- 관련 코어 메타데이터:
모든 요소는 반드시 항목이어야 합니다(MUST).
[project]
dependencies = [
'PyYAML ~= 5.0',
'requests[security] < 3',
'subprocess32; python_version < "3.2"',
]
선택적 의존성
- 형식: 테이블
- 관련 코어 메타데이터:
각 키는 제공되는 옵션의 이름이며, 각 값은 dependencies 필드와 같은 타입, 즉 문자열 배열입니다.
[project.optional-dependencies]
tests = [
'coverage>=5.0.3',
'pytest',
'pytest-benchmark[histogram]>=3.2.1',
]
예시
다음은 docker-compose가 정의하는 내용을 옮겨 온 실제 사례입니다.
[project]
dependencies = [
'cached-property >= 1.2.0, < 2',
'distro >= 1.5.0, < 2',
'docker[ssh] >= 4.2.2, < 5',
'dockerpty >= 0.4.1, < 1',
'docopt >= 0.6.1, < 1',
'jsonschema >= 2.5.1, < 4',
'PyYAML >= 3.10, < 6',
'python-dotenv >= 0.13.0, < 1',
'requests >= 2.20.0, < 3',
'texttable >= 0.9.0, < 2',
'websocket-client >= 0.32.0, < 1',
# Conditional
'backports.shutil_get_terminal_size == 1.0.0; python_version < "3.3"',
'backports.ssl_match_hostname >= 3.5, < 4; python_version < "3.5"',
'colorama >= 0.4, < 1; sys_platform == "win32"',
'enum34 >= 1.0.4, < 2; python_version < "3.4"',
'ipaddress >= 1.0.16, < 2; python_version < "3.3"',
'subprocess32 >= 3.5.4, < 4; python_version < "3.2"',
]
[project.optional-dependencies]
socks = [ 'PySocks >= 1.5.6, != 1.5.7, < 2' ]
tests = [
'ddt >= 1.2.2, < 2',
'pytest < 6',
'mock >= 1.0.1, < 4; python_version < "3.4"',
]
구현
파싱
from packaging.requirements import InvalidRequirement, Requirement
def parse_dependencies(config):
dependencies = config.get('dependencies', [])
if not isinstance(dependencies, list):
raise TypeError('Field `project.dependencies` must be an array')
for i, entry in enumerate(dependencies, 1):
if not isinstance(entry, str):
raise TypeError(f'Dependency #{i} of field `project.dependencies` must be a string')
try:
Requirement(entry)
except InvalidRequirement as e:
raise ValueError(f'Dependency #{i} of field `project.dependencies` is invalid: {e}')
return dependencies
def parse_optional_dependencies(config):
optional_dependencies = config.get('optional-dependencies', {})
if not isinstance(optional_dependencies, dict):
raise TypeError('Field `project.optional-dependencies` must be a table')
optional_dependency_entries = {}
for option, dependencies in optional_dependencies.items():
if not isinstance(dependencies, list):
raise TypeError(
f'Dependencies for option `{option}` of field '
'`project.optional-dependencies` must be an array'
)
entries = []
for i, entry in enumerate(dependencies, 1):
if not isinstance(entry, str):
raise TypeError(
f'Dependency #{i} of option `{option}` of field '
'`project.optional-dependencies` must be a string'
)
try:
Requirement(entry)
except InvalidRequirement as e:
raise ValueError(
f'Dependency #{i} of option `{option}` of field '
f'`project.optional-dependencies` is invalid: {e}'
)
else:
entries.append(entry)
optional_dependency_entries[option] = entries
return optional_dependency_entries
메타데이터
def construct_metadata_file(metadata_object):
"""
https://packaging.python.org/specifications/core-metadata/
"""
metadata_file = 'Metadata-Version: 2.1\n'
...
if metadata_object.dependencies:
# Sort dependencies to ensure reproducible builds
for dependency in sorted(metadata_object.dependencies):
metadata_file += f'Requires-Dist: {dependency}\n'
if metadata_object.optional_dependencies:
# Sort extras and dependencies to ensure reproducible builds
for option, dependencies in sorted(metadata_object.optional_dependencies.items()):
metadata_file += f'Provides-Extra: {option}\n'
for dependency in sorted(dependencies):
if ';' in dependency:
metadata_file += f'Requires-Dist: {dependency} and extra == "{option}"\n'
else:
metadata_file += f'Requires-Dist: {dependency}; extra == "{option}"\n'
...
return metadata_file
Copyright
This document is placed in the public domain or under the CC0-1.0-Universal license, whichever is more permissive.