Основная проблема заключается в том, что ipython
играет странные трюки с __main__
(через свой собственный FakeModule
модуль), так что к тому времени doctest
анализирует этот "предполагаемый модуль" через __dict__
, Foo
есть НЕ там - так что doctest не вернется в него.
Вот одно из решений:
class Foo():
"""
>>> 3+2
5
"""
if __name__ in ("__main__", "__console__"):
import doctest, inspect, sys
m = sys.modules['__main__']
m.__test__ = dict((n,v) for (n,v) in globals().items()
if inspect.isclass(v))
doctest.testmod(verbose=True)
Это производит в соответствии с просьбой:
$ ipython dot.py
Trying:
3+2
Expecting:
5
ok
1 items had no tests:
__main__
1 items passed all tests:
1 tests in __main__.__test__.Foo
1 tests in 2 items.
1 passed and 0 failed.
Test passed.
Python 2.5.1 (r251:54863, Feb 6 2009, 19:02:12)
[[ snip snip ]]
In [1]:
Простая установка глобального __test__
не работает, опять же, потому что установка его в глобальное значение, о котором вы думаете __main__
, на самом деле НЕ помещает его в __dict__
реального объекта, который восстанавливается m = sys.modules['__main__']
, и последнее является именно тем выражением, которое doctest
использует для внутреннего использования (на самом деле оно использует sys.modules.get
, но здесь нет необходимости в дополнительных мерах предосторожности, поскольку мы знаем, что __main__
существует в sys.modules
... это просто НЕ тот объект, который вы ожидаете! -).
Кроме того, просто установка m.__test__ = globals()
напрямую не работает, по другой причине: doctest
проверяет, что значения в __test__
являются строками, функциями, классами или модулями, и без некоторого выбора вы не можете гарантировать, что globals()
удовлетворит это условие (на самом деле это не так). Здесь я выбираю только классы, если вам также нужны функции или еще что-то, вы можете использовать or
в предложении if
в genexp в вызове dict
.
Я не знаю точно, как у вас работает оболочка Django, которая способна выполнить ваш скрипт (как я полагаю python manage.py shell
не принимает аргументы, вы должны делать что-то еще, и я не могу точно догадаться что! -), но подобный подход должен помочь (независимо от того, использует ли ваша оболочка Django ipython, значение по умолчанию, если оно доступно, или обычный Python): соответственно установите __test__
в объекте, который вы получите как sys.modules['__main__']
(или __console__
, если это то, что вы затем передаете в doctest.testmod, я думаю) должен работать, так как он имитирует то, что doctest будет делать внутри, чтобы найти ваши тестовые строки.
И, в заключение, философское осмысление дизайна, архитектуры, простоты, прозрачности и "черной магии" ...:
Все эти усилия - в основном то, что необходимо для победы над "черной магией", которую ipython (и, возможно, Django, хотя он может просто делегировать эту часть ipython) от вашего имени для вашего "удобства" ... любой В то время, когда две структуры (или больше ;-) независимо друг от друга делают каждый свой собственный бренд чёрной магии, взаимодействие может внезапно потребовать значительных усилий и стать чем угодно, НО удобным; -).
Я не говорю, что такое же удобство могло бы быть предоставлено (одним или несколькими из ipython, django и / или doctests) без чёрной магии, самоанализа, поддельных модулей и т. Д .; дизайнеры и сопровождающие каждого из этих фреймворков - превосходные инженеры, и я ожидаю, что они тщательно выполнили свою домашнюю работу и выполнили только минимальное количество черной магии, которое необходимо для обеспечения удобства пользователя, которое, по их мнению, им было необходимо. Тем не менее, даже в такой ситуации «черная магия» внезапно превращается из удобного сна в кошмар отладки, как только вы захотите сделать что-то хотя бы немного за пределами того, что задумал автор фреймворка.
Хорошо, может быть, в этом случае это не совсем кошмар, но я заметил, что этот вопрос был открыт некоторое время, и даже с приманкой щедрости он еще не получил много ответов - хотя у вас теперь есть два ответы, которые можно выбрать, мои используют специальную функцию __test__
doctest, @ codeape использует особую функцию __IP.magic_run
ironpython. Я предпочитаю мой, потому что он не опирается на что-то внутреннее или недокументированное - __test__
- документированная особенность doctest, в то время как __IP
, с этими двумя надвигающимися подчеркиваниями, кричит мне «глубокие внутренние органы, не трогайте»; -) ... если он сломается в следующем выпуске, я бы совсем не удивился. Тем не менее, дело вкуса - этот ответ, возможно, можно считать более «удобным».
Но это именно моя точка зрения: удобство может обойтись огромной ценой с точки зрения отказа от простоты, прозрачности и / или избежания внутренних / недокументированных / нестабильных функций; так что, как урок для всех нас, наименьшая чёрная магия, с которой мы можем справиться (даже ценой отказа от эпсилона удобства здесь и там), тем счастливее мы все будем в конечном итоге (и тем счастливее мы сделаем других разработчиков, которым необходимо использовать наши текущие усилия в будущем).