В Django, почему мне нужно добавить имя сайта к импорту в tests.py? - PullRequest
2 голосов
/ 20 сентября 2011

Я поместил некоторые модульные тесты в mysite/vncbrowser/tests.py, и я могу запустить их с:

cd mysite
python manage.py test vncbrowser

В tests.py, я импортирую классы моделей с помощью:

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

... и проверить вставку Integer3D в пользовательский тип поля с помощью теста вроде:

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 пустая ссылка на Integer3D (определенная ранее в этом файле) имеет полное имя vncbrowser.models.Integer3D, в то время как объект, переданный из теста, имеет полное имя mysite.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 из простоперед этим оператором он включает путь /home/mark/foo/mysite/, но не /home/mark/foo/.)

Мой текущий рабочий каталог /home/mark/foo/mysite/, когда я запускаю python manage.py test vncbrowser.


AsЗапрошенный макет моего проекта выглядит следующим образом:

 ── 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.Я использую Python в virtualenv, и если я печатаю "\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's answer , я попытался добавить следующее в верхней части 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

Ответы [ 2 ]

2 голосов
/ 20 сентября 2011

Вам не нужно знать всю вашу PYTHONPATH, чтобы узнать одну вещь, которую вы на самом деле хотите знать: откуда это other 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 ...
0 голосов
/ 25 сентября 2011

ответ фунта и комментарии помогли мне понять и решить проблему, поэтому я принимаю этот ответ, но я подумал, что, возможно, стоит объяснить в другом ответе, откуда пришли источники моего замешательства , поскольку это может помочь другим людям.

Когда вы запускаете manage.py, он добавляет каталог сайта (/home/mark/foo/mysite, в моем примере) в sys.path перед запуском тестов. Итак, если вы затем импортируете модель из tests.py, используя строку импорта , предложенную в документации Django (в моем случае это будет from vncbrowser.models import Integer3D), и впоследствии создадите экземпляр Integer3D, она будет иметь тип vncbrowser.models.Integer3D, поскольку предполагается, что корень иерархии пакетов начинается с каждой записи в sys.path.

Однако это имя пакета на самом деле неверно, поскольку каталог приложения вложен в каталог сайта (как это предлагается в django-admin.py и учебном руководстве по Django), и оба они являются пакетами Python. Это означает, что настоящее имя класса и то, которое действительно в models.py, является полностью квалифицированным mysite.vncbrowser.models.Integer3D.

Как правило, это не создает проблемы, поскольку, если вы хорошо пишете на Python, использование isinstance должно быть редким , а ввод с утиной означает, что любая разница в полном имени класс конкретных объектов не должен иметь значения. Однако при написании пользовательских типов полей вы должны различать, был ли вызван метод to_python со строкой или объектом, и документация предлагает использовать для этого isinstance.

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

...