Как исправить "Попытка относительного импорта в не-пакет" даже с __init__.py

Я пытаюсь следовать 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" и "Импортировать модуль из родственника путь", но они не помогли.

Есть ли что-нибудь, что мне не хватает здесь?

Ответ 1

Да. Вы не используете его в качестве пакета.

python -m pkg.tests.core_test

Ответ 2

Чтобы развить ответ Игнасио Васкеса-Абрамса:

Механизм импорта Python работает относительно __name__ текущего файла. Когда вы выполняете файл напрямую, он не имеет своего обычного имени, но вместо этого имеет имя "__main__". Так что относительный импорт не работает.

Как предложил -m вы можете выполнить его, используя -m. Если у вас есть часть вашего пакета, предназначенная для запуска в качестве сценария, вы также можете использовать атрибут __package__ чтобы сообщить этому файлу, какое имя он должен иметь в иерархии пакетов.

См. Http://www.python.org/dev/peps/pep-0366/ для получения подробной информации.

Ответ 3

Вы можете использовать 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__))))

Ответ 4

Это зависит от того, как вы хотите запустить 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

Ответ 5

В файле core_test.py выполните следующие действия:

import sys
sys.path.append('../components')
from core import GameLoopEvents

Ответ 6

Если ваш вариант использования предназначен для запуска тестов, и он швыряет его, то вы можете сделать следующее. Вместо запуска теста script в качестве python core_test.py используйте платформу тестирования, такую ​​как pytest. Затем в командной строке вы можете ввести

$$ py.test

Это запустит тесты в вашем каталоге. Это связано с проблемой __name__ __main__, на которую указал @BrenBarn. Затем поместите пустой файл __init__.py в ваш тестовый каталог, это сделает часть каталога теста частью вашего пакета. Тогда вы сможете сделать

from ..components.core import GameLoopEvents

Однако, если вы запустите свой тестовый script в качестве основной программы, тогда все будет терпеть неудачу еще раз. Так что просто используйте тестовый бегун. Возможно, это также работает с другими тестовыми участниками, такими как nosetests, но я не проверял его. Надеюсь это поможет.

Ответ 7

Мое быстрое исправление заключается в добавлении каталога в путь:

import sys
sys.path.insert(0, '../components/')

Ответ 8

Старая нить. Я обнаружил, что добавление __all__= ['submodule',...] в файл __init__.py и затем использование from <CURRENT_MODULE> import * в целевой системе работает нормально.

Ответ 9

Вы можете использовать from pkg.components.core import GameLoopEvents, например, я использую pycharm, ниже приведен образ структуры моего проекта, я просто импортирую из корневого пакета, затем он работает:

enter image description here

Ответ 10

Если кто-то ищет обходной путь, я наткнулся на один. Здесь немного контекста. Я хотел проверить один из методов в файле. Когда я запускаю его изнутри

if __name__ == "__main__":

он всегда жаловался на относительный импорт. Я попытался применить вышеупомянутые решения, но не сработал, так как было много вложенных файлов, каждый с несколькими импортами.

Вот что я сделал. Я только что создал лаунчер, внешнюю программу, которая будет импортировать необходимые методы и вызывать их. Хотя не очень хорошее решение, оно работает.

Ответ 11

Попробуй это

import components
from components import *

Ответ 12

Как сказал Паоло, у нас есть 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 проверено только.

Ответ 13

Этот подход работал для меня и менее загроможден, чем некоторые решения:

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