如何在 Python 包元数据中指定要求

How to specify requirements in Python packages metadata

Core metadata specification 记录了元数据字段 Requires-External,它似乎用于指定系统(非 python)依赖项。

你实际上是如何指定这个字段的?这是我试过的:

.
├── mypackage
│   └── __init__.py
└── setup.py

setup.py

的内容
from setuptools import setup

setup(
    name="mypackage",
    description="blah blah",
    url='https://example.org',
    version="0.1",
    packages=["mypackage"],
    requires_external=[
        "C",
        "libpng (>=1.5)",
        'make; sys_platform != "win32"',
    ],
)

当我构建这个包时,没有包含那个元数据

Metadata-Version: 2.1
Name: mypackage
Version: 0.1
Summary: blah blah
Home-page: https://example.org
License: UNKNOWN
Platform: UNKNOWN

UNKNOWN

那么将 Requires-External 传递给 setuptools/distutils 的语法是什么?注意:这个问题不是询问 Requires-Dist 元数据。

So what is the syntax to pass Requires-External to setuptools/distutils?

默认有none,因为distutilssetuptools都不支持该字段。此外,requires_external 关键字 arg 也不受支持 - 它会被静默忽略,就像任何其他未知关键字 arg 一样。

要添加对 requires_external kwarg 的本地支持,您需要提供自己的分发实现。最小示例(需要 Python 3.9):

from setuptools.dist import Distribution as DistributionOrig

class Distribution(DistributionOrig):
    _DISTUTILS_UNSUPPORTED_METADATA = DistributionOrig._DISTUTILS_UNSUPPORTED_METADATA | {'requires_external': dict}

当与 distclass:

配对时,您现在可以将 requires_external 传递给 setup()
setup(
    ...,
    requires_external=[
        'C',
        'libpng (>=1.5)',
        'make; sys_platform != "win32"',
    ],
    distclass=Distribution,
)

下一个目标是将requires_external的内容真正写到PKG-INFO。这通过分发元数据本身来完成是相当棘手的,因为 setuptools 用自己的整体实现修补相关方法(DistributionMetadata.read_pkg_file()DistributionMetadata.write_pkg_file())。这个 IMO 最简单的方法是通过自定义 egg_info impl:

修改 PKG-INFO post factum
import email
from pathlib import Path
from setuptools.command.egg_info import egg_info as egg_info_orig


class egg_info(egg_info_orig):
    def run(self):
        super().run()
        # PKG-INFO is now guaranteed to exist
        pkg_info_file = Path(self.egg_info, 'PKG-INFO')
        pkg_info = email.message_from_bytes(pkg_info_file.read_bytes())
        for req in self.distribution.metadata.requires_external:
            pkg_info.add_header('Requires-External', req)
        pkg_info_file.write_bytes(pkg_info.as_bytes())

通过 setup() 中的 cmdclass 参数传递您自己的 egg_info impl:

setup(
    ...,
    requires_external=[
        'C',
        'libpng (>=1.5)',
        'make; sys_platform != "win32"',
    ],
    distclass=Distribution,
    cmdclass={'egg_info': egg_info},
)