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 работает с сторонними пакетами дистрибутива, установленных в каталог Python's site-packages с помощью инструментов, таких как pip. В частности, он работает с дистрибутивами с открываемыми dist-info или egg-info каталогами и метаданными, определёнными спецификациями основных метаданных.
Важно
Они не обязательно эквивалентны или соответствуют 1:1 именам пакетов импорта верхнего уровня, которые можно импортировать внутри кода Python. Один пакет дистрибутива может содержать несколько пакетов импорта (и отдельные модули), а один пакет импорта верхнего уровня может соответствовать нескольким пакетам дистрибутива, если это пакет именованного пространства. Вы можете использовать packages_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'
Вы также можете получить коллекцию точек входа, выбираемых по свойствам точки входа (обычно «группа» или «имя»), таких как 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() передаёт ключевые аргументы для выбора:
>>> 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 для получения дополнительной информации о точках входа, их определении и использовании.
Изменено в версии 3.12: Точки входа «selectable» были добавлены в importlib_metadata 3.6 и Python 3.10. До этих изменений entry_points не принимала параметров и всегда возвращала словарь точек входа, ключами которого являлись группы. С importlib_metadata 5.0 и Python 3.12 entry_points всегда возвращает объект EntryPoints. См. backports.entry_points_selectable для вариантов совместимости.
Изменено в версии 3.13: Объекты EntryPoint больше не предоставляют интерфейс типа кортежа (__getitem__()).
Метаданные дистрибутива
-
importlib.metadata.metadata(distribution_name) -
Возвращает метаданные дистрибутива, соответствующие имени пакета дистрибутива, в виде экземпляра
PackageMetadata.Возбуждает исключение
PackageNotFoundError, если указанный пакет дистрибутива не установлен в текущей среде Python.
-
class importlib.metadata.PackageMetadata -
Конкретная реализация протокола PackageMetadata protocol.
Помимо предоставления определённых методов и атрибутов протокола, обращение к экземпляру по индексу эквивалентно вызову метода
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 Core metadata specification для получения дополнительной информации.
Изменено в версии 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'
Для редактируемых пакетов свойство origin может представлять метаданные PEP 610:
>>> dist.origin.url 'file:///path/to/wheel-0.32.3.editable-py3-none-any.whl'
Полный набор доступных метаданных здесь не описан. Обратитесь к спецификации основных метаданных PyPA спецификация основных метаданных для получения дополнительной информации.
Добавлена в версии 3.13: Свойство .origin было добавлено.
Обнаружение пакетов дистрибуции
По умолчанию этот пакет предоставляет встроенную поддержку обнаружения метаданных для пакетов дистрибуции файловой системы и zip-файлов пакетов дистрибуции. Этот поиск метаданных по умолчанию ищет в sys.path, но несколько отличается в том, как он интерпретирует эти значения, чем другие механизмы импорта. В частности:
-
importlib.metadataне учитывает объектыbytesвsys.path. -
importlib.metadataслучайно учтёт объектыpathlib.Pathвsys.path, даже если такие значения будут игнорироваться для импорта.
Расширение алгоритма поиска
Поскольку метаданные пакета дистрибуции недоступны через поиск по 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()):
"""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().
Пример
Представьте себе, например, пользовательского поисковика, который загружает модули Python из базы данных:
class DatabaseImporter(importlib.abc.MetaPathFinder):
def __init__(self, db):
self.db = db
def find_spec(self, fullname, target=None) -> ModuleSpec:
return self.db.spec_from_name(fullname)
sys.meta_path.append(DatabaseImporter(connect_db(...)))
Этот импортёр теперь, предположительно, предоставляет импортируемые модули из базы данных, но не предоставляет метаданные или точки входа. Для того, чтобы этот пользовательский импортёр предоставлял метаданные, ему также необходимо реализовать DistributionFinder:
from importlib.metadata import DistributionFinder
class DatabaseImporter(DistributionFinder):
...
def find_distributions(self, context=DistributionFinder.Context()):
query = dict(name=context.name) if context.name else {}
for dist_record in self.db.query_distributions(query):
yield DatabaseDistribution(dist_record)
Таким образом, query_distributions вернул бы записи для каждого пакета дистрибуции, предоставляемого базой данных, соответствующие запросу. Например, если requests-1.0 находится в базе данных, find_distributions вернул бы DatabaseDistribution для Context(name='requests') или Context(name=None).
Для простоты этот пример игнорирует context.path. Атрибут path по умолчанию равен sys.path и представляет собой набор путей импорта, которые должны учитываться при поиске. DatabaseImporter потенциально мог бы функционировать без учета пути поиска. Если импортёр не производит разбиение, «путь» не имеет значения. Чтобы проиллюстрировать назначение path, пример должен проиллюстрировать более сложный DatabaseImporter, поведение которого зависит от sys.path/PYTHONPATH. В этом случае find_distributions должен учитывать context.path и возвращать только Distribution, относящиеся к этому пути.
DatabaseDistribution, тогда, выглядело бы примерно так:
class DatabaseDistribution(importlib.metadata.Distribution):
def __init__(self, record):
self.record = record
def read_text(self, filename):
"""
Read a file like "METADATA" for the current distribution.
"""
if filename == "METADATA":
return f"""Name: {self.record.name}
Version: {self.record.version}
"""
if filename == "entry_points.txt":
return "\n".join(
f"""[{ep.group}]\n{ep.name}={ep.value}"""
for ep in self.record.entry_points)
def locate_file(self, path):
raise RuntimeError("This distribution has no file system")
Эта основная реализация должна предоставлять метаданные и точки входа для пакетов, обслуживаемых DatabaseImporter, при условии, что record предоставляет подходящие атрибуты .name, .version и .entry_points.
DatabaseDistribution также может предоставлять другие файлы метаданных, такие как RECORD (необходимые для Distribution.files) или переопределять реализацию Distribution.files. Смотрите исходный код для вдохновения.
© 2001–2024 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.13/library/importlib.metadata.html