> **Источник:** https://python-all.ru/3/library/importlib.metadata.html
>
> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.

---

# `importlib.metadata` – Доступ к метаданным пакета

Добавлено в версии 3.8.

Изменено в версии 3.10: `importlib.metadata` больше не является предварительным.

**Исходный код:** [Lib/importlib/metadata/\_\_init\_\_.py](https://python-all.ru/src/3.14/Lib/importlib/metadata/__init__.py)

`importlib.metadata` – это библиотека, предоставляющая доступ к метаданным установленного [дистрибутивного пакета](https://python-all.ru/3/library/importlib.metadata.html), таким как его точки входа или имена верхнего уровня ([импортируемые пакеты](https://python-all.ru/3/library/importlib.metadata.html), модули, если таковые имеются). Построенная отчасти на системе импорта Python, эта библиотека предоставляет API точек входа и метаданных, которые ранее были доступны через ныне удалённый пакет `pkg_resources`. Вместе с [`importlib.resources`](https://python-all.ru/3/library/importlib.resources.html#module-importlib.resources) она заменяет `pkg_resources`.

`importlib.metadata` работает со сторонними *дистрибутивными пакетами*, установленными в каталог `site-packages` Python с помощью таких инструментов, как [pip](https://python-all.ru/3/library/importlib.metadata.html). В частности, он работает с дистрибутивами, имеющими обнаруживаемые каталоги `dist-info` или `egg-info`, и метаданными, определёнными [спецификациями основных метаданных](https://python-all.ru/3/library/importlib.metadata.html).

> **Важно**
>
> Они *не* обязательно эквивалентны или соответствуют 1:1 именам *импортируемых пакетов* верхнего уровня, которые можно импортировать в коде Python. Один *дистрибутивный пакет* может содержать несколько *импортируемых пакетов* (и отдельных модулей), а один *импортируемый пакет* верхнего уровня может соответствовать нескольким *дистрибутивным пакетам*, если он является пакетом пространства имён. Вы можете использовать [packages\_distributions()](https://python-all.ru/3/library/importlib.metadata.html#package-distributions), чтобы получить отображение между ними.

По умолчанию метаданные дистрибутива могут находиться в файловой системе или в zip-архивах в [`sys.path`](https://python-all.ru/3/library/sys.html#sys.path). Благодаря механизму расширений метаданные могут находиться почти где угодно.

> **См. также**
>
> **[https://importlib-metadata.readthedocs.io/](https://python-all.ru/3/library/importlib.metadata.html)**
>
> Документация для `importlib_metadata`, который предоставляет обратный порт `importlib.metadata`. Она включает [справочник API](https://python-all.ru/3/library/importlib.metadata.html) для классов и функций этого модуля, а также [руководство по миграции](https://python-all.ru/3/library/importlib.metadata.html) для существующих пользователей `pkg_resources`.

## Обзор

Предположим, вы хотите получить строку версии для [дистрибутивного пакета](https://python-all.ru/3/library/importlib.metadata.html), установленного с помощью `pip`. Начнём с создания виртуального окружения и установки чего-нибудь в него:

```console
$ python -m venv example
$ source example/bin/activate
(example) $ python -m pip install wheel
```

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

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

Вы также можете получить коллекцию точек входа, выбираемых по свойствам EntryPoint (обычно 'group' или 'name'), например `console_scripts`, `distutils.commands` и другие. Каждая группа содержит коллекцию объектов [EntryPoint](https://python-all.ru/3/library/importlib.metadata.html#entry-points).

Вы можете получить [метаданные для дистрибутива](https://python-all.ru/3/library/importlib.metadata.html#metadata):

```python
>>> 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']
```

Вы также можете получить [номер версии дистрибутива](https://python-all.ru/3/library/importlib.metadata.html#version), перечислить его [составные файлы](https://python-all.ru/3/library/importlib.metadata.html#files) и получить список [зависимостей дистрибутива](https://python-all.ru/3/library/importlib.metadata.html#requirements).

#### `exception importlib.metadata.PackageNotFoundError`

Подкласс [`ModuleNotFoundError`](https://python-all.ru/3/library/exceptions.html#ModuleNotFoundError), вызываемый несколькими функциями в этом модуле при запросе дистрибутивного пакета, который не установлен в текущем окружении Python.

## Функциональное API

Этот пакет предоставляет следующие функции через свой публичный API.

### Точки входа

#### `importlib.metadata.entry_points(**select_params)`

Возвращает экземпляр [`EntryPoints`](https://python-all.ru/3/library/importlib.metadata.html#importlib.metadata.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` для получения информации о дистрибутивном пакете, который предоставляет точку входа.

Запрос всех точек входа:

```python
>>> eps = entry_points()
```

Функция `entry_points()` возвращает объект `EntryPoints`, коллекцию всех объектов `EntryPoint` с атрибутами `names` и `groups` для удобства:

```python
>>> sorted(eps.groups)
['console_scripts', 'distutils.commands', 'distutils.setup_keywords', 'egg_info.writers', 'setuptools.installation']
```

`EntryPoints` имеет метод `select()` для выбора точек входа, соответствующих определённым свойствам. Выберите точки входа в группе `console_scripts`:

```python
>>> scripts = eps.select(group='console_scripts')
```

Аналогично, поскольку `entry_points()` передаёт ключевые аргументы в select:

```python
>>> scripts = entry_points(group='console_scripts')
```

Выбрать конкретный скрипт с именем «wheel» (находится в проекте wheel):

```python
>>> 'wheel' in scripts.names
True
>>> wheel = scripts['wheel']
```

Аналогично, запросить эту точку входа во время выбора:

```python
>>> (wheel,) = entry_points(group='console_scripts', name='wheel')
>>> (wheel,) = entry_points().select(group='console_scripts', name='wheel')
```

Проверить разрешённую точку входа:

```python
>>> 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](https://python-all.ru/3/library/importlib.metadata.html).

Изменено в версии 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/library/importlib.metadata.html).

Изменено в версии 3.13: Объекты `EntryPoint` больше не предоставляют кортежеподобный интерфейс ([`__getitem__()`](https://python-all.ru/3/reference/datamodel.html#object.__getitem__)).

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

#### `importlib.metadata.metadata(distribution_name)`

Возвращает метаданные дистрибутива для указанного пакета дистрибутива в виде экземпляра [`PackageMetadata`](https://python-all.ru/3/library/importlib.metadata.html#importlib.metadata.PackageMetadata).

Вызывает [`PackageNotFoundError`](https://python-all.ru/3/library/importlib.metadata.html#importlib.metadata.PackageNotFoundError), если указанный пакет дистрибутива не установлен в текущем окружении Python.

#### `class importlib.metadata.PackageMetadata`

Конкретная реализация протокола [PackageMetadata](https://python-all.ru/3/library/importlib.metadata.html).

В дополнение к предоставлению определённых методов и атрибутов протокола, обращение к экземпляру по индексу эквивалентно вызову метода `get()`.

Каждый [пакет дистрибутива](https://python-all.ru/3/library/importlib.metadata.html) содержит метаданные, которые можно извлечь с помощью функции `metadata()`:

```python
>>> wheel_metadata = metadata('wheel')
```

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

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

[`PackageMetadata`](https://python-all.ru/3/library/importlib.metadata.html#importlib.metadata.PackageMetadata) также предоставляет атрибут `json`, который возвращает все метаданные в JSON-совместимой форме согласно [**PEP 566**](https://python-all.ru/3/library/importlib.metadata.html):

```python
>>> wheel_metadata.json['requires_python']
'>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*'
```

Полный набор доступных метаданных здесь не описан. Дополнительные сведения см. в [спецификации основных метаданных PyPA](https://python-all.ru/3/library/importlib.metadata.html).

Изменено в версии 3.10: Теперь `Description` включается в метаданные при представлении через полезную нагрузку (payload). Символы продолжения строки удалены.

Добавлен атрибут `json`.

### Версии дистрибутивов

#### `importlib.metadata.version(distribution_name)`

Возвращает [версию](https://python-all.ru/3/library/importlib.metadata.html) установленного пакета дистрибутива для указанного пакета дистрибутива.

Вызывает [`PackageNotFoundError`](https://python-all.ru/3/library/importlib.metadata.html#importlib.metadata.PackageNotFoundError), если указанный пакет дистрибутива не установлен в текущем окружении Python.

Функция `version()` – это самый быстрый способ получить номер [версии пакета дистрибутива](https://python-all.ru/3/library/importlib.metadata.html) в виде строки:

```python
>>> version('wheel')
'0.32.3'
```

### Файлы дистрибутива

#### `importlib.metadata.files(distribution_name)`

Возвращает полный набор файлов, содержащихся в указанном пакете дистрибутива.

Вызывает [`PackageNotFoundError`](https://python-all.ru/3/library/importlib.metadata.html#importlib.metadata.PackageNotFoundError), если указанный пакет дистрибутива не установлен в текущем окружении Python.

Возвращает [`None`](https://python-all.ru/3/library/constants.html#None), если дистрибутив найден, но в базе данных установки отсутствуют записи о файлах, связанных с пакетом дистрибутива.

#### `class importlib.metadata.PackagePath`

Объект, производный от [`pathlib.PurePath`](https://python-all.ru/3/library/pathlib.html#pathlib.PurePath), с дополнительными свойствами `dist`, `size` и `hash`, соответствующими метаданным установки пакета дистрибутива для этого файла.

Функция `files()` принимает имя [дистрибутивного пакета](https://python-all.ru/3/library/importlib.metadata.html) и возвращает все файлы, установленные этим дистрибутивом. Каждый файл представлен как экземпляр [`PackagePath`](https://python-all.ru/3/library/importlib.metadata.html#importlib.metadata.PackagePath). Например:

```python
>>> 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>
```

Получив файл, можно также прочитать его содержимое:

```python
>>> 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()`, чтобы получить абсолютный путь к файлу:

```python
>>> util.locate()
PosixPath('/home/gustav/example/lib/site-packages/wheel/util.py')
```

Если файл метаданных со списком файлов (`RECORD` или `SOURCES.txt`) отсутствует, `files()` вернёт [`None`](https://python-all.ru/3/library/constants.html#None). Вызывающий код может обернуть вызовы `files()` в [always\_iterable](https://python-all.ru/3/library/importlib.metadata.html) или иным образом защититься от такой ситуации, если неизвестно, что целевой дистрибутив содержит эти метаданные.

### Зависимости дистрибутивов

#### `importlib.metadata.requires(distribution_name)`

Возвращает объявленные спецификаторы зависимостей для именованного дистрибутивного пакета.

Вызывает [`PackageNotFoundError`](https://python-all.ru/3/library/importlib.metadata.html#importlib.metadata.PackageNotFoundError), если именованный дистрибутивный пакет не установлен в текущем окружении Python.

Чтобы получить полный набор зависимостей для [дистрибутивного пакета](https://python-all.ru/3/library/importlib.metadata.html), используйте функцию `requires()`:

```python
>>> requires('wheel')
["pytest (>=3.0.0) ; extra == 'test'", "pytest-cov ; extra == 'test'"]
```

### Отображение импортов на дистрибутивы

#### `importlib.metadata.packages_distributions()`

Возвращает отображение имён модулей верхнего уровня и импортируемых пакетов, найденных с помощью [`sys.meta_path`](https://python-all.ru/3/library/sys.html#sys.meta_path), на имена дистрибутивных пакетов (если таковые есть), предоставляющих соответствующие файлы.

Для поддержки пакетов пространств имён (которые могут содержать компоненты от нескольких дистрибутивов) каждое имя импорта верхнего уровня сопоставляется со списком имён дистрибутивов, а не напрямую с одним именем.

Удобный метод для разрешения имени [дистрибутивного пакета](https://python-all.ru/3/library/importlib.metadata.html) (или имён, в случае пакета пространства имён), который предоставляет каждый импортируемый модуль или [импортируемый пакет](https://python-all.ru/3/library/importlib.metadata.html) верхнего уровня:

```python
>>> packages_distributions()
{'importlib_metadata': ['importlib-metadata'], 'yaml': ['PyYAML'], 'jaraco': ['jaraco.classes', 'jaraco.functools'], ...}
```

Некоторые редактируемые установки [не предоставляют имён верхнего уровня](https://python-all.ru/3/library/importlib.metadata.html), поэтому эта функция не является надёжной для таких установок.

Добавлено в версии 3.10.

## Дистрибутивы

#### `importlib.metadata.distribution(distribution_name)`

Возвращает экземпляр [`Distribution`](https://python-all.ru/3/library/importlib.metadata.html#importlib.metadata.Distribution), описывающий именованный дистрибутивный пакет.

Вызывает [`PackageNotFoundError`](https://python-all.ru/3/library/importlib.metadata.html#importlib.metadata.PackageNotFoundError), если именованный дистрибутивный пакет не установлен в текущем окружении Python.

#### `class importlib.metadata.Distribution`

Сведения об установленном дистрибутивном пакете.

Примечание: разные экземпляры `Distribution` в настоящее время не считаются равными, даже если они относятся к одному и тому же установленному дистрибутиву и, соответственно, имеют одинаковые атрибуты.

Хотя описанный выше API на уровне модуля является наиболее распространённым и удобным, всю эту информацию можно получить из класса `Distribution`. `Distribution` – это абстрактный объект, представляющий метаданные [дистрибутивного пакета](https://python-all.ru/3/library/importlib.metadata.html) Python. Конкретный экземпляр подкласса `Distribution` для установленного дистрибутивного пакета можно получить, вызвав функцию [`distribution()`](https://python-all.ru/3/library/importlib.metadata.html#importlib.metadata.distribution):

```python
>>> from importlib.metadata import distribution
>>> dist = distribution('wheel')
>>> type(dist)
<class 'importlib.metadata.PathDistribution'>
```

Таким образом, альтернативный способ получить номер версии – через экземпляр `Distribution`:

```python
>>> dist.version
'0.32.3'
```

На экземплярах `Distribution` доступны самые разные дополнительные метаданные:

```python
>>> dist.metadata['Requires-Python']
'>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*'
>>> dist.metadata['License']
'MIT'
```

For editable packages, an `origin` property may present [**PEP 610**](https://python-all.ru/3/library/importlib.metadata.html) metadata:

```python
>>> dist.origin.url
'file:///path/to/wheel-0.32.3.editable-py3-none-any.whl'
```

Полный набор доступных метаданных здесь не описывается. За дополнительными сведениями обратитесь к [спецификации основных метаданных PyPA](https://python-all.ru/3/library/importlib.metadata.html).

Добавлено в версии 3.13: Добавлено свойство `.origin`.

## Обнаружение дистрибутивов

По умолчанию этот пакет предоставляет встроенную поддержку поиска метаданных для [дистрибутивных пакетов](https://python-all.ru/3/library/importlib.metadata.html) файловой системы и zip-файлов. Поиск метаданных по умолчанию использует `sys.path`, но трактует эти значения немного иначе, чем другие механизмы импорта. В частности:

- `importlib.metadata` не учитывает объекты [`bytes`](https://python-all.ru/3/library/stdtypes.html#bytes) из `sys.path`.
- `importlib.metadata` случайно будет учитывать объекты [`pathlib.Path`](https://python-all.ru/3/library/pathlib.html#pathlib.Path) из `sys.path`, хотя такие значения игнорируются при импорте.

## Реализация пользовательских провайдеров

`importlib.metadata` охватывает два API: один для *потребителей*, а другой для *провайдеров*. Большинство пользователей являются потребителями, потребляя метаданные, предоставляемые пакетами. Однако есть и другие сценарии, в которых пользователи хотят предоставлять метаданные через какой-то другой механизм, например, вместе с пользовательским импортёром. Такой сценарий требует *пользовательского провайдера*.

Поскольку метаданные [дистрибутивного пакета](https://python-all.ru/3/library/importlib.metadata.html) недоступны напрямую через поиск [`sys.path`](https://python-all.ru/3/library/sys.html#sys.path) или загрузчики пакетов, метаданные для дистрибутива находятся через [искатели](https://python-all.ru/3/reference/import.html#finders-and-loaders) системы импорта. Чтобы найти метаданные дистрибутивного пакета, `importlib.metadata` опрашивает список [искателей путей метаданных](https://python-all.ru/3/glossary.html#term-meta-path-finder) из [`sys.meta_path`](https://python-all.ru/3/library/sys.html#sys.meta_path).

Реализация включает хуки, встроенные в `PathFinder`, которые предоставляют метаданные для дистрибутивных пакетов, найденных в файловой системе.

Абстрактный класс [`importlib.abc.MetaPathFinder`](https://python-all.ru/3/library/importlib.html#importlib.abc.MetaPathFinder) определяет интерфейс, ожидаемый от искателей системой импорта Python. `importlib.metadata` расширяет этот протокол, проверяя наличие опционального вызываемого объекта `find_distributions` у искателей из [`sys.meta_path`](https://python-all.ru/3/library/sys.html#sys.meta_path), и предоставляет этот расширенный интерфейс как абстрактный базовый класс `DistributionFinder`, который определяет следующий абстрактный метод:

```python
@abc.abstractmethod
def find_distributions(context=DistributionFinder.Context()) -> Iterable[Distribution]:
    """Возвращает итерируемый объект со всеми экземплярами Distribution, способными
    загружать метаданные для пакетов для указанного ``context``.
    """
```

Объект `DistributionFinder.Context` предоставляет свойства `.path` и `.name`, указывающие путь для поиска и имя для сопоставления, и может предоставлять другой релевантный контекст, запрашиваемый потребителем.

На практике, чтобы поддерживать поиск метаданных дистрибутивных пакетов в местах, отличных от файловой системы, создайте подкласс `Distribution` и реализуйте абстрактные методы. Затем из пользовательского искателя возвращайте экземпляры этого производного `Distribution` в методе `find_distributions()`.

### Пример

Представьте пользовательский искатель, который загружает модули Python из базы данных:

```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`:

```python
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` будет выглядеть примерно так:

```python
class DatabaseDistribution(importlib.metadata.Distribution):
    def __init__(self, record):
        self.record = record

    def read_text(self, filename):
        """
        Читает файл, например "METADATA", для текущего 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`. За дополнительными идеями обращайтесь к исходному коду.
