我有一个相当基本的doctestable文件:
class Foo():
"""
>>> 3+2
5
"""
if __name__ in ("__main__", "__console__"):
import doctest
doctest.testmod(verbose=True)
直接通过python运行时可以正常工作。
然而,在iPython中,我得到了
1 items had no tests:
__main__
0 tests in 1 items.
0 passed and 0 failed.
Test passed.
由于这是Django项目的一部分,并且需要访问所有适当的变量并且manage.py设置,我还可以通过修改后的命令运行它,该命令使用code.InteractiveConsole,其中一个结果__name__
设置为“__console__
”。
使用上面的代码,我得到的结果与iPython相同。我尝试将最后一行更改为:
this = __import__(__name__)
doctest.testmod(this, verbose=True)
我在__console__
上得到一个ImportError,这是有意义的,我想。这对python或ipython都没有影响。
所以,我希望能够通过所有这三种方法成功运行doctests,尤其是InteractiveConsole方法,因为我希望很快就会需要Django pony magic。
为了澄清,这正是我所期待的:
Trying:
3+2
Expecting:
5
ok
1 items had no tests:
__main__
1 items passed all tests:
1 tests in __main__.Foo
1 tests in 2 items.
1 passed and 0 failed.
Test passed.
答案 0 :(得分:8)
根本问题是ipython
使用__main__
(通过其自己的FakeModule
模块)播放奇怪的技巧,以便在doctest
时反省“涉嫌模块” “通过其__dict__
,Foo
NOT - 所以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__
的{{1}}中由m = sys.modules['__main__']
恢复的实际对象,后者恰好是内部使用的doctest
表达式(实际上它使用sys.modules.get
,但由于我们知道这一点,因此不需要额外的预防措施__main__
中存在sys.modules
...它不是您期望的对象! - 。)。
另外,直接设置m.__test__ = globals()
也不起作用,原因不同:doctest
检查__test__
中的值是字符串,函数,类还是模块,没有有些选择你无法保证globals()
能满足这个条件(事实上它不会)。在这里,我只选择课程,如果您还想要函数或其他什么,可以在or
调用中的genexp的if
子句中使用dict
。
我不确切知道你是如何运行能够执行你的脚本的Django shell(因为我相信python manage.py shell
不接受参数,你必须做其他事情,我不能猜测究竟是什么! - ),但类似的方法应该有所帮助(无论你的Django shell是使用ipython,默认是可用的,还是普通的Python):在你获得的对象__test__
中适当地设置sys.modules['__main__']
(或者__console__
,如果那就是你传递给doctest.testmod的东西,我想)应该有效,因为它模仿doctest将在内部做什么来找到你的测试字符串。
并且,总而言之,对设计,建筑,简洁,透明度和“黑魔法”的哲学反思......:
所有这些努力基本上都是为了击败ipython(也许是Django,虽然它可能只是将那部分委托给ipython)的“黑魔法”所需要的,为你的“方便”代表你做...两个框架(或更多;-)独立完成每个自己的黑魔法品牌的时间,互操作性可能突然需要大量的努力并成为任何方便的东西; - )。
我并不是说可以提供同样的便利(ipython,django和/或doctests中的任何一个或多个)没有黑魔法,内省,虚假模块等等;每个框架的设计师和维护者都是精湛的工程师,我希望他们能够彻底完成他们的作业,并且只执行最少量的黑魔法,这对于提供他们认为需要的用户便利性是不可或缺的。尽管如此,即使在这样的情况下,只要你想要在框架作者构思的范围之外做一些事情,“黑魔法”突然从一个方便的梦想转变为调试的噩梦。
好吧,也许在这种情况下不是一场噩梦,但我确实注意到这个问题已经开放了一段时间,即使有了赏金的诱惑,它还没有得到很多答案 - 尽管你现在有两个使用docpy的__test__
特殊功能,@ codeape使用铁心的奇特__IP.magic_run
功能来挑选我的答案。我更喜欢我的,因为它不依赖于内部或无证的任何东西 - __test__
是doctest的记录特征,而__IP
,这两个迫在眉睫的领先下划线,尖叫“深入的内部,不要触摸“对我来说; - )......如果它在下一个发布时断开,我就不会感到惊讶。品味问题 - 答案可能被认为更“方便”。
但是,这正是我的观点:在放弃简单性,透明度和/或避免内部/无证/不稳定特征方面,便利可能会付出巨大代价;所以,作为我们所有人的一课,我们可以逃脱最少的黑魔法(即使以放弃方便ε的方式付出代价),从长远来看,我们都会更幸福(并且我们会让其他需要利用我们当前努力的开发人员更快乐。)
答案 1 :(得分:1)
以下作品:
$ ipython
...
In [1]: %run file.py
Trying:
3+2
Expecting:
5
ok
1 items had no tests:
__main__
1 items passed all tests:
1 tests in __main__.Foo
1 tests in 2 items.
1 passed and 0 failed.
Test passed.
In [2]:
我不知道为什么ipython file.py
不起作用。但上述至少是一种解决方法。
修改强>
我找到了它无效的原因。这很简单:
doctest.testmod()
中指定要测试的模块,则会假定您要测试__main__
模块。__main__
模块是IPython的__main__
,而不是你的模块。因此,doctest尝试在IPython的入门脚本中执行doctests。以下作品,但感觉有点奇怪:
if __name__ == '__main__':
import doctest
import the_current_module
doctest.testmod(the_current_module)
所以基本上模块导入自己(这是“感觉有点奇怪”部分)。但它的确有效。我不喜欢abt的东西。这种方法是每个模块都需要在源代码中包含自己的名称。
编辑2:
以下脚本ipython_doctest
使ipython的行为符合您的要求:
#! /usr/bin/env bash
echo "__IP.magic_run(\"$1\")" > __ipython_run.py
ipython __ipython_run.py
该脚本创建一个将在IPython中执行%run argname
的python脚本。
示例:
$ ./ipython_doctest file.py
Trying:
3+2
Expecting:
5
ok
1 items had no tests:
__main__
1 items passed all tests:
1 tests in __main__.Foo
1 tests in 2 items.
1 passed and 0 failed.
Test passed.
Python 2.5 (r25:51908, Mar 7 2008, 03:27:42)
Type "copyright", "credits" or "license" for more information.
IPython 0.9.1 -- An enhanced Interactive Python.
? -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help -> Python's own help system.
object? -> Details about 'object'. ?object also works, ?? prints more.
In [1]: