importlib.metadata.md
1> **Источник:** https://python-all.ru/3.16/library/importlib.metadata.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# `importlib.metadata` – Доступ к метаданным пакета89Добавлено в версии 3.8.1011Изменено в версии 3.10: `importlib.metadata` больше не является предварительным.1213**Исходный код:** [Lib/importlib/metadata/\_\_init\_\_.py](https://python-all.ru/src/main/Lib/importlib/metadata/__init__.py)1415`importlib.metadata` – это библиотека, предоставляющая доступ к метаданным установленного [дистрибутивного пакета](https://python-all.ru/3.16/library/importlib.metadata.html), таким как его точки входа или имена верхнего уровня ([импортируемые пакеты](https://python-all.ru/3.16/library/importlib.metadata.html), модули, если таковые имеются). Построенная отчасти на системе импорта Python, эта библиотека предоставляет API точек входа и метаданных, которые ранее были доступны через ныне удалённый пакет `pkg_resources`. Вместе с [`importlib.resources`](https://python-all.ru/3.16/library/importlib.resources.html#module-importlib.resources) она заменяет `pkg_resources`.1617`importlib.metadata` работает со сторонними *дистрибутивными пакетами*, установленными в каталог `site-packages` Python с помощью таких инструментов, как [pip](https://python-all.ru/3.16/library/importlib.metadata.html). В частности, он работает с дистрибутивами, имеющими обнаруживаемые каталоги `dist-info` или `egg-info`, и метаданными, определёнными [спецификациями основных метаданных](https://python-all.ru/3.16/library/importlib.metadata.html).1819> **Важно**20>21> Они *не* обязательно эквивалентны или соответствуют 1:1 именам *импортируемых пакетов* верхнего уровня, которые можно импортировать в коде Python. Один *дистрибутивный пакет* может содержать несколько *импортируемых пакетов* (и отдельных модулей), а один *импортируемый пакет* верхнего уровня может соответствовать нескольким *дистрибутивным пакетам*, если он является пакетом пространства имён. Вы можете использовать [packages\_distributions()](https://python-all.ru/3.16/library/importlib.metadata.html#package-distributions), чтобы получить отображение между ними.2223По умолчанию метаданные дистрибутива могут находиться в файловой системе или в zip-архивах в [`sys.path`](https://python-all.ru/3.16/library/sys.html#sys.path). Благодаря механизму расширений метаданные могут находиться почти где угодно.2425> **См. также**26>27> **[https://importlib-metadata.readthedocs.io/](https://python-all.ru/3.16/library/importlib.metadata.html)**28>29> Документация для `importlib_metadata`, который предоставляет обратный порт `importlib.metadata`. Она включает [справочник API](https://python-all.ru/3.16/library/importlib.metadata.html) для классов и функций этого модуля, а также [руководство по миграции](https://python-all.ru/3.16/library/importlib.metadata.html) для существующих пользователей `pkg_resources`.3031## Обзор3233Предположим, вы хотите получить строку версии для [дистрибутивного пакета](https://python-all.ru/3.16/library/importlib.metadata.html), установленного с помощью `pip`. Начнём с создания виртуального окружения и установки чего-нибудь в него:3435```console36$ python -m venv example37$ source example/bin/activate38(example) $ python -m pip install wheel39```4041Вы можете получить строку версии для `wheel`, выполнив следующее:4243```pycon44(example) $ python45>>> from importlib.metadata import version46>>> version('wheel')47'0.32.3'48```4950Вы также можете получить коллекцию точек входа, выбираемых по свойствам EntryPoint (обычно 'group' или 'name'), например `console_scripts`, `distutils.commands` и другие. Каждая группа содержит коллекцию объектов [EntryPoint](https://python-all.ru/3.16/library/importlib.metadata.html#entry-points).5152Вы можете получить [метаданные для дистрибутива](https://python-all.ru/3.16/library/importlib.metadata.html#metadata):5354```python55>>> list(metadata('wheel'))56['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']57```5859Вы также можете получить [номер версии дистрибутива](https://python-all.ru/3.16/library/importlib.metadata.html#version), перечислить его [составные файлы](https://python-all.ru/3.16/library/importlib.metadata.html#files) и получить список [зависимостей дистрибутива](https://python-all.ru/3.16/library/importlib.metadata.html#requirements).6061#### `exception importlib.metadata.PackageNotFoundError`6263Подкласс [`ModuleNotFoundError`](https://python-all.ru/3.16/library/exceptions.html#ModuleNotFoundError), вызываемый несколькими функциями в этом модуле при запросе дистрибутивного пакета, который не установлен в текущем окружении Python.6465#### `exception importlib.metadata.MetadataNotFound`6667Подкласс [`FileNotFoundError`](https://python-all.ru/3.16/library/exceptions.html#FileNotFoundError), возбуждаемый при попытке загрузить метаданные из папки дистрибутива, которая пуста или не содержит файла метаданных.6869## Функциональное API7071Этот пакет предоставляет следующие функции через свой публичный API.7273### Точки входа7475#### `importlib.metadata.entry_points(**select_params)`7677Возвращает экземпляр [`EntryPoints`](https://python-all.ru/3.16/library/importlib.metadata.html#importlib.metadata.EntryPoints), описывающий точки входа для текущего окружения. Любые заданные именованные параметры передаются методу `select()` для сравнения с атрибутами отдельных определений точек входа.7879Примечание: для запроса точек входа на основе атрибута `EntryPoint.dist` следует использовать [`Distribution.entry_points()`](https://python-all.ru/3.16/library/importlib.metadata.html#importlib.metadata.Distribution.entry_points) (поскольку разные экземпляры [`Distribution`](https://python-all.ru/3.16/library/importlib.metadata.html#importlib.metadata.Distribution) в настоящее время не считаются равными, даже если имеют одинаковые атрибуты).8081#### `class importlib.metadata.EntryPoints`8283Подробности о коллекции установленных точек входа.8485Также предоставляет атрибут `.groups`, который сообщает все идентифицированные группы точек входа, и атрибут `.names`, который сообщает все идентифицированные имена точек входа.8687#### `class importlib.metadata.EntryPoint`8889Подробности об установленной точке входа.9091Каждый экземпляр `EntryPoint` имеет атрибуты `.name`, `.group` и `.value` и метод `.load()` для разрешения значения. Также есть атрибуты `.module`, `.attr` и `.extras` для получения компонентов атрибута `.value` и `.dist` для получения информации о дистрибутивном пакете, который предоставляет точку входа.9293Запрос всех точек входа:9495```python96>>> eps = entry_points()97```9899Функция `entry_points()` возвращает объект `EntryPoints`, коллекцию всех объектов `EntryPoint` с атрибутами `names` и `groups` для удобства:100101```python102>>> sorted(eps.groups)103['console_scripts', 'distutils.commands', 'distutils.setup_keywords', 'egg_info.writers', 'setuptools.installation']104```105106`EntryPoints` имеет метод `select()` для выбора точек входа, соответствующих определённым свойствам. Выберите точки входа в группе `console_scripts`:107108```python109>>> scripts = eps.select(group='console_scripts')110```111112Аналогично, поскольку `entry_points()` передаёт ключевые аргументы в select:113114```python115>>> scripts = entry_points(group='console_scripts')116```117118Выбрать конкретный скрипт с именем «wheel» (находится в проекте wheel):119120```python121>>> 'wheel' in scripts.names122True123>>> wheel = scripts['wheel']124```125126Аналогично, запросить эту точку входа во время выбора:127128```python129>>> (wheel,) = entry_points(group='console_scripts', name='wheel')130>>> (wheel,) = entry_points().select(group='console_scripts', name='wheel')131```132133Проверить разрешённую точку входа:134135```python136>>> wheel137EntryPoint(name='wheel', value='wheel.cli:main', group='console_scripts')138>>> wheel.module139'wheel.cli'140>>> wheel.attr141'main'142>>> wheel.extras143[]144>>> main = wheel.load()145>>> main146<function main at 0x103528488>147```148149Значения `group` и `name` задаются автором пакета произвольно, и обычно клиенту требуется разрешить все точки входа для определённой группы. Дополнительные сведения о точках входа, их определении и использовании см. в [документации setuptools](https://python-all.ru/3.16/library/importlib.metadata.html).150151Изменено в версии 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](https://python-all.ru/3.16/library/importlib.metadata.html).152153Изменено в версии 3.13: Объекты `EntryPoint` больше не предоставляют кортежеподобный интерфейс ([`__getitem__()`](https://python-all.ru/3.16/reference/datamodel.html#object.__getitem__)).154155### Метаданные дистрибутива156157#### `importlib.metadata.metadata(distribution_name)`158159Возвращает метаданные дистрибутива для указанного пакета дистрибутива в виде экземпляра [`PackageMetadata`](https://python-all.ru/3.16/library/importlib.metadata.html#importlib.metadata.PackageMetadata).160161Вызывает [`PackageNotFoundError`](https://python-all.ru/3.16/library/importlib.metadata.html#importlib.metadata.PackageNotFoundError), если именованный дистрибутивный пакет не установлен в текущем окружении Python.162163Возбуждает [`MetadataNotFound`](https://python-all.ru/3.16/library/importlib.metadata.html#importlib.metadata.MetadataNotFound), если дистрибутивный пакет присутствует, но файл METADATA отсутствует.164165#### `class importlib.metadata.PackageMetadata`166167Конкретная реализация протокола [PackageMetadata](https://python-all.ru/3.16/library/importlib.metadata.html).168169В дополнение к предоставлению определённых методов и атрибутов протокола, обращение к экземпляру по индексу эквивалентно вызову метода `get()`.170171Каждый [пакет дистрибутива](https://python-all.ru/3.16/library/importlib.metadata.html) содержит метаданные, которые можно извлечь с помощью функции `metadata()`:172173```python174>>> wheel_metadata = metadata('wheel')175```176177Ключи возвращаемой структуры данных соответствуют ключевым словам метаданных, а значения возвращаются без разбора из метаданных дистрибутива:178179```python180>>> wheel_metadata['Requires-Python']181'>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*'182```183184[`PackageMetadata`](https://python-all.ru/3.16/library/importlib.metadata.html#importlib.metadata.PackageMetadata) также предоставляет атрибут `json`, который возвращает все метаданные в JSON-совместимой форме согласно [**PEP 566**](https://python-all.ru/3.16/library/importlib.metadata.html):185186```python187>>> wheel_metadata.json['requires_python']188'>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*'189```190191Полный набор доступных метаданных здесь не описывается. За дополнительными сведениями обратитесь к [спецификации основных метаданных PyPA](https://python-all.ru/3.16/library/importlib.metadata.html).192193Изменено в версии 3.15: Ранее, если файл METADATA отсутствовал в дистрибутиве, возвращался пустой `PackageMetadata`, неотличимый от пустого файла METADATA. Теперь отсутствующий файл METADATA вызывает исключение `MetadataNotFound`.194195Изменено в версии 3.10: Теперь `Description` включается в метаданные при представлении через полезную нагрузку (payload). Символы продолжения строки удалены.196197Добавлен атрибут `json`.198199### Версии дистрибутивов200201#### `importlib.metadata.version(distribution_name)`202203Возвращает [версию](https://python-all.ru/3.16/library/importlib.metadata.html) установленного пакета дистрибутива для указанного пакета дистрибутива.204205Вызывает [`PackageNotFoundError`](https://python-all.ru/3.16/library/importlib.metadata.html#importlib.metadata.PackageNotFoundError), если именованный дистрибутивный пакет не установлен в текущем окружении Python.206207Функция `version()` – это самый быстрый способ получить номер [версии пакета дистрибутива](https://python-all.ru/3.16/library/importlib.metadata.html) в виде строки:208209```python210>>> version('wheel')211'0.32.3'212```213214### Файлы дистрибутива215216#### `importlib.metadata.files(distribution_name)`217218Возвращает полный набор файлов, содержащихся в указанном дистрибутивном пакете, в виде экземпляров [`PackagePath`](https://python-all.ru/3.16/library/importlib.metadata.html#importlib.metadata.PackagePath).219220Вызывает [`PackageNotFoundError`](https://python-all.ru/3.16/library/importlib.metadata.html#importlib.metadata.PackageNotFoundError), если именованный дистрибутивный пакет не установлен в текущем окружении Python.221222Возвращает [`None`](https://python-all.ru/3.16/library/constants.html#None), если дистрибутив найден, но в базе данных установки отсутствуют записи о файлах, связанных с пакетом дистрибутива.223224#### `class importlib.metadata.PackagePath`225226Объект, производный от [`pathlib.PurePath`](https://python-all.ru/3.16/library/pathlib.html#pathlib.PurePath), с дополнительными свойствами `dist`, `size` и `hash`, соответствующими метаданным установки дистрибутивного пакета для этого файла, а также:227228#### `locate()`229230Если возможно, возвращает конкретный [`SimplePath`](https://python-all.ru/3.16/library/importlib.metadata.html#importlib.metadata.SimplePath), позволяющий получить доступ к данным, иначе возбуждает [`NotImplementedError`](https://python-all.ru/3.16/library/exceptions.html#NotImplementedError).231232#### `class importlib.metadata.SimplePath`233234Протокол, представляющий минимальное подмножество [`pathlib.Path`](https://python-all.ru/3.16/library/pathlib.html#pathlib.Path), которое позволяет проверить, `exists()` ли он, выполнять обход с помощью `joinpath()` и `parent`, а также получать данные с помощью `read_text()` и `read_bytes()`.235236Функция `files()` принимает имя [дистрибутивного пакета](https://python-all.ru/3.16/library/importlib.metadata.html) и возвращает все файлы, установленные этим дистрибутивом. Например:237238```python239>>> util = [p for p in files('wheel') if 'util.py' in str(p)][0]240>>> util241PackagePath('wheel/util.py')242>>> util.size243859244>>> util.dist245<importlib.metadata._hooks.PathDistribution object at 0x101e0cef0>246>>> util.hash247<FileHash mode: sha256 value: bYkw5oMccfazVCoYQwKkkemoVyMAFoR34mmKBx8R1NI>248```249250Получив файл, можно также прочитать его содержимое:251252```python253>>> print(util.read_text())254import base64255import sys256...257def as_bytes(s):258 if isinstance(s, text_type):259 return s.encode('utf-8')260 return s261```262263Также можно использовать метод `locate()`, чтобы получить абсолютный путь к файлу:264265```python266>>> util.locate()267PosixPath('/home/gustav/example/lib/site-packages/wheel/util.py')268```269270Если файл метаданных со списком файлов (`RECORD` или `SOURCES.txt`) отсутствует, `files()` вернёт [`None`](https://python-all.ru/3.16/library/constants.html#None). Вызывающий код может обернуть вызовы `files()` в [always\_iterable](https://python-all.ru/3.16/library/importlib.metadata.html) или иным образом защититься от такой ситуации, если неизвестно, что целевой дистрибутив содержит эти метаданные.271272### Зависимости дистрибутивов273274#### `importlib.metadata.requires(distribution_name)`275276Возвращает объявленные спецификаторы зависимостей для именованного дистрибутивного пакета.277278Вызывает [`PackageNotFoundError`](https://python-all.ru/3.16/library/importlib.metadata.html#importlib.metadata.PackageNotFoundError), если именованный дистрибутивный пакет не установлен в текущем окружении Python.279280Чтобы получить полный набор зависимостей для [дистрибутивного пакета](https://python-all.ru/3.16/library/importlib.metadata.html), используйте функцию `requires()`:281282```python283>>> requires('wheel')284["pytest (>=3.0.0) ; extra == 'test'", "pytest-cov ; extra == 'test'"]285```286287### Отображение импортов на дистрибутивы288289#### `importlib.metadata.packages_distributions()`290291Возвращает отображение имён модулей верхнего уровня и импортируемых пакетов, найденных с помощью [`sys.meta_path`](https://python-all.ru/3.16/library/sys.html#sys.meta_path), на имена дистрибутивных пакетов (если таковые есть), предоставляющих соответствующие файлы.292293Для поддержки пакетов пространств имён (которые могут содержать компоненты от нескольких дистрибутивов) каждое имя импорта верхнего уровня сопоставляется со списком имён дистрибутивов, а не напрямую с одним именем.294295Удобный метод для разрешения имени [дистрибутивного пакета](https://python-all.ru/3.16/library/importlib.metadata.html) (или имён, в случае пакета пространства имён), который предоставляет каждый импортируемый модуль или [импортируемый пакет](https://python-all.ru/3.16/library/importlib.metadata.html) верхнего уровня:296297```python298>>> packages_distributions()299{'importlib_metadata': ['importlib-metadata'], 'yaml': ['PyYAML'], 'jaraco': ['jaraco.classes', 'jaraco.functools'], ...}300```301302Некоторые редактируемые установки [не предоставляют имён верхнего уровня](https://python-all.ru/3.16/library/importlib.metadata.html), поэтому эта функция не является надёжной для таких установок.303304Добавлено в версии 3.10.305306## Дистрибутивы307308Хотя описанный выше API уровня модуля является наиболее распространённым и удобным, вся эта информация доступна из класса [`Distribution`](https://python-all.ru/3.16/library/importlib.metadata.html#importlib.metadata.Distribution). `Distribution` – это абстрактный объект, представляющий метаданные для [дистрибутивного пакета](https://python-all.ru/3.16/library/importlib.metadata.html) Python. Получить конкретный экземпляр подкласса `Distribution` для установленного дистрибутивного пакета можно, вызвав функцию [`distribution()`](https://python-all.ru/3.16/library/importlib.metadata.html#importlib.metadata.distribution):309310```python311>>> from importlib.metadata import distribution312>>> dist = distribution('wheel')313>>> type(dist)314<class 'importlib.metadata.PathDistribution'>315```316317#### `importlib.metadata.distribution(distribution_name)`318319Возвращает экземпляр [`Distribution`](https://python-all.ru/3.16/library/importlib.metadata.html#importlib.metadata.Distribution), описывающий именованный дистрибутивный пакет.320321Вызывает [`PackageNotFoundError`](https://python-all.ru/3.16/library/importlib.metadata.html#importlib.metadata.PackageNotFoundError), если именованный дистрибутивный пакет не установлен в текущем окружении Python.322323Таким образом, альтернативный способ получить, например, номер версии – через атрибут [`Distribution.version`](https://python-all.ru/3.16/library/importlib.metadata.html#importlib.metadata.Distribution.version):324325```python326>>> dist.version327'0.32.3'328```329330То же самое относится к [`entry_points()`](https://python-all.ru/3.16/library/importlib.metadata.html#importlib.metadata.entry_points) и [`files()`](https://python-all.ru/3.16/library/importlib.metadata.html#importlib.metadata.files).331332#### `class importlib.metadata.Distribution`333334Сведения об установленном дистрибутивном пакете.335336Примечание: разные экземпляры `Distribution` в настоящее время не считаются равными, даже если они относятся к одному и тому же установленному дистрибутиву и, соответственно, имеют одинаковые атрибуты.337338#### `static at(path)`339340#### `classmethod from_name(name)`341342Возвращает экземпляр `Distribution` по указанному пути или с указанным именем.343344#### `classmethod discover(*, context=None, **kwargs)`345346Возвращает итерируемый объект с экземплярами `Distribution` для всех пакетов (см. [distribution-discovery](https://python-all.ru/3.16/library/importlib.metadata.html#distribution-discovery)).347348Необязательный аргумент *context* – это экземпляр [`DistributionFinder.Context`](https://python-all.ru/3.16/library/importlib.metadata.html#importlib.metadata.DistributionFinder.Context), используемый для изменения поиска дистрибутивов. Альтернативно, *kwargs* может содержать именованные аргументы для создания нового `DistributionFinder.Context`.349350#### `metadata: PackageMetadata`351352Вызывает [`MetadataNotFound`](https://python-all.ru/3.16/library/importlib.metadata.html#importlib.metadata.MetadataNotFound), если файл METADATA отсутствует в дистрибутиве.353354На экземплярах `Distribution` доступны разнообразные дополнительные метаданные в виде экземпляра [`PackageMetadata`](https://python-all.ru/3.16/library/importlib.metadata.html#importlib.metadata.PackageMetadata):355356```python357>>> dist.metadata['Requires-Python']358'>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*'359>>> dist.metadata['License']360'MIT'361```362363Полный набор доступных метаданных здесь не описывается. За дополнительными сведениями обратитесь к [спецификации основных метаданных PyPA](https://python-all.ru/3.16/library/importlib.metadata.html).364365#### `name: str`366367#### `requires: list[str]`368369#### `version: str`370371Некоторые поля метаданных также доступны как сокращённые свойства.372373Добавлено в версии 3.10: Было добавлено сокращённое свойство `name`.374375#### `origin`376377Для редактируемых пакетов свойство `origin` может содержать метаданные [**PEP 610**](https://python-all.ru/3.16/library/importlib.metadata.html) (для нередактируемых пакетов `origin` равно [`None`](https://python-all.ru/3.16/library/constants.html#None)):378379```python380>>> dist.origin.url381'file:///path/to/wheel-0.32.3.editable-py3-none-any.whl'382```383384Объект `origin` соответствует [структуре данных прямого URL](https://python-all.ru/3.16/library/importlib.metadata.html).385386Добавлено в версии 3.13.387388#### `entry_points: EntryPoints`389390Точки входа, предоставляемые этим пакетом дистрибутива.391392#### `files: list[PackagePath] | None`393394Все файлы, содержащиеся в этом пакете дистрибутива. Как и [`files()`](https://python-all.ru/3.16/library/importlib.metadata.html#importlib.metadata.files), возвращает [`None`](https://python-all.ru/3.16/library/constants.html#None), если записи отсутствуют.395396Следующие два абстрактных метода необходимо реализовать при [реализации пользовательских провайдеров](https://python-all.ru/3.16/library/importlib.metadata.html#implementing-custom-providers):397398#### `locate_file(path)`399400Как и `PackagePath.locate()`, возвращает [`SimplePath`](https://python-all.ru/3.16/library/importlib.metadata.html#importlib.metadata.SimplePath) для указанного пути. Принимает [`os.PathLike`](https://python-all.ru/3.16/library/os.html#os.PathLike) или [`str`](https://python-all.ru/3.16/library/stdtypes.html#str).401402#### `read_text(filename)`403404Сокращённая запись для `distribution.locate_file(filename).read_text()`.405406## Обнаружение дистрибутивов407408По умолчанию этот пакет предоставляет встроенную поддержку поиска метаданных для [дистрибутивных пакетов](https://python-all.ru/3.16/library/importlib.metadata.html) файловой системы и zip-файлов. Поиск метаданных по умолчанию использует `sys.path`, но трактует эти значения немного иначе, чем другие механизмы импорта. В частности:409410- `importlib.metadata` не учитывает объекты [`bytes`](https://python-all.ru/3.16/library/stdtypes.html#bytes) из `sys.path`.411- `importlib.metadata` случайно будет учитывать объекты [`pathlib.Path`](https://python-all.ru/3.16/library/pathlib.html#pathlib.Path) из `sys.path`, хотя такие значения игнорируются при импорте.412413#### `class importlib.metadata.DistributionFinder`414415Подкласс [`MetaPathFinder`](https://python-all.ru/3.16/library/importlib.html#importlib.abc.MetaPathFinder), способный обнаруживать установленные дистрибутивы.416417Пользовательские провайдеры должны реализовать этот интерфейс для предоставления метаданных.418419> #### `class Context(**kwargs)`420>421> `Context` даёт пользовательскому провайдеру возможность запрашивать дополнительные сведения у вызывающих сторон функций поиска дистрибутивов, таких как [`distributions()`](https://python-all.ru/3.16/library/importlib.metadata.html#importlib.metadata.distributions) или [`Distribution.discover()`](https://python-all.ru/3.16/library/importlib.metadata.html#importlib.metadata.Distribution.discover), помимо `.name` и `.path` при поиске дистрибутивов.422>423> Например, провайдер может предоставлять наборы пакетов в «публичном» или «частном» `realm`. Вызывающая сторона функций поиска дистрибутивов может захотеть запросить только дистрибутивы в определённой области и может вызвать `distributions(realm="private")`, чтобы указать пользовательскому провайдеру включать только дистрибутивы из этой области.424>425> Каждый `DistributionFinder` должен принимать любые параметры и по возможности стараться соблюдать канонические параметры, определённые ниже.426>427> См. раздел [Реализация пользовательских провайдеров](https://python-all.ru/3.16/library/importlib.metadata.html#implementing-custom-providers) для получения дополнительных сведений.428>429> #### `name`430>431> Конкретное имя, которому должен соответствовать искатель дистрибутивов.432>433> `.name` со значением `None` соответствует всем дистрибутивам.434>435> #### `path`436>437> Свойство, предоставляющее последовательность путей к каталогам, которые должен просматривать поисковик дистрибутивов.438>439> Обычно относится к путям установленных пакетов Python, например, к каталогам site-packages, и по умолчанию равно [`sys.path`](https://python-all.ru/3.16/library/sys.html#sys.path).440441#### `importlib.metadata.distributions(**kwargs)`442443Возвращает итерируемый объект с экземплярами [`Distribution`](https://python-all.ru/3.16/library/importlib.metadata.html#importlib.metadata.Distribution) для всех пакетов.444445Аргумент *kwargs* может содержать именованный аргумент `context`, экземпляр [`DistributionFinder.Context`](https://python-all.ru/3.16/library/importlib.metadata.html#importlib.metadata.DistributionFinder.Context) или ключевые аргументы для создания нового `DistributionFinder.Context`. `DistributionFinder.Context` используется для изменения поиска дистрибутивов.446447## Реализация пользовательских провайдеров448449`importlib.metadata` охватывает два API: один для *потребителей*, а другой для *провайдеров*. Большинство пользователей являются потребителями, потребляя метаданные, предоставляемые пакетами. Однако есть и другие сценарии, в которых пользователи хотят предоставлять метаданные через какой-то другой механизм, например, вместе с пользовательским импортёром. Такой сценарий требует *пользовательского провайдера*.450451Поскольку метаданные [дистрибутивного пакета](https://python-all.ru/3.16/library/importlib.metadata.html) недоступны напрямую через поиск [`sys.path`](https://python-all.ru/3.16/library/sys.html#sys.path) или загрузчики пакетов, метаданные для дистрибутива находятся через [искатели](https://python-all.ru/3.16/reference/import.html#finders-and-loaders) системы импорта. Чтобы найти метаданные дистрибутивного пакета, `importlib.metadata` опрашивает список [искателей путей метаданных](https://python-all.ru/3.16/glossary.html#term-meta-path-finder) из [`sys.meta_path`](https://python-all.ru/3.16/library/sys.html#sys.meta_path).452453Реализация включает хуки, встроенные в `PathFinder`, которые предоставляют метаданные для дистрибутивных пакетов, найденных в файловой системе.454455Абстрактный класс [`importlib.abc.MetaPathFinder`](https://python-all.ru/3.16/library/importlib.html#importlib.abc.MetaPathFinder) определяет интерфейс, ожидаемый от искателей системой импорта Python. `importlib.metadata` расширяет этот протокол, проверяя наличие опционального вызываемого объекта `find_distributions` у искателей из [`sys.meta_path`](https://python-all.ru/3.16/library/sys.html#sys.meta_path), и предоставляет этот расширенный интерфейс как абстрактный базовый класс [`DistributionFinder`](https://python-all.ru/3.16/library/importlib.metadata.html#importlib.metadata.DistributionFinder), который определяет следующий абстрактный метод:456457```python458@abc.abstractmethod459def find_distributions(context=DistributionFinder.Context()) -> Iterable[Distribution]:460 """Возвращает итерируемый объект со всеми экземплярами Distribution, способными461 загружать метаданные для пакетов для указанного ``context``.462 """463```464465Объект [`DistributionFinder.Context`](https://python-all.ru/3.16/library/importlib.metadata.html#importlib.metadata.DistributionFinder.Context) предоставляет свойства [`path`](https://python-all.ru/3.16/library/importlib.metadata.html#importlib.metadata.DistributionFinder.Context.path) и [`name`](https://python-all.ru/3.16/library/importlib.metadata.html#importlib.metadata.DistributionFinder.Context.name), указывающие путь для поиска и имя для сопоставления, а также может предоставлять другой соответствующий контекст, запрашиваемый потребителем.466467На практике, чтобы поддерживать поиск метаданных дистрибутивных пакетов в местах, отличных от файловой системы, создайте подкласс `Distribution` и реализуйте абстрактные методы. Затем из пользовательского искателя возвращайте экземпляры этого производного `Distribution` в методе `find_distributions()`.468469### Пример470471Представьте пользовательский искатель, который загружает модули Python из базы данных:472473```python474class DatabaseImporter(importlib.abc.MetaPathFinder):475 def __init__(self, db):476 self.db = db477478 def find_spec(self, fullname, target=None) -> ModuleSpec:479 return self.db.spec_from_name(fullname)480481sys.meta_path.append(DatabaseImporter(connect_db(...)))482```483484Этот импортёр предположительно предоставляет импортируемые модули из базы данных, но не предоставляет метаданные или точки входа. Чтобы этот пользовательский импортёр мог предоставлять метаданные, ему также необходимо реализовать [`DistributionFinder`](https://python-all.ru/3.16/library/importlib.metadata.html#importlib.metadata.DistributionFinder):485486```python487from importlib.metadata import DistributionFinder488489class DatabaseImporter(DistributionFinder):490 ...491492 def find_distributions(self, context=DistributionFinder.Context()):493 query = dict(name=context.name) if context.name else {}494 for dist_record in self.db.query_distributions(query):495 yield DatabaseDistribution(dist_record)496```497498Таким образом, `query_distributions` будет возвращать записи для каждого дистрибутива, обслуживаемого базой данных, которые соответствуют запросу. Например, если `requests-1.0` находится в базе данных, `find_distributions` вернёт `DatabaseDistribution` для `Context(name='requests')` или `Context(name=None)`.499500Ради простоты этот пример игнорирует `context.path`. Атрибут `path` по умолчанию равен `sys.path` и представляет собой набор путей импорта, которые следует учитывать при поиске. `DatabaseImporter` может потенциально функционировать без учёта пути поиска. Если предположить, что импортёр не выполняет разделение, «путь» не будет иметь значения. Чтобы проиллюстрировать назначение `path`, пример должен показать более сложный `DatabaseImporter`, поведение которого зависит от `sys.path`/`PYTHONPATH`. В таком случае `find_distributions` должен соблюдать `context.path` и возвращать только `Distribution`, относящиеся к этому пути.501502Тогда `DatabaseDistribution` будет выглядеть примерно так:503504```python505class DatabaseDistribution(importlib.metadata.Distribution):506 def __init__(self, record):507 self.record = record508509 def read_text(self, filename):510 """511 Читает файл, например "METADATA", для текущего Distribution.512 """513 if filename == "METADATA":514 return f"""Name: {self.record.name}515Version: {self.record.version}516"""517 if filename == "entry_points.txt":518 return "\n".join(519 f"""[{ep.group}]\n{ep.name}={ep.value}"""520 for ep in self.record.entry_points)521522 def locate_file(self, path):523 raise RuntimeError("This distribution has no file system")524```525526Эта базовая реализация должна предоставлять метаданные и точки входа для пакетов, обслуживаемых `DatabaseImporter`, при условии, что `record` предоставляет подходящие атрибуты `.name`, `.version` и `.entry_points`.527528`DatabaseDistribution` может также предоставлять другие файлы метаданных, например `RECORD` (требуется для `Distribution.files`), или переопределять реализацию `Distribution.files`. За дополнительными идеями обращайтесь к исходному коду.529