代码之家  ›  专栏  ›  技术社区  ›  Xiong Chiamiov

通过ipython和伪控制台运行doctests

  •  1
  • Xiong Chiamiov  · 技术社区  · 15 年前

    我有一个相当基本的文档:

    class Foo():
        """
        >>> 3+2
        5
        """
    
    if __name__ in ("__main__", "__console__"):
        import doctest
        doctest.testmod(verbose=True)
    

    当直接通过python运行时,它会按预期工作。

    然而,在伊普敦,我得到

    1 items had no tests:
        __main__
    0 tests in 1 items.
    0 passed and 0 failed.
    Test passed.
    

    由于这是Django项目的一部分,需要访问所有适当的变量,并设置manage.py,因此我还可以通过使用code.interactionvconsole的修改命令来运行它,其中一个结果是 __name__ 设置为 __console__ '的。

    使用上面的代码,我得到的结果与使用IPython相同。我试着把最后一行改成:

     this = __import__(__name__)
     doctest.testmod(this, verbose=True)
    

    我得到一个重要的错误 γ-康索雷 我想这是有道理的。这对python或ipython都没有影响。

    所以,我希望能够通过这三种方法成功地运行doctests,尤其是InteractiveConsole 1,因为我希望很快就需要django pony魔法。

    我只是想澄清一下,这正是我所期待的:

    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.
    
    2 回复  |  直到 15 年前
        1
  •  1
  •   codeape    15 年前

    以下工作:

    $ 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__ 模块。
    • 当ipython执行在命令行上传递给它的文件时, 圣米尼 模块是IPython的 圣米尼 不是你的模块。所以doctest尝试在ipython的条目脚本中执行doctest。

    以下是可行的,但感觉有点奇怪:

    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
    

    该脚本创建将执行的python脚本 %run argname 在IPython。

    例子:

    $ ./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]:
    
        2
  •  8
  •   Alex Martelli    15 年前

    根本问题是 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__ 又不起作用了,因为把它设定为一个你所认为的整体 圣米尼 实际上不是把它放在 第二节 被恢复的实际对象的 m = sys.modules['__main__'] 而后者正是 博士学位 正在内部使用(实际上它使用 sys.modules.get 但是这里不需要额外的预防措施,因为我们知道 圣米尼 存在于 sys.modules …这不是你期望的目标!-)

    另外,只需设置 m.__test__ = globals() 直接也不起作用,原因不同: 博士学位 检查中的值 _测试__ 是字符串、函数、类或模块,如果没有某些选择,则无法保证 globals() 将满足该条件(事实上不会)。这里我只选择类,如果您还需要函数或其他什么,您可以使用 or if genexp中的子句 dict 打电话。

    我不知道你是如何运行一个可以执行你的脚本的django shell的(我相信 python manage.py shell 不接受争论,你一定在做别的事情,我也猜不出到底是什么!-,但是类似的方法应该会有所帮助(无论您的django shell是使用ipython(可用时的默认值)还是普通的python):适当地设置 _测试__ 在你获得的对象中 sys.modules['__main__'] (或) __console__ ,如果这就是您要传递给doctest.testmod的内容,我想)应该可以工作,因为它模仿doctest在内部执行的操作来定位测试字符串。

    最后,对设计、建筑、简洁、透明和“黑色魔法”的哲学思考是:

    所有这些努力基本上都是为了打败伊普利森(也许还有姜戈,尽管这可能只是把这部分委托给伊普利森)为你的“方便”而为你所做的“黑色魔法”…任何时候,当两个框架(或更多;-)各自独立地执行各自的黑魔法品牌时,互操作性可能突然需要大量的努力,变得非常不方便;-)。

    我不是说也可以提供同样的便利(任何一个或多个伊比顿,姜戈和/或教义) 没有 黑魔法、内省、假模块等等;每一个框架的设计者和维护者都是优秀的工程师,我希望他们已经彻底完成了他们的作业,并且只执行了最少数量的黑魔法,这对于提供他们所需要的用户便利是必不可少的。然而,即使在这种情况下,“黑魔法”也会突然从一个方便的梦想变成一个调试的噩梦,只要你想做一些超出框架作者设想范围的事情。

    好吧,也许在这种情况下,这不是一场噩梦,但我确实注意到这个问题已经打开了一段时间,即使是在赏金的诱惑下,它还没有得到很多答案——尽管你现在有两个答案可供选择,我的答案是 _测试__ 博士的特色,@codeape使用了 __IP.magic_run 铁蟒的特征。我更喜欢我的,因为它不依赖于任何内部的或无文件的东西-- _测试__ 是doctest的文档功能,而 __IP 在这两个即将出现的前导下划线下,对我大喊“内心深处,不要触碰”—……如果它在下一个发布点中断,我一点也不惊讶。不过,关于品味的问题——这个答案可能被认为更“方便”。

    但是,这正是我的观点:在放弃简单性、透明性和/或避免内部/未记录/不稳定的功能方面,便利可能会付出巨大的代价;因此,作为我们所有人的一个教训,我们可以摆脱的最不黑的魔法&C(即使是在这里或那里放弃便利的代价),越快乐。从长远来看,我们都会很高兴(我们会让其他需要在未来充分利用我们当前工作的开发人员感到高兴)。