Не работает импорт модуля по абсолютному пути в python

Структура проекта такая:

my_project
|
|___database
|   |
    |___api
    |   |
    |   |___db_api.py
    |
    |___models
    |   |
    |   |__film
    |   |_user
        |_recomendation

Я запуская скрипт db_api в котором модули из папки models импортируются так

from database.models.film import Film
from database.models.user import User
from database.models.recomendation import Recomendation

И я получаю ошибку

Exception has occurred: ModuleNotFoundError
No module named 'database'
  File "/home/vor/vscode_WS/db_sqlalchemy_practice/database/api/database_api.py", line 6, in <module>
    from database.models.film import Film
ModuleNotFoundError: No module named 'database'

Я знаю, что относительные пути нельзя использовать в скриптах, которые непосредственно запускаются, но я использую абсолютный! В чем проблема, подскажите?


Ответы (3 шт):

Автор решения: popovic86

возможно, не так понял вопрос, но, может, мой ответ чем то поможет:

По Вашей структуре получается, что в database у вас действительно нет директории models, она находится в my_project. Возможно, вот так нужно сделать импорт:

from my_project.models.film import Film

Я бы сделал классический пакет с файлом __unit__.py, и туда бы напихал все импорты.

примерно:

введите сюда описание изображения

→ Ссылка
Автор решения: insolor

Абсолютный импорт точно так же как и относительный работает только внутри пакета, т.е. чтобы работало, нужно запускать командой python -m database.api.db_api из корня проекта.

Когда вы запускаете модуль как скрипт, импорт вида from database.models.film import Film для интерпретатора Python означает, что в папке со скриптом (либо по какому-то из путей из переменной окружения PYTHONPATH) должен находиться пакет (папка) database, в нем пакет models, в нем модуль film, и из модуля уже нужно импортировать класс Film. Интерпретатор в этом случае не смотрит на уровни выше, а смотрит только в папке скрипта и папках из PYTHONPATH.

Если запускать db_api как модуль из пакета, то интерпретатор поймет, что мы находимся в пакете database. И что в этом же пакете нужно искать, то что мы импортируем, в данном случае по абсолютному импорту от "корня" пакета, а не от директории, запускаемый модуль находится.

Можно, конечно, добавить корень проекта в PYTHONPATH, но это костыльное решение. Нормальное решение — запускать как пакет.


Для упрощения запуска можно в корень проекта (рядом с папкой database) добавить "пусковой" скрипт, в нем импортировать db_api. Предположим, что в db_api у вас есть функция main, ее запуск идет в блоке if __name__ == "__main__": (код не стартует просто при импорте). Тогда в пусковом файле пишем что-то типа:

from database.api.db_api import main

if __name__ == "__main__":
    main()

Если в db_api пусковой код не в if __name__ == "__main__":, то он сработает просто при импорте, дополнительно вызывать ничего не нужно будет. Но я бы не советовал так оставлять, лучше спрятать код в этот блок, а в корневом пусковом файле явно запускать то что вам нужно ("явное лучше неявного").

→ Ссылка
Автор решения: artur1214

Есть несколько способов решения такой проблемы:

$PYTHONPATH

В Python вы можете создать (или обновить) переменную окружения PYTHOPATH, работает она так же как и стандартная системная переменная $PATH, только если $PATH указывает где искать пути к исполняемым файлам, PYTHONPATH указывает пути к пакетам питона. Особенно удобно это может быть при испоьзовании virtualenv, ведь вы можете добавить экспорт этой переменной в скрипт активации виртуальной среды, примерно так:

export PYTHONPATH=/home/artur1214/ptest/my_project && python3 db_api.py

(структура проекта:)

введите сюда описание изображения

Кстати, почти наверняка в Pycharm ваш код бы работал, потому что он автоматически добавляет к PYTHONPATH путь до текущего проекта.

Но такой способ очень неудобен:

Придется придется постоянно разбираться как правильно настроить PYTHONPATH при переносе на новый компьютер или просто переносе проекта в другую папку.

Запуск из файла.

Создайте в корне проекта условный main.py

from database.api.db_api import *
#...код

При этом вы импорты будут работать. Основной код вы можете продолжать писать внутри db_api, или (лучше) перенести тесты в main.py, а в db_api оставить только логику модуля api

Если совсем невмоготу, вы можете написать в самом верху db_api вот такой код:

import sys
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))

Он добавляет в системный список путей путь до корня проекта относительно db_api.py. (По сути то же самое, что PYTHONPATH, только из питона) Но на мой взгляд это полукостыльное решение.

→ Ссылка