Попытка относительного импорта за пределы пакета
Вот моя структура папок:
Mopy/ # no init.py !
bash/
__init__.py
bash.py # <--- Edit: yep there is such a module too
bass.py
bosh/
__init__.py # contains from .. import bass
bsa_files.py
...
test_bash\
__init__.py # code below
test_bosh\
__init__.py
test_bsa_files.py
В test_bash\__init__.py
меня есть:
import sys
from os.path import dirname, abspath, join, sep
mopy = dirname(dirname(abspath(__file__)))
assert mopy.split(sep)[-1].lower() == 'mopy'
sys.path.append(mopy)
print 'Mopy folder appended to path: ', mopy
в то время как в test_bsa_files.py
:
import unittest
from unittest import TestCase
import bosh
class TestBSAHeader(TestCase):
def test_read_header(self):
bosh.bsa_files.Header.read_header()
if __name__ == '__main__':
unittest.main()
Теперь, когда я выпускаю:
python.exe "C:\_\JetBrains\PyCharm 2016.2.2\helpers\pycharm\utrunner.py" C:\path\to\Mopy\test_bash\test_bosh\test_bsa_files.py true
Я получил:
Traceback (most recent call last):
File "C:\_\JetBrains\PyCharm 2016.2.2\helpers\pycharm\utrunner.py", line 124, in <module>
modules = [loadSource(a[0])]
File "C:\_\JetBrains\PyCharm 2016.2.2\helpers\pycharm\utrunner.py", line 43, in loadSource
module = imp.load_source(moduleName, fileName)
File "C:\Dropbox\eclipse_workspaces\python\wrye-bash\Mopy\test_bash\test_bosh\test_bsa_files.py", line 4, in <module>
import bosh
File "C:\Dropbox\eclipse_workspaces\python\wrye-bash\Mopy\bash\bosh\__init__.py", line 50, in <module>
from .. import bass
ValueError: Attempted relative import beyond toplevel package
Так как "Mopy" находится в sys.path и bosh bosh\__init__.py
правильно решен, почему он жалуется на относительный импорт выше пакета верхнего уровня? Какой пакет верхнего уровня?
Кстати, это моя попытка добавить тесты в унаследованный проект - спрашивал в макете пакета тестов Python, но был закрыт как дубликат " Куда идут юнит-тесты Python?" , Комментарии к моему текущему макету тестового пакета очень ценятся!
Ну, ответ ниже не работает в моем случае:
Модуль bash.py является точкой входа в приложение, содержащее:
if __name__ == '__main__':
main()
Когда я использую import bash.bosh
или from bash import bosh
я получаю:
C:\_\Python27\python.exe "C:\_\JetBrains\PyCharm 2016.2.2\helpers\pycharm\utrunner.py" C:\Dropbox\eclipse_workspaces\python\wrye-bash\Mopy\test_bash\test_bosh\test_bsa_files.py true
Testing started at 3:45 PM ...
usage: utrunner.py [-h] [-o OBLIVIONPATH] [-p PERSONALPATH] [-u USERPATH]
[-l LOCALAPPDATAPATH] [-b] [-r] [-f FILENAME] [-q] [-i]
[-I] [-g GAMENAME] [-d] [-C] [-P] [--no-uac] [--uac]
[--bashmon] [-L LANGUAGE]
utrunner.py: error: unrecognized arguments: C:\Dropbox\eclipse_workspaces\python\wrye-bash\Mopy\test_bash\test_bosh\test_bsa_files.py true
Process finished with exit code 2
Это сообщение об использовании от main() в bash.
Ответы
Ответ 1
TL;DR: Do
import bash.bosh
или
from bash import bosh
Если у вас также есть конструкция типа bash.bash
, вы должны убедиться, что ваш пакет имеет приоритет над его содержимым. Вместо добавления добавьте его в начало порядка поиска:
# test_bash\__init__.py
sys.path.insert(0, mopy)
Когда вы делаете
import bosh
он импортирует модуль bosh
. Это означает, что Mopy/bash
находится в вашем sys.path
, python находит файл bosh
там и импортирует его. Теперь модуль глобально известен под именем bosh
. Независимо от того, является ли bosh
самим модулем или пакетом, это изменяет только значение bosh.py
или bosh/__init__.py
.
Теперь, когда bosh
пытается выполнить
from .. import bass
это не операция файловой системы ( "один каталог вверх, файл баса" ), а операция имени модуля. Это означает "один уровень пакета, бас модуля". bosh
не был импортирован из своего пакета, но сам по себе. Таким образом, один пакет невозможен - вы попадаете в пакет ''
, что недопустимо.
Посмотрите, что происходит, когда вы делаете
import bash.bosh
вместо этого. Сначала импортируется пакет bash
. Затем bosh
импортируется как модуль этого пакета - он глобально известен как bash.bosh
, даже если вы использовали from bash import bosh
.
Когда bosh
делает
from .. import bass
который работает сейчас: переход на один уровень от bash.bosh
приведет вас к bash
. Оттуда bass
импортируется как bash.bass
.
См. также этот связанный ответ для выполнения модуля из пакета без изменения sys.path
.