Сообщить об ошибке.

Модуль importlib.resources в Python

Обеспечение доступа к ресурсам внутри пакетов

Этот модуль использует систему импорта Python для обеспечения доступа к ресурсам внутри пакетов. Если можно импортировать пакет, то можно получить доступ к ресурсам в этом пакете. Ресурсы можно открывать или читать в двоичном или текстовом режиме.

Ресурсы примерно похожи на файлы внутри каталогов, хотя важно помнить, что это всего лишь метафора. Ресурсы и пакеты не обязательно должны существовать как физические файлы и каталоги в файловой системе.

В конце описания API, смотрите примеры использования модуля importlib.resources.

Примечание. Этот модуль предоставляет функциональные возможности, аналогичные базовому доступу к ресурсам pkg_resources (входит в комплект setuptools), без дополнительных затрат на производительность. Это упрощает чтение ресурсов, включенных в пакеты, с более стабильной и последовательной семантикой.

Загрузчики, которым необходимо поддерживать чтение ресурсов, должны реализовать метод get_resource_reader(fullname), как указано в importlib.abc.ResourceReader.


Определены следующие типы.

importlib.resources.Package:

Тип Package определяется как Union[str, ModuleType]. Это означает, что там, где функция описывает принятие пакета, можно передать либо строку, либо модуль. Объекты модуля должны иметь разрешаемый __spec__.submodule_search_locations, который не равен None.

importlib.resources.Resource:

Этот тип описывает имена ресурсов, переданные различным функциям в этом пакете. Он определяется как Union[str, os.PathLike].


Доступны следующие функции.


importlib.resources.files(package):

Новое в Python 3.9.

Метод importlib.resources.files() возвращает объект importlib.resources.abc.Traversable, представляющий контейнер ресурсов для пакета (каталог) и его ресурсы (файлы). Объект Traversable может содержать другие контейнеры (подкаталоги).

Аргумент package - это либо имя, либо объект модуля, который соответствует требованиям пакета.

importlib.resources.as_file(traversable):

Новое в Python 3.9.

Метод importlib.resources.as_file() возвращает диспетчер контекста для использования в операторе with, обычно из importlib.resources.files(), учитывая объект importlib.resources.abc.Traversable, представляющий файл.

Диспетчер контекста предоставляет объект pathlib.Path.

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

resources.as_file необходимо использовать, когда методов объекта Traversable (read_text и т. д.) недостаточно и требуется фактический файл в файловой системе.

Изменено в Python 3.12: добавлена поддержка traversable, представляющего каталог.

importlib.resources.open_binary(package, resource):

Метод importlib.resources.open_binary() открывает ресурс в пакете для чтения в двоичном формате.

Аргумент package - это либо имя, либо объект модуля, который соответствует требованиям пакета.

Аргумент resource - это имя ресурса, который нужно открыть в пакете. Он может не содержать разделителей путей и не иметь подресурсов (т. е. не может быть каталогом).

Эта функция возвращает экземпляр typing.BinaryIO, двоичный поток ввода-вывода, открытый для чтения.

importlib.resources.open_text(package, resource, encoding='utf-8', errors='strict'):

Метод importlib.resources.open_text() открывает ресурс в пакете для чтения в текстовом формате. По умолчанию ресурс открыт для чтения как UTF-8.

Аргумент package - это либо имя, либо объект модуля, который соответствует требованиям пакета.

Аргумент resource - это имя ресурса, который нужно открыть в пакете. Он может не содержать разделителей путей и не иметь подресурсов (т. е. не может быть каталогом).

Аргументы encoding и errors имеют то же значение, что и аналогичные аргументы встроенной функции open().

Эта функция возвращает экземпляр typing.TextIO, текстовый поток ввода-вывода, открытый для чтения.

importlib.resources.read_binary(package, resource):

Метод importlib.resources.read_binary() читает и возвращает содержимое ресурса в пакете в байтах.

Аргумент package - это либо имя, либо объект модуля, который соответствует требованиям пакета.

Аргумент resource - это имя ресурса, который нужно открыть в пакете. Он может не содержать разделителей путей и не иметь подресурсов (т. е. не может быть каталогом).

Эта функция возвращает содержимое ресурса в байтах.

importlib.resources.read_text(package, resource, encoding='utf-8', errors='strict'):

Метод importlib.resources.read_text() читает и возвращает содержимое ресурса в пакете как str. По умолчанию содержимое читается в строгом UTF-8.

Аргумент package - это либо имя, либо объект модуля, который соответствует требованиям пакета.

Аргумент resource - это имя ресурса, который нужно открыть в пакете. Он может не содержать разделителей путей и не иметь подресурсов (т. е. не может быть каталогом).

Аргументы encoding и errors имеют то же значение, что и аналогичные аргументы встроенной функции open().

Эта функция возвращает содержимое ресурса как строку str.

importlib.resources.path(package, resource):

Метод importlib.resources.path() возвращает путь к ресурсу как фактический путь к файловой системе. Эта функция возвращает диспетчер контекста для использования в операторе with. Диспетчер контекста предоставляет объект pathlib.Path.

Выход из диспетчера контекста очищает любой временный файл, созданный, когда ресурс должен быть извлечен, например, из zip-файла.

Аргумент package - это либо имя, либо объект модуля, который соответствует требованиям пакета.

Аргумент resource - это имя ресурса, который нужно открыть в пакете. Он может не содержать разделителей путей и не иметь подресурсов (т. е. не может быть каталогом).

importlib.resources.is_resource(package, name):

Метод importlib.resources.is_resource() возвращает True, если в пакете есть ресурс с именем name, в противном случае - False. Помните, что каталоги не являются ресурсами!

Аргумент package - это либо имя, либо объект модуля, который соответствует требованиям пакета.

importlib.resources.contents(package):

Метод importlib.resources.contents() возвращает итерацию по именованным элементам в пакете. Итерируемый объект возвращает ресурсы str (например, файлы) и не ресурсы (например, каталоги). Итерируемый объект не рекурсивно переходит в подкаталоги.

Аргумент package - это либо имя, либо объект модуля, который соответствует требованиям пакета.

Примеры использования модуля importlib.resources:

Допустим, вы пишете библиотеку синтаксического анализа электронной почты и в тестовом наборе есть образец сообщения электронной почты в файле с именем message.eml. Необходимо получить доступ к содержимому этого файла для своих тестов, файл находится в проекте по пути email/tests/data/message.eml. Допустим, модульные тесты находятся в /tests/test_email.py.

Тогда тест может прочитать файл данных, выполнив что-то вроде:

data_dir = os.path.join(os.path.dirname(__file__), 'tests', 'data')
data_path = os.path.join(data_dir, 'message.eml')
with open(data_path, encoding='utf-8') as fp:
    eml = fp.read()

Использование __file__ не работает, если пакет находится внутри zip-файла, т.к. в этом случае этот код не находится в файловой системе.

Модуль importlib.resources решает эту проблему. Используя преимущества всей эффективности системы импорта Python и того факта, что она встроена в Python, эквивалентный код с использованием importlib_resources будет выглядеть так:

from importlib.resources import files
# Читает содержимое в кодировке UTF-8 и возвращает `str`
eml = files('email.tests.data').joinpath('message.eml').read_text()

Пакеты или названия пакетов?

Все API importlib.resources принимают пакет в качестве первого параметра, но это может быть либо имя пакета (как str), либо фактический объект модуля, хотя модуль должен быть пакетом. Если передается строка, то она должна называть импортируемый пакет Python, и сначала импортируется он. Таким образом, приведенный выше пример также может быть записан как:

import email.tests.data
eml = files(email.tests.data).joinpath('message.eml').read_text()

Файловая система или zip-файл?

В общем - без разницы. Никогда не нужно беспокоиться о том, находится ли ваш пакет в файловой системе или в zip-файле, т.к. API importlib.resources скрывают эти детали. НО иногда необходим путь к реальному файлу в файловой системе. Например, некоторые API-интерфейсы SSL требуют, чтобы в файле сертификата был указан реальный путь к файловой системе, а для функции dlopen() языка C также требуется реальный путь к файловой системе.

Для поддержки этого importlib.resources предоставляет API, который извлекает ресурс из zip-файла во временный файл и возвращает путь файловой системы к этому временному файлу в виде объекта pathlib.Path, который можно использовать в операторе with:

from importlib.resources import files, as_file

source = files(email.tests.data).joinpath('message.eml')
with as_file(source) as eml:
    third_party_api_requiring_file_system_path(eml)

Можно использовать все стандартные API contextlib для управления этим диспетчером контекста.

!!! Внимание:

Существует странное взаимодействие с Python 3.4, 3.5 и 3.6 относительно добавления путей к файлам zip или wheel в sys.path. Из-за ограничений в zipimport, которые нельзя изменить без нарушения обратной совместимости, необходимо использовать абсолютный путь к файлу zip/wheel. Если использовать относительный путь, то нельзя будет найти ресурсы в этих zip-файлах.

Как относительные, так и абсолютные пути работают для Python 3.7 и новее.

Например:

# !Неправильно!
sys.path.append('relative/path/to/foo.whl')
files('foo')  # This will fail!

# Правильно!
sys.path.append(os.path.abspath('relative/path/to/foo.whl'))
files('foo')