Как исправить ошибку "попытался относительный импорт в упаковке" даже с init.py
Я пытаюсь следовать PEP 328, со следующей структурой каталогов:
pkg/
__init__.py
components/
core.py
__init__.py
tests/
core_test.py
__init__.py
на core_test.py у меня есть следующий оператор импорта
from ..components.core import GameLoopEvents
однако, когда я запускаю, я получаю следующую ошибку:
tests$ python core_test.py
Traceback (most recent call last):
File "core_test.py", line 3, in <module>
from ..components.core import GameLoopEvents
ValueError: Attempted relative import in non-package
Поиск вокруг я нашел "относительный путь не работает даже с __init__.py" и "импорт модуля из относительного пути", но они не помогли.
есть ли что-нибудь, что я упускаю здесь?
12 ответов:
разъяснить Игнасио Васкес-Абрамс ответ:
механизм импорта Python работает относительно
__name__текущего файла. Когда вы выполняете файл напрямую, он не имеет своего обычного имени, но имеет"__main__"как его имя. Так что относительный импорт не работает.вы можете, как предложил Igancio, выполнить его с помощью . Если у вас есть часть пакета, которая предназначена для запуска скрипта, вы также можете использовать
__package__атрибут, чтобы сообщить этому файлу, какое имя он должен иметь в иерархии пакетов.посмотреть http://www.python.org/dev/peps/pep-0366/ для сведения.
можно использовать
import components.coreнапрямую, если вы добавляете текущий каталог вsys.path:if __name__ == '__main__' and __package__ is None: from os import sys, path sys.path.append(path.dirname(path.dirname(path.abspath(__file__))))
Это зависит от того, как вы хотите запустить свой скрипт.
если вы хотите запустите UnitTest из командной строки классическим способом, то есть:
python tests/core_test.pyтогда, так как в данном случае 'компоненты' и 'тесты' являются папки братьев и сестер, вы можете импортировать относительный модуль либо с помощью вставить или добавить метод sys.путь модуль. Что-то например:
import sys from os import path sys.path.append( path.dirname( path.dirname( path.abspath(__file__) ) ) ) from components.core import GameLoopEventsв противном случае, вы можете запустите скрипт с аргументом '- m' (обратите внимание, что в данном случае речь идет о пакете, и поэтому вы не должны давать расширение '.py'), то есть:
python -m pkg.tests.core_testв таком случае, вы можете просто использовать относительный импорт как:
from ..components.core import GameLoopEventsвы можете, наконец, смешать два подхода, так что ваш скрипт будет работать независимо от того, как он называется. Для пример:
if __name__ == '__main__': if __package__ is None: import sys from os import path sys.path.append( path.dirname( path.dirname( path.abspath(__file__) ) ) ) from components.core import GameLoopEvents else: from ..components.core import GameLoopEvents
In core_test.py, выполните следующие действия:
import sys sys.path.append('../components') from core import GameLoopEvents
если ваш вариант использования предназначен для выполнения тестов, и это швы, что это, то вы можете сделать следующее. Вместо того, чтобы запускать свой тестовый скрипт как
python core_test.pyиспользуйте фреймворк тестирования, напримерpytest. Затем в командной строке можно ввести$$ py.testчто будет запускать тесты в каталоге. Это позволяет обойти проблему
__name__будучи__main__на это указал @BrenBarn. Далее ставим пустой__init__.pyфайл в тестовый каталог, это сделает тестовый каталог частью вашего пакет. Тогда вы сможете сделатьfrom ..components.core import GameLoopEventsоднако, если вы запустите тестовый скрипт в качестве основной программы, то все снова не получится. Так что просто используйте тестов. Возможно, это также работает с другими тестовыми бегунами, такими как
nosetestsно я не проверял его. Надеюсь, это поможет.
старая нить. Я узнал, что добавление
__all__= ['submodule', ...]к __init__.py файл, а затем с помощьюfrom <CURRENT_MODULE> import *в целевой работает нормально.
Если кто-то ищет обходной путь, я наткнулся на один. Вот немного контекста. Я хотел проверить один из методов, которые у меня есть в файле. Когда я запускаю его внутри
if __name__ == "__main__":Он всегда жаловался на относительный импорт. Я попытался применить вышеуказанные решения, но не смог работать, так как было много вложенных файлов, каждый с несколькими импортами.
вот что я сделал. Я только что создал launcher, внешнюю программу, которая импортировала бы необходимые методы и позвони им. Хотя, не отличное решение, оно работает.
как Пауло сказал, У нас есть 2 способа вызова:
1) python -m tests.core_test 2) python tests/core_test.pyодно различие между ними-sys.путь[0] строку. Так как интерпретатор будет искать sys.путь при выполнении импорта, мы можем сделать с
tests/core_test.py:if __name__ == '__main__': import sys from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) from components import core <other stuff>и больше после этого, мы можем побежать core_test.py с другими методами:
cd tests python core_test.py python -m core_test ...Примечание, py36 проверено только.

Comments