importlib.metadata -- 访问软件包元数�¶

在 3.8 版本加入.

在 3.10 版本�生�更: importlib.metadata ��是暂定的。

�代�: Lib/importlib/metadata/__init__.py

importlib.metadata 是一个�供对已安装的 分�包 的元数�的访问的库,如其入�点或其最高层级�称 (导入包, 模�等,如果存在的�)。 这个库部分构建于 Python 的导入系统之上,其目标是�代 pkg_resources 的中的 entry point API 和 metadata API。 �� importlib.resources ,这个包�以消除使用较�旧且低效的 pkg_resources 包的必�性。

importlib.metadata 对通过 pip 之类的工具安装到 Python 的 site-packages 目录的第三方 分�包 进行�作。 具体�说,它适用于带有��现 dist-info 或 egg-info 目录,以�由 核心元数�规范说明 所定义的元数�的分�包。

��

这些 并� 必须等�于或 1:1 对应于�在 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_scripts, distutils.commands 等等。 �个 group 包�一个由 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 对象,�由带有 names 和 groups 属性的全部 EntryPoint 对象组�的多项集以方便使用:

>>> 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 传递关键字�数 "group" 以实现相�的效果:

>>> 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>

group 和 name 是由包作者定义的任�值并且通常�说客户端会想�解�特定 group 的所有入�点。 请�阅 the setuptools docs 了解有关入�点,其定义和用法的更多信�。

兼容性说明

"selectable" 入�点是在 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 也�供了按照 PEP 566 将所有元数�以 JSON 兼容的方�返回的 json 属性:

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

备注

metadata() 所返回的对象的实际类型是一个实现细节并且应当�能通过 PackageMetadata �议 所�述的接��访问。

在 3.10 版本�生�更: 当有效载�中包�时,Description 以去除续行符的形�被包�于元数�中。

在 3.10 版本加入: 添加了 json 属性。

分�的版本¶

version() 函数�以最快�地以字符串形�获�一个 分�包 的版本�:

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

分�的文件¶

你还�以获�包�在分�包内的全部文件的集�。 files() 函数接�一个 分�包 �称并返回此分�包所安装的全部文件。 �个返回的文件对象都是一个 PackagePath,�带有由元数�指明的�外 dist, size 和 hash 特�属性的派生自 pathlib.PurePath 的对象。 例如:

>>> 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 实例。