importlib.metadata – 访问包元数据

在版本 3.8 中添加。

在版本 3.10 中更改: importlib.metadata 不再是临时的。

源代码: Lib/importlib/metadata/__init__.py

importlib.metadata 是一个库,它提供对已安装 发行版包 的元数据的访问,例如它的入口点或它的顶级名称 (导入包、模块,如果有的话)。该库部分基于 Python 的导入系统,旨在替换 入口点 API元数据 API 中的类似功能 pkg_resources。与 importlib.resources 一起,这个包可以消除使用旧的、效率较低的 pkg_resources 包的需要。

importlib.metadata 对通过 pip 等工具安装到 Python 的 site-packages 目录中的第三方发行版包进行操作。具体来说,它适用于具有可发现的 dist-infoegg-info 目录以及由 核心元数据规范 定义的元数据的发行版。

重要

这些一定等同于或与可以在 Python 代码中导入的顶级导入包名称一一对应。一个发行版包可以包含多个导入包(和单个模块),而一个顶级导入包如果是一个命名空间包,则可能映射到多个发行版包。您可以使用 package_distributions() 来获取它们之间的映射。

默认情况下,发行版元数据可以位于 sys.path 上的文件系统或 zip 存档中。通过扩展机制,元数据几乎可以位于任何地方。

另请参阅

https://importlib-metadata.readthedocs.io/

有关 importlib_metadata 的文档,它提供了 importlib.metadata 的移植。这包括该模块的类和函数的 API 参考,以及针对 pkg_resources 的现有用户的 迁移指南

概述

假设您想获取使用 pip 安装的 发行版包 的版本字符串。我们首先创建一个虚拟环境并将一些东西安装到其中

$ python -m venv example
$ source example/bin/activate
(example) $ python -m pip install wheel

您可以通过运行以下命令来获取 wheel 的版本字符串

(example) $ python
>>> from importlib.metadata import version  
>>> version('wheel')  
'0.32.3'

您还可以获取可通过 EntryPoint 属性(通常是“group”或“name”)选择的入口点集合,例如 console_scriptsdistutils.commands 等。每个组都包含一个 EntryPoint 对象的集合。

您可以获取 发行版的元数据

>>> list(metadata('wheel'))  
['Metadata-Version', 'Name', 'Version', 'Summary', 'Home-page', 'Author', 'Author-email', 'Maintainer', 'Maintainer-email', 'License', 'Project-URL', 'Project-URL', 'Project-URL', 'Keywords', 'Platform', 'Classifier', 'Classifier', 'Classifier', 'Classifier', 'Classifier', 'Classifier', 'Classifier', 'Classifier', 'Classifier', 'Classifier', 'Classifier', 'Classifier', 'Requires-Python', 'Provides-Extra', 'Requires-Dist', 'Requires-Dist']

您还可以获取 发行版的版本号,列出它的 组成文件,并获取发行版的 发行版需求 列表。

函数式 API

此包通过其公共 API 提供以下功能。

入口点

entry_points() 函数返回一个入口点集合。入口点由 EntryPoint 实例表示;每个 EntryPoint 都有 .name.group.value 属性以及一个 .load() 方法来解析值。还有 .module.attr.extras 属性用于获取 .value 属性的组件。

查询所有入口点

>>> eps = entry_points()  

entry_points() 函数返回一个 EntryPoints 对象,它是所有 EntryPoint 对象的集合,具有 namesgroups 属性以方便使用

>>> sorted(eps.groups)  
['console_scripts', 'distutils.commands', 'distutils.setup_keywords', 'egg_info.writers', 'setuptools.installation']

EntryPoints 具有一个 select 方法来选择匹配特定属性的入口点。选择 console_scripts 组中的入口点

>>> scripts = eps.select(group='console_scripts')  

等效地,因为 entry_points 将关键字参数传递给 select

>>> scripts = entry_points(group='console_scripts')  

挑选一个名为“wheel”的特定脚本(在 wheel 项目中找到)

>>> 'wheel' in scripts.names  
True
>>> wheel = scripts['wheel']  

等效地,在选择期间查询该入口点

>>> (wheel,) = entry_points(group='console_scripts', name='wheel')  
>>> (wheel,) = entry_points().select(group='console_scripts', name='wheel')  

检查已解析的入口点

>>> wheel  
EntryPoint(name='wheel', value='wheel.cli:main', group='console_scripts')
>>> wheel.module  
'wheel.cli'
>>> wheel.attr  
'main'
>>> wheel.extras  
[]
>>> main = wheel.load()  
>>> main  
<function main at 0x103528488>

groupname 是由包作者定义的任意值,通常客户端希望解析特定组的所有入口点。阅读 setuptools 文档 以获取有关入口点、其定义和用法的更多信息。

兼容性说明

“可选”入口点是在 importlib_metadata 3.6 和 Python 3.10 中引入的。在这些更改之前,entry_points 不接受任何参数,并且始终返回一个入口点字典,以组为键。使用 importlib_metadata 5.0 和 Python 3.12,entry_points 始终返回一个 EntryPoints 对象。有关兼容性选项,请参阅 backports.entry_points_selectable

发行版元数据

每个 发行版包 都包含一些元数据,您可以使用 metadata() 函数提取这些元数据。

>>> wheel_metadata = metadata('wheel')  

返回的数据结构(一个 PackageMetadata)的键命名元数据关键字,值从发行版元数据中未解析地返回。

>>> wheel_metadata['Requires-Python']  
'>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*'

PackageMetadata 还提供了一个 json 属性,该属性根据 PEP 566 以 JSON 兼容的形式返回所有元数据。

>>> wheel_metadata.json['requires_python']
'>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*'

注意

metadata() 返回的对象的实际类型是实现细节,应该只通过 PackageMetadata 协议 描述的接口访问。

在版本 3.10 中更改: 现在,通过有效负载呈现时,元数据中包含 Description。行延续字符已被删除。

添加了 json 属性。

发行版版本

version() 函数是获取 发行版包 版本号(作为字符串)的最快方法。

>>> version('wheel')  
'0.32.3'

发行版文件

您还可以获取包含在发行版中的所有文件集。 files() 函数接受一个 发行版包 名称,并返回此发行版安装的所有文件。返回的每个文件对象都是一个 PackagePath,一个 pathlib.PurePath 派生对象,具有元数据指示的附加 distsizehash 属性。例如

>>> util = [p for p in files('wheel') if 'util.py' in str(p)][0]  
>>> util  
PackagePath('wheel/util.py')
>>> util.size  
859
>>> util.dist  
<importlib.metadata._hooks.PathDistribution object at 0x101e0cef0>
>>> util.hash  
<FileHash mode: sha256 value: bYkw5oMccfazVCoYQwKkkemoVyMAFoR34mmKBx8R1NI>

获得文件后,您还可以读取其内容。

>>> print(util.read_text())  
import base64
import sys
...
def as_bytes(s):
    if isinstance(s, text_type):
        return s.encode('utf-8')
    return s

您还可以使用 locate 方法获取文件的绝对路径。

>>> util.locate()  
PosixPath('/home/gustav/example/lib/site-packages/wheel/util.py')

在列出文件的元数据文件(RECORD 或 SOURCES.txt)丢失的情况下,files() 将返回 None。如果目标发行版已知没有元数据,则调用者可能希望将对 files() 的调用包装在 always_iterable 中或以其他方式防止这种情况。

发行版要求

要获取 发行版包 的完整要求集,请使用 requires() 函数。

>>> requires('wheel')  
["pytest (>=3.0.0) ; extra == 'test'", "pytest-cov ; extra == 'test'"]

将导入映射到发行版包

一个方便的方法来解析提供每个可导入的顶级 Python 模块或 导入包发行版包 名称(或名称,在命名空间包的情况下)。

>>> packages_distributions()
{'importlib_metadata': ['importlib-metadata'], 'yaml': ['PyYAML'], 'jaraco': ['jaraco.classes', 'jaraco.functools'], ...}

一些可编辑的安装 不提供顶级名称,因此此函数对于此类安装不可靠。

在版本 3.10 中添加。

发行版

虽然上面的 API 是最常见和最方便的使用方式,但您可以从 Distribution 类中获取所有这些信息。 Distribution 是一个抽象对象,它代表 Python 发行版包 的元数据。您可以获取 Distribution 实例。

>>> from importlib.metadata import distribution  
>>> dist = distribution('wheel')  

因此,获取版本号的另一种方法是通过 Distribution 实例。

>>> dist.version  
'0.32.3'

Distribution 实例上提供了各种其他元数据。

>>> dist.metadata['Requires-Python']  
'>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*'
>>> dist.metadata['License']  
'MIT'

此处未描述所有可用的元数据。有关更多详细信息,请参阅 核心元数据规范

发行版发现

默认情况下,此包为文件系统和 zip 文件 发行版包 的元数据发现提供内置支持。此元数据查找器搜索默认设置为 sys.path,但它解释这些值的细微差别与其他导入机制略有不同。特别是

  • importlib.metadata 不尊重 sys.path 上的 bytes 对象。

  • importlib.metadata 偶然会尊重 sys.path 上的 pathlib.Path 对象,即使这些值将被忽略以进行导入。

扩展搜索算法

因为 发行版包 元数据无法通过 sys.path 搜索或包加载器直接获得,因此发行版包的元数据是通过导入系统 查找器 找到的。为了找到发行版包的元数据,importlib.metadata 查询 元路径查找器sys.meta_path 上的列表。

默认情况下,importlib.metadata 为文件系统上找到的发行版包安装一个查找器。此查找器实际上不会找到任何发行版,但它可以找到它们的元数据。

抽象类 importlib.abc.MetaPathFinder 定义了 Python 导入系统对查找器的预期接口。 importlib.metadata 通过在 sys.meta_path 中的查找器上查找可选的 find_distributions 可调用函数来扩展此协议,并将此扩展的接口呈现为 DistributionFinder 抽象基类,该类定义了此抽象方法

@abc.abstractmethod
def find_distributions(context=DistributionFinder.Context()):
    """Return an iterable of all Distribution instances capable of
    loading the metadata for packages for the indicated ``context``.
    """

DistributionFinder.Context 对象提供 .path.name 属性,指示要搜索的路径和要匹配的名称,并可能提供其他相关上下文。

在实践中,这意味着要支持在文件系统以外的位置查找发行版包元数据,请子类化 Distribution 并实现抽象方法。然后从自定义查找器中,在 find_distributions() 方法中返回此派生 Distribution 的实例。