Ответ 1
Да. Вы не используете его в качестве пакета.
python -m pkg.tests.core_test
Я пытаюсь следовать PEP 328 со следующей структурой каталогов:
pkg/
__init__.py
components/
core.py
__init__.py
tests/
core_test.py
__init__.py
В core_test.py
У меня есть следующая инструкция import
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" и "Импортировать модуль из родственника путь", но они не помогли.
Есть ли что-нибудь, что мне не хватает здесь?
Да. Вы не используете его в качестве пакета.
python -m pkg.tests.core_test
Чтобы развить ответ Игнасио Васкеса-Абрамса:
Механизм импорта Python работает относительно __name__
текущего файла. Когда вы выполняете файл напрямую, он не имеет своего обычного имени, но вместо этого имеет имя "__main__"
. Так что относительный импорт не работает.
Как предложил -m
вы можете выполнить его, используя -m
. Если у вас есть часть вашего пакета, предназначенная для запуска в качестве сценария, вы также можете использовать атрибут __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__))))
Это зависит от того, как вы хотите запустить script.
Если вы хотите запустить свой UnitTest из командной строки классическим способом, то есть:
python tests/core_test.py
Тогда, поскольку в этом случае "компоненты" и "тесты" являются палочками сестер, вы можете импортировать относительный модуль либо с помощью метода вставки или добавления sys.path. Что-то вроде:
import sys
from os import path
sys.path.append( path.dirname( path.dirname( path.abspath(__file__) ) ) )
from components.core import GameLoopEvents
В противном случае вы можете запустить script с аргументом '-m' (обратите внимание, что в этом случае речь идет о пакете, и, таким образом, вы не должен давать расширение ".py" ), то есть:
python -m pkg.tests.core_test
В таком случае вы можете просто использовать относительный импорт, как вы делали:
from ..components.core import GameLoopEvents
Вы можете, наконец, объединить два подхода, чтобы ваш script работал независимо от того, как он называется. Например:
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
В файле core_test.py выполните следующие действия:
import sys
sys.path.append('../components')
from core import GameLoopEvents
Если ваш вариант использования предназначен для запуска тестов, и он швыряет его, то вы можете сделать следующее. Вместо запуска теста script в качестве python core_test.py
используйте платформу тестирования, такую как pytest
. Затем в командной строке вы можете ввести
$$ py.test
Это запустит тесты в вашем каталоге. Это связано с проблемой __name__
__main__
, на которую указал @BrenBarn. Затем поместите пустой файл __init__.py
в ваш тестовый каталог, это сделает часть каталога теста частью вашего пакета. Тогда вы сможете сделать
from ..components.core import GameLoopEvents
Однако, если вы запустите свой тестовый script в качестве основной программы, тогда все будет терпеть неудачу еще раз. Так что просто используйте тестовый бегун. Возможно, это также работает с другими тестовыми участниками, такими как nosetests
, но я не проверял его. Надеюсь это поможет.
Мое быстрое исправление заключается в добавлении каталога в путь:
import sys
sys.path.insert(0, '../components/')
Старая нить. Я обнаружил, что добавление __all__= ['submodule',...]
в файл __init__.py и затем использование from <CURRENT_MODULE> import *
в целевой системе работает нормально.
Вы можете использовать from pkg.components.core import GameLoopEvents
, например, я использую pycharm, ниже приведен образ структуры моего проекта, я просто импортирую из корневого пакета, затем он работает:
Если кто-то ищет обходной путь, я наткнулся на один. Здесь немного контекста. Я хотел проверить один из методов в файле. Когда я запускаю его изнутри
if __name__ == "__main__":
он всегда жаловался на относительный импорт. Я попытался применить вышеупомянутые решения, но не сработал, так как было много вложенных файлов, каждый с несколькими импортами.
Вот что я сделал. Я только что создал лаунчер, внешнюю программу, которая будет импортировать необходимые методы и вызывать их. Хотя не очень хорошее решение, оно работает.
Попробуй это
import components
from components import *
Как сказал Паоло, у нас есть 2 метода вызова:
1) python -m tests.core_test
2) python tests/core_test.py
Одно из различий между ними - строка sys.path [0]. Так как интерпретатор будет искать sys.path при выполнении импорта, мы можем сделать с помощью 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 проверено только.
Этот подход работал для меня и менее загроможден, чем некоторые решения:
try:
from ..components.core import GameLoopEvents
except ValueError:
from components.core import GameLoopEvents
Родительский каталог находится в моем PYTHONPATH, и в родительском каталоге и в этом каталоге есть файлы __init__.py
.
Вышеупомянутое всегда работало в python 2, но python 3 иногда сталкивался с ImportError или ModuleNotFoundError (последний является новым в python 3.6 и подклассом ImportError), поэтому следующая настройка работает для меня как в python 2, так и в 3:
try:
from ..components.core import GameLoopEvents
except ( ValueError, ImportError):
from components.core import GameLoopEvents