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 работает со сторонними пакетами дистрибутивов, установленными в каталог site-packages Python с помощью таких инструментов, как pip. В частности, он работает с дистрибутивами, для которых можно обнаружить каталоги dist-info или egg-info, а также с метаданными, определенными в спецификациях основных метаданных.
Важно
Эти понятия не обязательно эквивалентны именам пакетов импорта верхнего уровня, которые можно импортировать в коде Python, и не обязательно соответствуют им один к одному. Один пакет дистрибутива может содержать несколько пакетов импорта (и отдельные модули), а один пакет импорта верхнего уровня может соответствовать нескольким пакетам дистрибутивов, если это пакет пространства имен. Чтобы получить соответствие между ними, можно использовать package_distributions().
По умолчанию метаданные дистрибутивов могут находиться в файловой системе или в ZIP-архивах, перечисленных в sys.path. Благодаря механизму расширения метаданные могут находиться практически где угодно.
См. также
- 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 и другим. Каждая группа содержит коллекцию объектов 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']
Также можно получить номер версии дистрибутива, перечислить его составляющие файлы и получить список зависимостей дистрибутива.
-
exception importlib.metadata.PackageNotFoundError -
Подкласс
ModuleNotFoundError, возникающий в нескольких функциях этого модуля при запросе пакета дистрибутива, не установленного в текущей среде Python.
Функциональный API
Этот пакет предоставляет следующие возможности через свой публичный API.
Точки входа
-
importlib.metadata.entry_points(**select_params) -
Возвращает экземпляр
EntryPoints, описывающий точки входа для текущей среды. Все переданные именованные аргументы передаются методуselect()для сравнения со значениями атрибутов отдельных определений точек входа.Примечание: в настоящее время невозможно запрашивать точки входа по их атрибуту
EntryPoint.dist(поскольку разные экземплярыDistributionсейчас не считаются равными, даже если у них одинаковые атрибуты).
-
class importlib.metadata.EntryPoints -
Сведения о коллекции установленных точек входа.
Также предоставляет атрибут
.groups, содержащий все обнаруженные группы точек входа, и атрибут.names, содержащий все обнаруженные имена точек входа.
-
class importlib.metadata.EntryPoint -
Сведения об установленной точке входа.
Каждый экземпляр
EntryPointимеет атрибуты.name,.groupи.value, а также метод.load()для разрешения значения. Кроме того, имеются атрибуты.module,.attrи.extrasдля получения компонентов атрибута.value, а также.distдля получения сведений о пакете дистрибутива, предоставляющем точку входа.
Запросить все точки входа:
>>> 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 не принимала параметров и всегда возвращала словарь точек входа с ключами-группами. Начиная с importlib_metadata 5.0 и Python 3.12, entry_points всегда возвращает объект EntryPoints. Варианты обеспечения совместимости см. в backports.entry_points_selectable.
Метаданные дистрибутива
-
importlib.metadata.metadata(distribution_name) -
Возвращает метаданные указанного пакета дистрибутива в виде экземпляра
PackageMetadata.Вызывает
PackageNotFoundError, если указанный пакет дистрибутива не установлен в текущей среде Python.
-
class importlib.metadata.PackageMetadata -
Конкретная реализация протокола PackageMetadata.
Помимо предоставления определенных протоколом методов и атрибутов, обращение к элементу экземпляра эквивалентно вызову метода
get().
Каждый пакет дистрибутива содержит некоторые метаданные, которые можно извлечь с помощью функции metadata():
>>> wheel_metadata = metadata('wheel')
Ключи возвращенной структуры данных соответствуют ключевым словам метаданных, а значения возвращаются в исходном, неразобранном виде из метаданных дистрибутива:
>>> 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.*'
Здесь не описан полный набор доступных метаданных. Дополнительные сведения см. в спецификации основных метаданных PyPA.
Изменено в версии 3.10: Теперь Description включается в метаданные, представленные в payload. Символы продолжения строки удалены.
Добавлен атрибут json.
Версии дистрибутивов
-
importlib.metadata.version(distribution_name) -
Возвращает версию установленного пакета дистрибутива с указанным именем.
Вызывает
PackageNotFoundError, если указанный пакет дистрибутива не установлен в текущей среде Python.
Функция version() — самый быстрый способ получить номер версии пакета дистрибутива в виде строки:
>>> version('wheel')
'0.32.3'
Файлы дистрибутива
-
importlib.metadata.files(distribution_name) -
Возвращает полный набор файлов, содержащихся в указанном пакете дистрибутива.
Вызывает
PackageNotFoundError, если указанный пакет дистрибутива не установлен в текущей среде Python.Возвращает
None, если дистрибутив найден, но в базе данных установки отсутствуют записи о файлах, связанных с пакетом дистрибутива.
-
class importlib.metadata.PackagePath -
Объект, производный от
pathlib.PurePath, с дополнительными свойствамиdist,sizeиhash, соответствующими метаданным установки пакета дистрибутива для этого файла.
Функция files() принимает имя пакета дистрибутива и возвращает все файлы, установленные этим дистрибутивом. Каждый файл представлен экземпляром PackagePath. Например:
>>> 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 или иным образом предусмотреть обработку этой ситуации.
Зависимости дистрибутива
-
importlib.metadata.requires(distribution_name) -
Возвращает объявленные спецификаторы зависимостей для указанного пакета дистрибутива.
Вызывает
PackageNotFoundError, если указанный пакет дистрибутива не установлен в текущей среде Python.
Чтобы получить полный набор зависимостей пакета дистрибутива, используйте функцию requires():
>>> requires('wheel')
["pytest (>=3.0.0) ; extra == 'test'", "pytest-cov ; extra == 'test'"]
Соответствие пакетов импорта пакетам дистрибутивов
-
importlib.metadata.packages_distributions() -
Возвращает соответствие между именами модулей и пакетов импорта верхнего уровня, обнаруженными через
sys.meta_path, и именами пакетов дистрибутивов (если они есть), предоставляющих соответствующие файлы.Для поддержки пакетов пространства имен (чьи элементы могут предоставляться несколькими пакетами дистрибутивов) каждому имени импорта верхнего уровня соответствует список имен дистрибутивов, а не одно имя.
Вспомогательный метод для определения имен пакетов дистрибутивов (или нескольких имен в случае пакета пространства имен), предоставляющих каждый импортируемый модуль Python или пакет импорта верхнего уровня:
>>> packages_distributions()
{'importlib_metadata': ['importlib-metadata'], 'yaml': ['PyYAML'], 'jaraco': ['jaraco.classes', 'jaraco.functools'], ...}
Некоторые редактируемые установки не предоставляют имена верхнего уровня, поэтому эта функция ненадежна для таких установок.
Добавлено в версии 3.10.
Дистрибутивы
-
importlib.metadata.distribution(distribution_name) -
Возвращает экземпляр
Distribution, описывающий указанный пакет дистрибутива.Вызывает
PackageNotFoundError, если указанный пакет дистрибутива не установлен в текущей среде Python.
-
class importlib.metadata.Distribution -
Сведения об установленном пакете дистрибутива.
Примечание: разные экземпляры
Distributionсейчас не считаются равными, даже если они относятся к одному и тому же установленному дистрибутиву и, соответственно, имеют одинаковые атрибуты.
Хотя описанный выше API уровня модуля используется чаще всего и является наиболее удобным, всю эту информацию можно получить и из класса Distribution. Distribution — это абстрактный объект, представляющий метаданные пакета дистрибутива Python. Получить экземпляр конкретного подкласса Distribution для установленного пакета дистрибутива можно, вызвав функцию distribution():
>>> from importlib.metadata import distribution
>>> dist = distribution('wheel')
>>> type(dist)
<class 'importlib.metadata.PathDistribution'>
Таким образом, альтернативный способ получить номер версии — обратиться к экземпляру 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'
Здесь не описан полный набор доступных метаданных. Дополнительные сведения см. в спецификации основных метаданных PyPA.
Обнаружение дистрибутивов
По умолчанию этот пакет поддерживает обнаружение метаданных пакетов дистрибутивов дистрибутивов в файловой системе и ZIP-файлах. По умолчанию этот механизм поиска метаданных проверяет sys.path, но интерпретирует эти значения немного иначе, чем другие механизмы импорта. В частности:
-
importlib.metadataне учитывает объектыbytesвsys.path. -
importlib.metadataпопутно учитывает объектыpathlib.Pathвsys.path, хотя при импорте такие значения игнорируются.
Расширение алгоритма поиска
Поскольку метаданные пакетов дистрибутивов недоступны при поиске через sys.path или напрямую через загрузчики пакетов, метаданные дистрибутива ищутся с помощью поисковых механизмов системы импорта. Чтобы найти метаданные пакета дистрибутива, importlib.metadata запрашивает список поисковых механизмов meta path в 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()):
"""Return an iterable of all Distribution instances capable of
loading the metadata for packages for the indicated ``context``.
"""
Объект DistributionFinder.Context предоставляет свойства .path и .name, указывающие путь для поиска и имя для сопоставления, а также может предоставлять другой релевантный контекст.
На практике это означает, что для поддержки поиска метаданных пакетов дистрибутивов в местах, отличных от файловой системы, нужно создать подкласс Distribution и реализовать его абстрактные методы. Затем из пользовательского поискового механизма следует возвращать экземпляры этого производного класса Distribution в методе find_distributions().
© 2001–2024 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.12/library/importlib.metadata.html