当Python处于优化模式时,如何进行单元测试以确定某些行为有何不同?

时间:2016-02-06 19:05:54

标签: python unit-testing debugging

Python允许您通过传递-O选项以“优化模式”运行脚本。如果我将此脚本保存为“assert.py”:

assert False
print("Hello")

然后这两次Python调用会产生不同的输出(一个打印异常消息和堆栈跟踪,另一个打招呼):

python -m assert
python -O -m assert

Python脚本的作者可以通过检查全局名称__debug__的值来确定Python是否处于优化模式。这允许我们根据我们是否以优化模式运行来执行不同的操作。

如果Python处于优化模式,假设我想做一件事,如果不是,那么我想做另一件事。这很容易 - 我们可以使用if __debug__:。但现在假设我想要对每种情况下的行为进行单元测试是正确的。我应该怎么做呢?

我想到我可以设置__debug__的值,但是你不允许这样做:

>>> __debug__ = False
  File "<stdin>", line 1
SyntaxError: assignment to keyword
>>>

1 个答案:

答案 0 :(得分:2)

考虑以下代码:

assert 123

if __debug__:
    do_something()

优化的字节码将为空。在字节码编译期间评估assert和裸if __debug__语句,而不是在运行时。因此,即使您成功更改__debug__(例如使用setattr(builtins, '__debug__', True)),您仍然无法执行该代码。

唯一的方法是运行测试套件两次,首先是-O,然后是-O。显然,您可以自动执行此部件,您无需手动执行此操作。

为了完整性:

$ python3
>>> dis.dis('assert 123')
  1           0 LOAD_CONST               0 (123)
              3 POP_JUMP_IF_TRUE        12
              6 LOAD_GLOBAL              0 (AssertionError)
              9 RAISE_VARARGS            1
        >>   12 LOAD_CONST               1 (None)
             15 RETURN_VALUE
>>> dis.dis('if __debug__: do_something()')
  1           0 LOAD_NAME                0 (do_something)
              3 CALL_FUNCTION            0 (0 positional, 0 keyword pair)
              6 POP_TOP
              7 LOAD_CONST               0 (None)
             10 RETURN_VALUE

$ python3 -O
>>> dis.dis('assert 123')
  1           0 LOAD_CONST               0 (None)
              3 RETURN_VALUE
>>> dis.dis('if __debug__: do_something()')
  1           0 LOAD_CONST               0 (None)
              3 RETURN_VALUE

请注意,只会优化裸if __debug__个语句。如果使用复杂条件,它们将出现在字节码中:

$ python3 -O
>>> dis.dis('if __debug__ and something_else: do_something()')
  1           0 LOAD_NAME                0 (__debug__)
              3 POP_JUMP_IF_FALSE       22
              6 LOAD_NAME                1 (something_else)
              9 POP_JUMP_IF_FALSE       22
             12 LOAD_NAME                2 (do_something)
             15 CALL_FUNCTION            0 (0 positional, 0 keyword pair)
             18 POP_TOP
             19 JUMP_FORWARD             0 (to 22)
        >>   22 LOAD_CONST               0 (None)
             25 RETURN_VALUE