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

Атрибуты path и path_hooks модуля sys в Python

Список путей поиска для импорта модулей

Содержание:


sys.path:

Атрибут sys.path модуля sys представляет собой список строк, который указывает путь поиска для модулей. Инициализируется из переменной среды PYTHONPATH, а также зависит от установки по умолчанию.

Атрибут sys.path инициализируется при запуске программы, первый элемент этого списка, path[0] является каталогом, содержащим скрипт, который использовался для вызова интерпретатора Python. Если каталог скриптов недоступен, например если интерпретатор вызывается в интерактивном режиме или если скрипт читается из стандартного ввода, то path[0] является пустой строкой, которая в первую очередь направляет Python для поиска модулей в текущем каталоге. Обратите внимание, что каталог скриптов вставляется перед записями, вставленными из PYTHONPATH.

Из кода можно свободно изменять этот список для своих собственных целей. В sys.path должны быть добавлены только строки и байтовые строки, все остальные типы данных игнорируются при импорте.

Внимание! sys.path больше не принимает байтовые строки. Поддержка прекратилась где-то между Python 3.2 и 3.6, и никто этого не заметил, пока не вышел Python 3.10.

Примеры использования sys.path:

Путь поиска для модулей управляется как список Python, сохраненный в sys.path. Содержимое пути по умолчанию включает каталог скрипта, используемого для запуска приложения и текущий рабочий каталог.

import sys

for d in sys.path:
    print(d)

# /home/docs-python/python-3.7.4/docs-python
# /home/docs-python/python-3.7.4/docs-python/lib/python37.zip
# /home/docs-python/python-3.7.4/docs-python/lib/python3.7
# /home/docs-python/python-3.7.4/docs-python/lib/python3.7/lib-dynload
# /opt/python-3.7.4/lib/python3.7
# /home/docs-python/python-3.7.4/docs-python/lib/python3.7/site-packages

Из кода также можно добавить свой путь поиска модуля из нестандартного места, добавив новый путь непосредственно в sys.path.

Создадим тестовый модуль test_md.py, который сохраним в директории ./md/test_md.py

# test_md.py
DATA = [1, 2, 3, 4, 5, 6, 7, 8, 9]

Теперь импортируем тестовый модуль

import os
import sys

base_dir = os.path.dirname(__file__) or '.'
print('Base directory:', base_dir)

# добавление нового пути поиска
dir_md = os.path.join(base_dir, 'md')
sys.path.append(dir_md)

import test_md
print('Imported example from:', test_md.__file__)
print('  ', test_md.DATA)

# Base directory: .
# Imported test_md from: ./md/test_md.py
#    [1, 2, 3, 4, 5, 6, 7, 8, 9]

Изменение пути поиска позволяет контролировать поиск стандартных модулей Python. Но что, если программе необходимо импортировать код из файла, отличного от обычных файлов .py или .pyc в файловой системе? Ловушки импорта sys.path_hooks решает эту проблему. Они могут перехватить попытку найти модуль в sys.path и принять альтернативные меры для загрузки кода из другого места или применения к нему предварительной обработки.


sys.path_hooks:

Атрибут sys.path_hooks содержит список вызываемых объектов, которые принимают путь - аргумент path, чтобы попытаться создать искатель finder для этого пути. Если искатель может быть создан, он должен быть возвращен вызываемым объектом, иначе необходимо вызвать исключение ImportError.

Пользовательские импортеры реализуются в два отдельных этапа. Искатель отвечает за определение местоположения модуля и предоставление загрузчика для управления фактическим импортом. Пользовательские модули поиска добавляются путем добавления фабрики в список sys.path_hooks. При импорте каждая часть пути передается искателю до тех пор, пока не будет заявлено о поддержке не вызывая исключение ImportError. Затем этот искатель отвечает за поиск в хранилище данных, представленном его записью пути для именованных модулей.

Примеры использования sys.path_hooks:

Создадим тестовый модуль test_md.py, который сохраним в директории ./md/test_md.py

# test_md.py
DATA = [1, 2, 3, 4, 5, 6, 7, 8, 9]
import sys

class ImportFinder:
    PATH_TRIGGER = './md'

    def __init__(self, path_entry):
        print(f'Checking {path_entry}:', end=' ')
        if path_entry != self.PATH_TRIGGER:
            print('wrong finder')
            raise ImportError()
        else:
            print('works')
        return

    def find_module(self, fullname, path=None):
        print('Looking for {!r}'.format(fullname))
        return None

sys.path_hooks.append(ImportFinder)

for hook in sys.path_hooks:
    print(f'Path hook: {hook}')

sys.path.insert(0, ImportFinder.PATH_TRIGGER)

try:
    print('importing "test_md"')
    import test_md
except Exception as e:
    print('Import failed:', e)

# Path hook: <class 'zipimport.zipimporter'>
# Path hook: <function FileFinder.path_hook.<locals>.path_hook_for_FileFinder at 0x7f3a6e4de3b0>
# Path hook: <class '__main__.ImportFinder'>
# importing "test_md"

sys.path_importer_cache:

Атрибут sys.path_importer_cache представляет собой словарь, который выступает в качестве кэша для объектов поиска. Ключи - это пути, которые были переданы в систему sys.path_hooks и значения - это найденные искатели. Если путь является допустимым путем файловой системы, но искатель не найден в системе, то тогда путь в sys.path_hooks не сохраняется.

>>> import sys
>>> sys.path_importer_cache
# {'/home/docs-python/python-3.7.4/docs-python/lib/python37.zip': None, 
# '/home/docs-python/python-3.7.4/docs-python/lib/python3.7': FileFinder('/home/docs-python/python-3.7.4/docs-python/lib/python3.7'),
# ...
# ...
# '/home/docs-python': FileFinder('/home/docs-python')}

sys.meta_path:

Атрибут sys.meta_path представляет собой список объектов поиска мета-пути, у которых есть свои методы find_spec(), вызываемые для определения, может ли один из объектов найти модуль для импорта.

Метод find_spec() вызывается как минимум с абсолютным именем импортируемого модуля. Если импортируемый модуль содержится в пакете, то атрибут __path__ родительского пакета передается в качестве второго аргумента. Метод возвращает спецификацию модуля или None, если модуль не может быть найден.

>>> import sys
>>> sys.meta_path
# [<class '_frozen_importlib.BuiltinImporter'>, 
# <class '_frozen_importlib.FrozenImporter'>, 
# <class '_frozen_importlib_external.PathFinder'>]