Пакеты Python?

Хорошо, я думаю, что бы я ни делал неправильно, это, вероятно, ослепительно очевидно, но я не могу понять это. Я читал и перечитывал раздел учебника по пакетам, и единственное, что я могу понять, это то, что это не сработает, потому что я выполняю его напрямую. Вот настройка каталога:

eulerproject/
  __init__.py
  euler1.py
  euler2.py
  ...
  eulern.py
  tests/
    __init__.py
    testeulern.py

Вот содержимое testeuler12.py (первый тестовый модуль, который я написал):

import unittest
from .. import euler12

class Euler12UnitTests(unittest.TestCase):


    def testtriangle(self):
        """
        Ensure that the triangle number generator returns the first 10
        triangle numbers.

        """
        self.seq = [1,3,6,10,15,21,28,36,45,55]
        self.generator = euler12.trianglegenerator()
        self.results = []
        while len(self.results) != 10:
            self.results.append(self.generator.next())
        self.assertEqual(self.seq, self.results)

    def testdivisors(self):
        """
        Ensure that the divisors function can properly factor the number 28.

        """
        self.number = 28
        self.answer = [1,2,4,7,14,28]
        self.assertEqual(self.answer, euler12.divisors(self.number))


if __name__ == '__main__':

    unittest.main()

Теперь, когда я выполняю это из IDLE и из командной строки, находясь в каталоге, я получаю следующую ошибку:

Traceback (most recent call last):
  File "C:\Documents and Settings\jbennet\My Documents\Python\eulerproject\tests\testeuler12.py", line 2, in <module>
    from .. import euler12
ValueError: Attempted relative import in non-package

Я думаю, проблема в том, что, поскольку я запускаю его напрямую, я не могу выполнять относительный импорт (потому что __name__ меняется, и мое смутное понимание описания пакетов состоит в том, что __name__ является частью того, как он сообщает, в каком пакете он находится), но в таком случае, что вы, ребята, предлагаете, как импортировать «производственный» код, хранящийся на 1 уровень выше из тестового кода?


person Jonathanb    schedule 27.08.2009    source источник
comment
Просто выберите его и нажмите кнопку примера кода. Или заверните его в ``   -  person Nadia Alramli    schedule 27.08.2009


Ответы (2)


Как правило, у вас будет каталог, имя которого является именем вашего пакета, где-то в вашем PYTHONPATH. Например:

eulerproject/
    euler/
        __init__.py
        euler1.py
        ...
        tests/
            ...
    setup.py

Затем вы можете либо установить это для всей системы, либо обязательно установить PYTHONPATH=/path/to/eulerproject/:$PYTHONPATH при вызове вашего скрипта.

Абсолютный импорт, подобный этому, будет работать:

from euler import euler1

Изменить:

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

Таким образом, тестовая система, такая как nose, упомянутая в другом ответе, работает, потому что она импортирует пакеты, а не запускает их из командной строки.

Если вы хотите делать что-то вручную, ваш исполняемый скрипт должен находиться вне иерархии пакетов, например:

eulerproject/
    runtests.py
    euler/
        __init__.py
        euler1.py
        ...
        tests/
            __init__.py
           testeulern.py

Теперь runtests.py может делать from euler.tests.testeulern import TestCase, а testeulern.py может делать from .. import euler1.

person Jason S    schedule 27.08.2009
comment
Да, но тогда ваш проект больше не использует преимущества относительного импорта. Преимущество относительного импорта в том, что он делает ваш пакет автономным. Он не зависит от имени основной папки и не может неправильно импортировать модуль, например, из устаревшей версии пакета, спрятанной где-то в PYTHONPATH. - person Virgil Dupras; 27.08.2009
comment
Итак, как мне установить PYTHONPATH перед вызовом скрипта? Например, если бы я хотел иметь возможность запускать это из двух разных мест на одном компьютере? ps: я также изучаю, как обрабатывать клонирование/слияние с mercurial, поэтому это в двух местах. - person Jonathanb; 27.08.2009
comment
Добавлено примечание о том, как может работать относительный импорт, если модуль импортируется из сценария, расположенного вне иерархии пакетов. В противном случае используйте тестовый жгут. Установка переменных среды зависит от того, с чем вы работаете. Если вы используете bash, просто введите PYTHONPATH=foo python scriptname.py - person Jason S; 27.08.2009
comment
Превосходно! Этот ответ после редактирования делает именно то, что я хочу. Я изменил иерархию и сегодня вечером собираюсь написать сценарий euler.py в корневом каталоге, который примет --test для включения модульных тестов. Это позволяет мне сохранять относительный импорт, использовать структуру пакета и экспериментировать с автоматическим тестированием моих сборок. - person Jonathanb; 27.08.2009

У меня такая же проблема. Теперь я использую nose для запуска своих тестов, и относительный импорт обрабатывается правильно.

Да, весь этот относительный импорт сбивает с толку.

person Virgil Dupras    schedule 27.08.2009
comment
Я принимаю другой ответ только потому, что он позволяет мне делать то, что я пытался сделать с относительным импортом. Тем не менее, я также загружаю Nose, чтобы использовать его в качестве своего тестового набора, поскольку он выглядит гораздо более мощным, чем все, что я мог бы собрать самостоятельно. - person Jonathanb; 27.08.2009