2

我已经在 中进行了一些单元测试mysite/vncbrowser/tests.py,并且可以使用以下命令运行它们:

cd mysite
python manage.py test vncbrowser

tests.py中,我使用以下命令导入模型类:

from vncbrowser.models import Project, Stack, Integer3D, Double3D

...并使用以下测试测试将 anInteger3D插入自定义字段类型:

class InsertionTest(TestCase):

    def test_stack_insertion(self):
        s = Stack()
        s.title = "Example Stack"
        s.image_base = "http://foo/bar/"
        s.dimension = Integer3D(x=2048, y=1536, z=460)
        s.resolution = Double3D(x=5.0001, y = 5.0002, z=9.0003)
        s.save()
        self.assertEqual(s.id, 1)

但是,当我使用 运行测试时python manage.py test vncbrowser,我发现源代码检查失败isinstance(value, Integer3D)models.py似乎在文件中,对(在该文件前面定义的)models.py的裸引用具有 full name ,而从测试传入的对象具有 full name 。Integer3Dvncbrowser.models.Integer3Dmysite.vncbrowser.models.Integer3D

models.py一些调试语句的相关代码是:

class Integer3D(object):
    [... elided ...]

class Integer3DField(models.Field):
    def to_python(self, value):
        a = Integer3D()
        print >> sys.stderr, "value is %s, of type %s" % (value, type(value))
        print >> sys.stderr, "but a new Integer3D instance is", type(a)
        if isinstance(value, Integer3D):
            print >> sys.stderr, "isinstance check worked"
            return value
        print >> sys.stderr, "isinstance check failed"

...产生此输出(为清楚起见,添加了一些换行符和空格):

value is <vncbrowser.models.Integer3D object at 0x22bbf90>, of type
      <class 'vncbrowser.models.Integer3D'>

but a new Integer3D instance is
      <class 'mysite.vncbrowser.models.Integer3D'>

isinstance check failed

我可以通过将导入更改tests.py为:

 from mysite.vncbrowser.models import Project, Stack, Integer3D, Double3D

...但我不明白为什么文件mysite中需要资格tests.py。在我的 django 源代码的其他地方似乎不需要它。我确定我遗漏了一些明显的东西,但也许有人可以解释一下?

(事实上​​,我什至不确定为什么from mysite....导入有效,因为如果我sys.path从该语句之前打印,它包含 path /home/mark/foo/mysite/,但不包含/home/mark/foo/。)

我当前的工作目录是/home/mark/foo/mysite/当我运行python manage.py test vncbrowser.


根据要求,我的项目布局如下:

 ── mysite
    ├── custom_postgresql_psycopg2
    │   ├── base.py
    │   └── __init__.py
    ├── __init__.py
    ├── manage.py
    ├── settings.py
    ├── urls.py
    └── vncbrowser
        ├── __init__.py
        ├── models.py
        ├── tables.sql
        ├── tests.py
        └── views.py

__init__.py上面列出的所有文件都是空的。我正在使用 Python 2.6.5 和 Django 1.3。我在 virtualenv 中使用 Python,如果我"\n".join(sys.path)在 tests.py 开始打印,我会得到:

/home/mark/foo/mysite
/home/mark/foo/env/lib/python2.6/site-packages/distribute-0.6.10-py2.6.egg
/home/mark/foo/env/lib/python2.6
/home/mark/foo/env/lib/python2.6/plat-linux2
/home/mark/foo/env/lib/python2.6/lib-tk
/home/mark/foo/env/lib/python2.6/lib-old
/home/mark/foo/env/lib/python2.6/lib-dynload
/usr/lib/python2.6
/usr/lib64/python2.6
/usr/lib/python2.6/plat-linux2
/usr/lib/python2.6/lib-tk
/usr/lib64/python2.6/lib-tk
/home/mark/foo/env/lib/python2.6/site-packages

更新:正如lbp的回答中所建议的,我尝试在tests.py的顶部添加以下内容:

import vncbrowser as vnc_one
import mysite.vncbrowser as vnc_two

print "vnc_one:", vnc_one.__file__
print "vnc_two:", vnc_two.__file__

...产生了输出:

vnc_one: /home/mark/foo/mysite/vncbrowser/__init__.pyc
vnc_two: /home/mark/foo/mysite/../mysite/vncbrowser/__init__.pyc
4

2 回答 2

2

你不必知道所有的 PYTHONPATH 就可以知道你真正想知道的一件事:另一个 vncbrowser是从哪里来的。您可以执行以下操作,而不是打印出 python 路径:

import vncbrowser as vnc_one
import mysite.vncbrowser as vnc_two

print vnc_one.__file__
print vnc_two.__file__

在那里你会在你的文件系统上看到两个不同的路径。然后你就可以开始找出原因了。

这只是一个疯狂的猜测,但我认为 vnc_one 安装在您的 python 路径中的某个位置,而 vnc_two 位于您的源代码中。(编辑:错误的猜测)

此外,随机评论:

此外,您可以通过使用使 tests.py 中的导入语句更简单

from models import ...

代替

from XXX.models import ...
于 2011-09-20T12:34:35.890 回答
0

lbp 的回答和评论帮助我理解和解决了问题,所以这就是我接受的答案,但我认为在另一个答案中解释我的困惑来源可能是值得的,因为这可能对其他人有所帮助。

当您运行时,它会在运行测试之前manage.py将站点目录(/home/mark/foo/mysite在我的示例中)添加到。sys.path因此,如果您随后使用Django 文档中建议tests.py的 import 行(在我的情况下)导入模型并随后创建 an 的实例,它将是 type ,因为假定包层次结构的根为从 中的每个条目开始。from vncbrowser.models import Integer3DInteger3Dvncbrowser.models.Integer3Dsys.path

然而,这个包的名称实际上是不正确的,因为应用程序目录嵌套在站点目录中(正如django-admin.pyDjango 教程所建议的那样),而且这两个都是 Python 包。这意味着该类的真实名称以及在 中有效的名称models.py是完全限定的mysite.vncbrowser.models.Integer3D

通常,这不会造成问题,因为如果你写得很好,isinstance 那么应该很少使用 of ,而鸭子类型意味着特定对象的类的全名的任何差异都应该是无关紧要的。但是,在编写自定义字段类型时,您必须区分该to_python方法是使用字符串还是对象调用的,并且文档建议使用isinstance该方法。

为了避免将来出现这种混淆,正如lbp在评论中所建议的那样,我现在将我的应用程序放在一个单独的(非包)目录中,以避免意外地使应用程序依赖于站点名称的风险——这可以将应用程序包嵌套在站点包中的奇怪的默认建议层次结构很容易发生。

于 2011-09-25T15:15:59.673 回答