使用 pyproject.toml 构建时如何正确捕获包中的库/包版本

问题描述 投票:0回答:2

我已经不再使用

setup.py
文件来构建我的包/库,而是完全使用
pyproject.toml
。总的来说,我更喜欢它,但似乎放置在
pyproject.toml
中的版本不会以任何方式通过构建传播。因此,我无法弄清楚如何将包版本(或 pyproject.toml 中提供的任何其他元数据)注入到我的包中。

谷歌搜索让我找到了这个帖子,其中有一些建议。它们看起来都像是黑客,但我尝试了这个,因为它看起来最好:

from pip._vendor import tomli ## I need to be backwards compatible to Python 3.8

with open("pyproject.toml", "rb") as proj_file:
    _METADATA = tomli.load(proj_file)

DESCRIPTION = _METADATA["project"]["description"]
NAME = _METADATA["project"]["name"]
VERSION = _METADATA["project"]["version"]

它在测试时工作正常,但我的测试不够稳健:一旦我尝试将其安装在新位置/机器上,它就会失败,因为

pyproject.toml
文件不是软件包安装的一部分。 (我应该意识到这一点。)


那么,向我构建的包提供元数据(例如包版本)的正确/最佳方式是什么?我需要满足以下要求:

  1. 想在
    pyproject.toml
    中提供一次信息。 (我知道如果我需要重复一个值,在某些时候就会出现不匹配。)
  2. 我希望最终用户可以使用这些信息,以便安装该包的人可以从她的交互式 Python 会话中执行类似
    mypackage.VERSION
    的操作。
  3. 我只想使用pyproject.toml
    和诗歌/PDM。 (我实际上使用 PDM,但我知道 Poetry 更受欢迎。重点是我不想要 
    setup.py
    setup.cfg
     hack。我想纯粹使用新方法。)
python metadata python-packaging
2个回答
1
投票
要使用

pyproject.toml

 向构建的包提供元数据(包括包版本),您可以使用 
importlib.metadata
 模块,该模块自版本 3.8 起成为 Python 标准库的一部分。该模块允许您访问已安装软件包的元数据,包括您自己的软件包,而无需直接读取 
pyproject.toml
 文件。

以下是实现此目标的方法:

  1. 确保您的开发环境中已安装

    importlib_metadata

    。如果您使用的是 Python 3.8 或更高版本,它应该已经可用。

  2. 在您的

    pyproject.toml

    中,确保您拥有必要的元数据信息,包括项目名称和版本。例如:

[project] name = "mypackage" version = "1.0.0" description = "My package description"

    在代码中,您可以使用
  1. importlib.metadata
     模块访问元数据:
import importlib.metadata as metadata def get_package_version(package_name): return metadata.version(package_name) def get_package_description(package_name): return metadata.metadata(package_name)["Summary"] # Example usage package_name = "mypackage" package_version = get_package_version(package_name) package_description = get_package_description(package_name) print(package_version) print(package_description)
请注意,

importlib.metadata

会读取已安装软件包的元数据,因此您需要在访问元数据之前确保您的软件包已安装(例如,使用 Poetry、PDM 或其他软件包管理器)。安装后,最终用户将可以使用元数据,并且假设您在包的主模块中定义了相关变量,他们可以使用 
mypackage.VERSION
mypackage.DESCRIPTION
 从交互式 Python 会话中访问元数据。

此方法满足您的要求:

    您只需在
  1. pyproject.toml
    提供一次信息。
  2. 这些信息将提供给最终用户,他们可以使用
  3. mypackage.VERSION
     访问它。
  4. 此方法与
  5. pyproject.toml
     兼容,不需要 
    setup.py
    setup.cfg
     hack。

0
投票
如您所见,原始

源树

中的
pyproject.toml文件通常无法从已安装的软件包中读取。但是,包元数据可在包安装旁边的 .dist-info 子目录中找到。

必要时使用 stdlib

importlib.metadata

 从包元数据中读取版本。版本字符串保证存在于已安装的软件包中,因为它是元数据规范中的“必填字段”。访问元数据并非易事的所有 Python 版本现已停产。
您可以使用 __package__

属性,这样您就不必在源代码中硬编码包名称:

# can be accessed anywhere within the package
import importlib.metadata

the_version_str = importlib.metadata.version(__package__)

这满足第 1 点和第 3 点。
对于第 2 点:

我希望最终用户可以使用这些信息,以便安装该包的人可以从她的交互式 Python 会话中执行类似 mypackage.VERSION 的操作。

类似的食谱有效:

# in your_package/__init__.py import importlib.metadata VERSION = importlib.metadata.version(__package__)

但是,我建议
提供版本属性。这样做有几个缺点,原因我已经在

here描述过。如果您以前提供过版本属性,并且出于向后兼容性考虑而需要保留它,您可以考虑使用后备模块来维护支持__getattr__,当通过通常的方式找不到属性时将调用该模块:

def __getattr__(name):
    if name == "VERSION":
        # consider adding a deprecation warning here advising user to call
        # importlib.metadata.version directly
        return importlib.metadata.version(__package__)
    raise AttributeError(f"module {__name__!r} has no attribute {name!r}")


© www.soinside.com 2019 - 2024. All rights reserved.