简体   繁体   English

Django测试运行器在Ubuntu上的virtualenv失败

[英]Django test runner fails in virtualenv on Ubuntu

I've been struggling with a problem with the Django test runner installed in a Python virtualenv on Ubuntu 14.04. 我一直在努力解决在Ubuntu 14.04上安装在Python virtualenv中的Django测试运行器的问题。 The same software runs fine on MacOS, and I think it was fine on an earlier version of Ubuntu. 相同的软件在MacOS上运行正常,我认为在早期版本的Ubuntu上运行良好。

The failure message is: 失败消息是:

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?

And the full stack trace from the error is: 并且错误的完整堆栈跟踪是:

  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?

The test cases run fine in the development environment, and they also run fine when installed from a source distribution kit into a new virtualenv environment on the MAcOS development host. 测试用例在开发环境中运行良好,当从源分发工具包安装到MAcOS开发主机上的新virtualenv环境时,它们也运行良好。 But when I install the same package into a new virtualenv on an Ubuntu 14.04 host, the test runner fails with the above message. 但是当我在Ubuntu 14.04主机上将相同的软件包安装到新的virtualenv时,测试运行器会因上述消息而失败。

The problems came up in a management utility I created that invokes some functions of django-admin (as well as some other stuff). 我创建的管理实用程序出现了问题,它调用了django-admin一些功能(以及其他一些东西)。

Web searches revealed reports of bugs with virtualenv and posix compatibility, which have been addressed relatively recently (2013/14) in Ubuntu distributions by creating a local directory in the virtual environment which in turn contains symlinks to directories that are also accessible from the top-level virtual environment directory. 网络搜索显示有关virtualenv和posix兼容性的错误的报告,这些错误已经在Ubuntu发行版中相对最近(2013/14)通过在虚拟环境中创建local目录而得到解决,该目录又包含对也可从顶部访问的目录的符号链接。级虚拟环境目录。 The paths shown in the error message correspond to these aliased directory paths. 错误消息中显示的路径对应于这些别名目录路径。

(I'm posting this as a question so I can post the outcome and answer from my investigations, in the hope that it might be useful to others. Hence, I'm not trying to give a detailed description of my particular software setup.) (我将此作为一个问题发布,因此我可以发布结果并从我的调查中回答,希望它对其他人有用。因此,我不打算详细描述我的特定软件设置。 )

I had exactly the same problem and couldn't figure out what was going on. 我有完全相同的问题,无法弄清楚发生了什么。 Finally it was a stupid thing: 最后这是一个愚蠢的事情:

I had a layout similar to this one: 我有一个类似于这个的布局:

my_app/
    __init__.py
    tests.py
    tests/
        __init__.py
       test_foo.py

The problem was generated by having both a "tests.py" module and a "tests" package in the same folder. 通过在同一文件夹中同时包含“tests.py”模块和“tests”包来生成问题。

Just deleting the "tests.py" file solved the problem for me. 只是删除“tests.py”文件解决了我的问题。

Hope it helps. 希望能帮助到你。

Short answer 简短的回答

The fix in my code was to use os.path.realpath to get a canonicalized version of the installed package path, and pass this value on the command line that invokes the django-admin utility. 我的代码中的修复是使用os.path.realpath来获取已安装的包路径的规范化版本,并在调用django-admin实用程序的命令行上传递此值。 In my case it looks something like this: 在我看来,它看起来像这样:

approot = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))

and: 和:

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())

(where ChangeCurrentDir is a context handler that runs the enclosed code with a specified current working directory). (其中ChangeCurrentDir是一个上下文处理程序,它使用指定的当前工作目录运行附带的代码)。

More details 更多细节

Some further experimentation showed that I could "fix" the problem by strategic replacement of os.path.abspath with os.path.realpath in the Python and/or Django library code. 一些进一步的实验表明,我可以通过在Python和/或Django库代码中用os.path.realpath战略性地替换os.path.abspath来“修复”这个问题。

The core problem I found was in: 我发现的核心问题是:

/usr/lib/python2.7/unittest/loader.py

Specifically: 特别:

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 ...

The offending code in loader.py leading up to this is: 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 I replace the if statement with this: 如果我用这个替换if语句:

        if os.path.realpath(realpath).lower() != fullpath_noext.lower():

Then everything is happy. 一切都很开心。 This confirms that it is a symlink aliasing problem, as os.path.realpath() resolves any symlinks to the underlying path. 这确认了它是符号链接别名问题,因为os.path.realpath()将任何符号链接解析为基础路径。 But this is not a solution for an installable software package, as it involves modifying the underlying Python installation. 但这不是可安装软件包的解决方案,因为它涉及修改底层Python安装。 So, having probed the underlying problem, I need for something more accessible to attack. 因此,在探究了潜在的问题之后,我需要一些更易于攻击的东西。

Next port of call was the Django test runner library, which is installed in the virtual environment. 下一个停靠点是Django测试运行器库,它安装在虚拟环境中。

<base-env>/local/lib/python2.7/site-packages/django/test/runner.py

In particular, focusing on this part of the stack trace: 特别是,关注堆栈跟踪的这一部分:

  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)

Digging around this code, I was able to identify the problem was related to the label parameter, which defaults to '.' 挖掘这段代码,我能够确定问题与label参数有关,默认为'.' (ie current directory). (即当前目录)。 No easy fix is apparent here, but it suggests the problem may be related to the current directory and/or path used when running django-admin . 这里没有明显的简单修复,但它表明问题可能与运行django-admin时使用的当前目录和/或路径有关。 This led to the above solution (which may be overkill - I'm not sure that the --pythonpath= option is needed, but it is working for me). 这导致了上面的解决方案(这可能是矫枉过正 - 我不确定是否需要--pythonpath=选项,但它对我--pythonpath= )。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM