Тест-драйв Django не работает в virtualenv на Ubuntu
Я боролся с проблемой с тестовым бегуном Django, установленным в виртуальной среде Python на Ubuntu 14.04. То же самое программное обеспечение отлично работает на MacOS, и я думаю, что это было хорошо для более ранней версии Ubuntu.
Сообщение об ошибке:
ImportError: '<test>' module incorrectly imported from '<base-env>/local/lib/python2.7/site-packages/<package-dir>'. Expected '<base-env>/lib/python2.7/site-packages/<package-dir>'. Is this module globally installed?
И полная трассировка стека от ошибки:
Traceback (most recent call last):
File "/home/annalist/anenv/bin/django-admin", line 11, in <module>
sys.exit(execute_from_command_line())
File "/home/annalist/anenv/local/lib/python2.7/site-packages/django/core/management/__init__.py", line 385, in execute_from_command_line
utility.execute()
File "/home/annalist/anenv/local/lib/python2.7/site-packages/django/core/management/__init__.py", line 377, in execute
self.fetch_command(subcommand).run_from_argv(self.argv)
File "/home/annalist/anenv/local/lib/python2.7/site-packages/django/core/management/commands/test.py", line 50, in run_from_argv
super(Command, self).run_from_argv(argv)
File "/home/annalist/anenv/local/lib/python2.7/site-packages/django/core/management/base.py", line 288, in run_from_argv
self.execute(*args, **options.__dict__)
File "/home/annalist/anenv/local/lib/python2.7/site-packages/django/core/management/commands/test.py", line 71, in execute
super(Command, self).execute(*args, **options)
File "/home/annalist/anenv/local/lib/python2.7/site-packages/django/core/management/base.py", line 338, in execute
output = self.handle(*args, **options)
File "/home/annalist/anenv/local/lib/python2.7/site-packages/django/core/management/commands/test.py", line 88, in handle
failures = test_runner.run_tests(test_labels)
File "/home/annalist/anenv/local/lib/python2.7/site-packages/django/test/runner.py", line 147, in run_tests
suite = self.build_suite(test_labels, extra_tests)
File "/home/annalist/anenv/local/lib/python2.7/site-packages/django/test/runner.py", line 96, in build_suite
tests = self.test_loader.discover(start_dir=label, **kwargs)
File "/usr/lib/python2.7/unittest/loader.py", line 206, in discover
tests = list(self._find_tests(start_dir, pattern))
File "/usr/lib/python2.7/unittest/loader.py", line 287, in _find_tests
for test in self._find_tests(full_path, pattern):
File "/usr/lib/python2.7/unittest/loader.py", line 287, in _find_tests
for test in self._find_tests(full_path, pattern):
File "/usr/lib/python2.7/unittest/loader.py", line 267, in _find_tests
raise ImportError(msg % (mod_name, module_dir, expected_dir))
ImportError: 'test_entity' module incorrectly imported from '/home/annalist/anenv/local/lib/python2.7/site-packages/annalist_root/annalist/tests'. Expected '/home/annalist/anenv/lib/python2.7/site-packages/annalist_root/annalist/tests'. Is this module globally installed?
В тестовой версии отлично работают в среде разработки, и они также отлично работают при установке из дистрибутива источника в новую виртуальную среду на узле разработки MAcOS. Но когда я устанавливаю тот же пакет в новый virtualenv на хосте Ubuntu 14.04, тестовый бегун выходит из строя с указанным выше сообщением.
Проблемы возникли в утилите управления, которую я создал, которая вызывает некоторые функции django-admin
(а также некоторые другие вещи).
Веб-поиск выявил сообщения об ошибках с совместимостью virtualenv и posix, которые были рассмотрены относительно недавно (2013/14) в дистрибутивах Ubuntu, создав в виртуальной среде каталог local
, который, в свою очередь, содержит символические ссылки на каталоги, которые также доступный из каталога виртуальной среды верхнего уровня. Пути, указанные в сообщении об ошибке, соответствуют этим дорожкам с псевдонимом.
(Я публикую это как вопрос, чтобы опубликовать результаты и ответы из моих исследований в надежде, что это может быть полезно другим. Следовательно, я не пытаюсь дать подробное описание моего конкретного настройка программного обеспечения.)
Ответы
Ответ 1
Короткий ответ
Исправление в моем коде было использовать os.path.realpath
, чтобы получить каноническую версию установленного пути пакета и передать это значение в командной строке, которая вызывает утилиту django-admin
. В моем случае это выглядит примерно так:
approot = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
и
with ChangeCurrentDir(approot):
subprocess_command = (
"django-admin test --pythonpath=%s --settings=%s --top-level-directory=%s"%
(approot, settings_module_name, approot)
)
status = subprocess.call(subprocess_command.split())
(где ChangeCurrentDir
- обработчик контекста, который запускает закрытый код с указанным текущим рабочим каталогом).
Подробнее
Некоторые дальнейшие эксперименты показали, что я мог бы "исправить" проблему путем стратегической замены os.path.abspath
на os.path.realpath
в коде библиотеки Python и/или Django.
Основная проблема, которую я нашел, была в:
/usr/lib/python2.7/unittest/loader.py
В частности:
File "/usr/lib/python2.7/unittest/loader.py", line 267, in _find_tests
raise ImportError(msg % (mod_name, module_dir, expected_dir))
ImportError: 'test_entity' module incorrectly imported from ...
Код нарушения в loader.py
, предшествующий этому:
if realpath.lower() != fullpath_noext.lower():
module_dir = os.path.dirname(realpath)
mod_name = os.path.splitext(os.path.basename(full_path))[0]
expected_dir = os.path.dirname(full_path)
msg = ("%r module incorrectly imported from %r. Expected %r. "
"Is this module globally installed?")
raise ImportError(msg % (mod_name, module_dir, expected_dir))
Если я заменил оператор if
следующим образом:
if os.path.realpath(realpath).lower() != fullpath_noext.lower():
Тогда все будет счастливо. Это подтверждает, что это проблема с псевдонимом симлинк, поскольку os.path.realpath()
разрешает любые символические ссылки на базовый путь. Но это не решение для устанавливаемого программного пакета, поскольку оно включает в себя модификацию базовой установки Python. Поэтому, исследуя основную проблему, мне нужно что-то более доступное для атаки.
Следующий порт вызова был библиотекой тестового бегунка Django, которая установлена в виртуальной среде.
<base-env>/local/lib/python2.7/site-packages/django/test/runner.py
В частности, фокусировка на этой части трассировки стека:
File "/home/annalist/anenv/local/lib/python2.7/site-packages/django/test/runner.py", line 96, in build_suite
tests = self.test_loader.discover(start_dir=label, **kwargs)
Копаясь вокруг этого кода, я смог идентифицировать проблему, связанную с параметром label
, который по умолчанию имеет значение '.'
(т.е. текущий каталог). Здесь нет простого исправления, но это говорит о том, что проблема может быть связана с текущим каталогом и/или контуром, используемым при запуске django-admin
. Это привело к вышесказанному решению (которое может быть излишним - я не уверен, что параметр --pythonpath=
необходим, но он работает для меня).
Ответ 2
У меня была точно такая же проблема, и я не мог понять, что происходит. Наконец, это было глупо:
У меня был макет, похожий на этот:
my_app/
__init__.py
tests.py
tests/
__init__.py
test_foo.py
Проблема возникла благодаря наличию как модуля "tests.py", так и пакета "tests" в той же папке.
Просто удаление файла "tests.py" решило проблему для меня.
Надеюсь, что это поможет.