Релятивный импорт в исполняемом файле Часто встречается ситуа | Python Заметки
Релятивный импорт в исполняемом файле
Часто встречается ситуация, когда исполняемый скрипт находится внутри Python-пакета. Например, представим такую структуру библиотеки:
my_lib/
cmd/
start.py
stop.py
core.py
services.py
Для запуска каких-то процессов мне надо исполнить скрипт start.py и вот как я делаю его вызов:
python3 /mnt/libs/my_lib/cmd/start.py
Пока выглядит всё красиво.
Но что, если я внутри этого файла хочу импортировать модуль services.py? При этом я хочу использовать релятивный импорт
# start.py
if __name__ == "__main__":
from .. import services
Я получу такую ошибку:
ValueError: attempted relative import beyond top-level package
Эта ошибка возникает потому, что интерпретатор просто не знает что мы находимся внутри пакета и не может понять куда это мы собрались выйти на уровень выше)
Есть три способа как избежать этой ошибки. Все они требуют чтобы библиотека my_lib находилась в доступном для импорта месте, то есть в моëм случае чтобы путь /mnt/libs был в sys.path.
Просто пишем полный путь импорта
if __name__ == "__main__":
from my_lib import services
Это сработает. Но, очевидно, что это не то, что мы ищем. Нам нужен релятивный импорт.
Если интерпретатору подсказать имя пакета в котором мы находимся, то всё заведётся. И есть два способа это сделать. Первый способ — это запускать не через имя файла а по имени модуля
python -m my_lib.cmd.start
Уже самой командой мы обозначили все необходимые неймспейсы.
Если предыдущий способ недоступен (то есть запускаем именно по пути к файлу .../start.py), то объявляем имя пакета прямо внутри кода. Для этого используем переменную модуля __package__
if __name__ == "__main__":
if __package__ is None:
__package__ = 'my_lib.cmd'
from .. import services
Кстати, мы также можем при необходимости:
динамически определить имя пакета в котором находимся
добавить необходимые пути к основной библиотеке в sys.path перед импортом
переместить обновление __package__ в начале скрипта вместе со всеми импортами но обязательно с проверкой is None!
#tricks