Подтвердить что ты не робот

Тест-драйв 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, который, в свою очередь, содержит символические ссылки на каталоги, которые также доступный из каталога виртуальной среды верхнего уровня. Пути, указанные в сообщении об ошибке, соответствуют этим дорожкам с псевдонимом.

(Я публикую это как вопрос, чтобы опубликовать результаты и ответы из моих исследований в надежде, что это может быть полезно другим. Следовательно, я не пытаюсь дать подробное описание моего конкретного настройка программного обеспечения.)

4b9b3361

Ответ 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" решило проблему для меня.

Надеюсь, что это поможет.