Spec-Zone.ru › Python 3.12

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

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API