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

Использование importlib.metadataUsing importlib.metadata

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

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

Примечание

Эта функциональность является предварительной и может отклоняться от обычной семантики версий стандартной библиотеки.

importlib.metadata – это библиотека, обеспечивающая доступ к метаданным установленных пакетов. Частично основанная на системе импорта Python, эта библиотека призвана заменить аналогичную функциональность в точки входа API и API метаданных из pkg_resources. Вместе с importlib.resources (начиная с Python 3.7 и новее, с обратным портированием как importlib_resources для более старых версий Python), это позволяет отказаться от использования устаревшего и менее эффективного пакета pkg_resources.

Под «установленным пакетом» обычно понимается сторонний пакет, установленный в каталог site-packages Python с помощью таких инструментов, как pip. В частности, это пакет, имеющий либо обнаруживаемый каталог dist-info или egg-info и метаданные, определённые PEP 566 или более старыми спецификациями. По умолчанию метаданные пакета могут находиться в файловой системе или в zip-архивах на sys.path. Благодаря механизму расширения метаданные могут находиться практически где угодно.

ОбзорOverview

Предположим, вы хотите получить строку версии для пакета, установленного с помощью pip. Начнём с создания виртуального окружения и установки чего-нибудь в него:

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

Вы можете получить строку версии для wheel, выполнив следующее:

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

Также можно получить набор точек входа, разбитых по группам, таких как 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()  
>>> list(eps)  
['console_scripts', 'distutils.commands', 'distutils.setup_keywords', 'egg_info.writers', 'setuptools.installation']
>>> scripts = eps['console_scripts']  
>>> wheel = [ep for ep in scripts if ep.name == 'wheel'][0]  
>>> 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.

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

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

>>> wheel_metadata = metadata('wheel')  

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

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

Версии дистрибутивов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'"]

Дистрибутивы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'

Полный набор доступных метаданных здесь не описан. Подробнее см. PEP 566 .

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

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

Стандартный PathFinder для Python включает хук, который вызывает importlib.metadata.MetadataPathFinder для поиска дистрибутивов, загруженных из типичных путей файловой системы.

Абстрактный класс 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().

Сноски

1

Технически, возвращаемый объект метаданных дистрибутива является email.message.EmailMessage экземпляром, но это деталь реализации и не является частью стабильного API. Следует использовать только методы и синтаксис, подобные словарю, для доступа к содержимому метаданных.