PEP 631 – 基於 PEP 508 在 pyproject.toml 中規範依賴關係
- 作者:
- Ofek Lev <ofekmeister at gmail.com>
- 贊助人:
- Paul Ganssle <paul at ganssle.io>
- 討論於:
- Discourse 討論串
- 狀態:
- 已取代
- 類型:
- 標準軌跡 (Standards Track)
- 主題:
- 套件封裝 (Packaging)
- 建立日期:
- 2020年8月20日
- 公告歷史:
- 2020年8月20日
- 被以下文件取代:
- 621
- 決議:
- Discourse 訊息
摘要
本 PEP 規範了如何使用 PEP 621 中定義的欄位,在 pyproject.toml 檔案中撰寫項目的依賴關係,以便打包相關工具使用。
附註
本 PEP 已獲接受並併入 PEP 621。
項目 (Entries)
所有依賴項目必須是有效的 PEP 508 字串。
建置後端 (Build backends) 應在載入時針對任何解析錯誤予以中止。
from packaging.requirements import InvalidRequirement, Requirement
...
try:
Requirement(entry)
except InvalidRequirement:
# exit
規範
dependencies
- 格式:字串陣列
- 相關核心元數據
每個元素必須是一個項目 (entry)。
[project]
dependencies = [
'PyYAML ~= 5.0',
'requests[security] < 3',
'subprocess32; python_version < "3.2"',
]
可選依賴 (optional-dependencies)
- 格式:表格 (table)
- 相關核心元數據
每個鍵 (key) 為提供的選項名稱,每個值 (value) 與 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
元數據 (Metadata)
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
版權
本文件已進入公有領域或遵循 CC0-1.0-Universal 授權,以較寬鬆者為準。
來源:https://github.com/python/peps/blob/main/peps/pep-0631.rst