Как исправить ошибку "попытался относительный импорт в упаковке" даже с 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" и "импорт модуля из относительного пути", но они не помогли.



есть ли что-нибудь, что я упускаю здесь?

1059   12  

12 ответов:

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

python -m pkg.tests.core_test

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

механизм импорта 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 но я не проверял его. Надеюсь, это поможет.

мое быстрое решение-добавить каталог в путь:

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

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

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

if __name__ == "__main__":

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

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

попробуй такое

import components
from components import *

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

enter image description here

как Пауло сказал, У нас есть 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

    Ничего не найдено.