Документация Python неофициальный перевод
Содержание страницы

importlib.metadata – Доступ к метаданным пакетаimportlib.metadata – Accessing package metadata

Новое в версии 3.8.

Изменено в версии 3.10: importlib.metadata больше не является предварительным.

Исходный код: Lib/importlib/metadata/__init__.py

importlib.metadata – библиотека, предоставляющая доступ к метаданным установленного дистрибутивного пакета (Distribution Package), например, к его точкам входа (entry points) или именам верхнего уровня (импортируемые пакеты (Import Package), модули, если они есть). Основанная на системе импорта Python, эта библиотека призвана заменить аналогичную функциональность из API точек входа и API метаданных из pkg_resources. Вместе с importlib.resources этот пакет позволяет отказаться от использования более старого и менее эффективного пакета pkg_resources.

importlib.metadata работает со сторонними дистрибутивными пакетами, установленными в каталог site-packages Python с помощью таких инструментов, как pip. В частности, он работает с дистрибутивами, имеющими обнаруживаемые каталоги dist-info или egg-info, и метаданными, определёнными спецификациями основных метаданных.

Важно

Они не обязательно эквивалентны или соответствуют 1:1 именам верхнего уровня импортируемых пакетов, которые можно импортировать в коде Python. Один дистрибутивный пакет может содержать несколько импортируемых пакетов (и отдельных модулей), а один импортируемый пакет верхнего уровня может соответствовать нескольким дистрибутивным пакетам, если это пакет пространства имен. Для получения соответствия между ними можно использовать package_distributions().

По умолчанию метаданные дистрибутива могут располагаться в файловой системе или в zip-архивах на sys.path. Благодаря механизму расширений метаданные могут находиться практически где угодно.

См. также

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

Документация для importlib_metadata, который предоставляет обратный порт importlib.metadata. Она включает справочник API для классов и функций этого модуля, а также руководство по миграции для существующих пользователей pkg_resources.

ОбзорOverview

Допустим, нужно получить строку версии для дистрибутивного пакета, установленного с помощью 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 и другие. Каждая группа содержит набор объектов 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']

Вы также можете получить номер версии дистрибутива, перечислить его составные файлы и получить список зависимостей дистрибутива.

Функциональное APIFunctional API

Этот пакет предоставляет следующие функции через свой публичный API.

Точки входаEntry points

Функция entry_points() возвращает коллекцию точек входа. Точки входа представлены экземплярами EntryPoint; каждый EntryPoint имеет атрибуты .name, .group и .value, а также метод .load() для получения значения. Также есть атрибуты .module, .attr и .extras для получения компонентов атрибута .value.

Запрос всех точек входа:

>>> eps = entry_points()  

Функция entry_points() возвращает объект EntryPoints, коллекцию всех объектов EntryPoint с атрибутами names и groups для удобства:

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

group и name – произвольные значения, определяемые автором пакета; обычно клиент хочет получить все точки входа для определённой группы. Подробнее о точках входа, их определении и использовании читайте в документации setuptools.

Примечание о совместимости

«Выбираемые» точки входа были введены в importlib_metadata 3.6 и Python 3.10. До этих изменений entry_points не принимал параметров и всегда возвращал словарь точек входа, сгруппированных по группе. Для совместимости, если в entry_points не переданы параметры, возвращается объект SelectableGroups, реализующий интерфейс словаря. В будущем вызов entry_points без параметров будет возвращать объект EntryPoints. Пользователям следует полагаться на интерфейс выбора для получения точек входа по группе.

Метаданные дистрибутиваDistribution metadata

Каждый дистрибутивный пакет содержит некоторые метаданные, которые можно извлечь с помощью функции metadata():

>>> wheel_metadata = metadata('wheel')  

Ключи возвращаемой структуры данных, PackageMetadata, определяют ключевые слова метаданных, а значения возвращаются неразобранными из метаданных дистрибутива:

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

PackageMetadata также предоставляет атрибут json, который возвращает все метаданные в JSON-совместимой форме согласно PEP 566:

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

Примечание

Фактический тип объекта, возвращаемого metadata(), является деталью реализации, и к нему следует обращаться только через интерфейс, описанный протоколом PackageMetadata.

Изменено в версии 3.10: Теперь Description включается в метаданные при представлении через полезную нагрузку (payload). Символы продолжения строки удалены.

Добавлен атрибут json.

Версии дистрибутивовDistribution versions

Функция version() – это самый быстрый способ получить номер версии пакета дистрибутива в виде строки:

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

Файлы дистрибутиваDistribution files

Также можно получить полный набор файлов, содержащихся в дистрибутиве. Функция files() принимает имя дистрибутива и возвращает все файлы, установленные этим дистрибутивом. Каждый возвращаемый файловый объект является PackagePath, объектом, производным от pathlib.PurePath, с дополнительными свойствами dist, size и hash, как указано в метаданных. Например:

>>> 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 или иным образом защититься от этой ситуации, если неизвестно, что целевой дистрибутив содержит метаданные.

Зависимости дистрибутивовDistribution requirements

Чтобы получить полный набор зависимостей для дистрибутивного пакета, используйте функцию requires():

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

Отображение импортов на дистрибутивыMapping import to distribution packages

Удобный метод для разрешения имени дистрибутивного пакета (или имён, в случае пакета пространства имён), который предоставляет каждый импортируемый модуль или импортируемый пакет верхнего уровня:

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

Новое в версии 3.10.

ДистрибутивыDistributions

Хотя вышеописанный 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'

Полный набор доступных метаданных здесь не описан. Дополнительные сведения см. в спецификациях основных метаданных.

Обнаружение дистрибутивовDistribution Discovery

По умолчанию этот пакет предоставляет встроенную поддержку поиска метаданных для дистрибутивных пакетов файловой системы и zip-файлов. Поиск метаданных по умолчанию использует sys.path, но трактует эти значения немного иначе, чем другие механизмы импорта. В частности:

  • importlib.metadata не учитывает объекты bytes из sys.path.

  • importlib.metadata случайно будет учитывать объекты pathlib.Path из sys.path, хотя такие значения игнорируются при импорте.

Расширение алгоритма поискаExtending the search algorithm

Поскольку метаданные дистрибутивного пакета недоступны через поиск sys.path или напрямую через загрузчики пакетов, метаданные дистрибутива находятся через искатели системы импорта. Чтобы найти метаданные дистрибутивного пакета, importlib.metadata запрашивает список искателей метапути на sys.meta_path.

По умолчанию importlib.metadata устанавливает находильщик для дистрибутивных пакетов, найденных в файловой системе. Этот находильщик на самом деле не находит никакие дистрибутивы, но может находить их метаданные.

Абстрактный класс importlib.abc.MetaPathFinder определяет интерфейс, ожидаемый от искателей системой импорта Python. importlib.metadata расширяет этот протокол, проверяя наличие опционального вызываемого объекта find_distributions у искателей из sys.meta_path, и предоставляет этот расширенный интерфейс как абстрактный базовый класс DistributionFinder, который определяет следующий абстрактный метод:

@abc.abstractmethod
def find_distributions(context=DistributionFinder.Context()):
    """Возвращает итерируемый объект со всеми экземплярами Distribution, способными
    загружать метаданные для пакетов для указанного ``context``.
    """

Объект DistributionFinder.Context предоставляет свойства .path и .name, указывающие путь для поиска и имя для сопоставления, а также может предоставлять другой релевантный контекст.

На практике это означает, что для поддержки поиска метаданных дистрибутивного пакета в местах, отличных от файловой системы, нужно создать подкласс Distribution и реализовать абстрактные методы. Затем из пользовательского находильщика возвращать экземпляры этого производного Distribution в методе find_distributions().