Python断言 - 改进了对失败的反省?

时间:2009-08-20 20:25:24

标签: python debugging assert syntactic-sugar

这是一个相当无用的断言错误;它没有告诉所涉及的表达式的值(假设使用的常量实际上是变量名):

$ python -c "assert 6-(3*2)"
[...]
AssertionError

Python中有更好的assert实现更加花哨吗?它不得在执行时引入额外的开销(断言失败时除外)..如果使用-O标志,则必须关闭。

编辑:我知道assert的第二个参数是一个字符串。我不想写一个..因为它是在被断言的表达式中编码的。干(不要重复自己)。

7 个答案:

答案 0 :(得分:10)

将您的函数安装为sys.excepthook - 请参阅the docs。如果第二个参数是AssertionError,你的函数可以反省你内心的内容;特别是,通过第三个参数,回溯,它可以获得断言失败的帧和确切位置,通过源或字节码获得失败的异常,所有相关变量的值等等。模块inspect帮助

完全开展这项工作是一项相当大的工作,但是根据您在编写assert的方式中愿意接受的约束,它可以大大减轻(例如将它们限制为仅限于本地或全局变量使内省比闭包的非局部变量更容易,等等。

答案 1 :(得分:7)

您可以将消息附加到assert

assert 6-(3*2), "always fails"

该消息也可以动态构建:

assert x != 0, "x is not equal to zero (%d)" % x

有关详细信息,请参阅Python文档中的The assert statement

答案 2 :(得分:7)

由于@Mark Rushakoff said nose可以评估失败的断言。它也适用于标准assert

# test_error_reporting.py
def test():
    a,b,c = 6, 2, 3
    assert a - b*c

nosetests'帮助:

$ nosetests --help|grep -B2 assert
  -d, --detailed-errors, --failure-detail
                        Add detail to error output by attempting to evaluate
                        failed asserts [NOSE_DETAILED_ERRORS]

示例:

$ nosetests -d
F
======================================================================
FAIL: test_error_reporting.test
----------------------------------------------------------------------
Traceback (most recent call last):
  File "..snip../site-packages/nose/case.py", line 183, in runTest
    self.test(*self.arg)
  File "..snip../test_error_reporting.py", line 3, in test
    assert a - b*c
AssertionError:
    6,2,3 = 6, 2, 3
>>  assert 6 - 2*3


----------------------------------------------------------------------
Ran 1 test in 0.089s

FAILED (failures=1)

答案 3 :(得分:4)

The nose testing suite applies introspection to asserts

但是,AFAICT,您必须调用他们的断言才能获得内省:

import nose
def test1():
    nose.tools.assert_equal(6, 5+2)

结果

C:\temp\py>C:\Python26\Scripts\nosetests.exe -d test.py
F
======================================================================
FAIL: test.test1
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\Python26\lib\site-packages\nose-0.11.1-py2.6.egg\nose\case.py", line
183, in runTest
    self.test(*self.arg)
  File "C:\temp\py\test.py", line 3, in test1
    nose.tools.assert_equal(6, 5+2)
AssertionError: 6 != 7
>>  raise self.failureException, \
          (None or '%r != %r' % (6, 7))

注意那里的AssertionError。当我的行只是assert 6 == 5+2时,我会得到:

C:\temp\py>C:\Python26\Scripts\nosetests.exe -d test.py
F
======================================================================
FAIL: test.test1
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\Python26\lib\site-packages\nose-0.11.1-py2.6.egg\nose\case.py", line
183, in runTest
    self.test(*self.arg)
  File "C:\temp\py\test.py", line 2, in test1
    assert 6 == 5 + 2
AssertionError:
>>  assert 6 == 5 + 2

此外,我不确定他们的断言是否会被-O跳过,但这将是一个非常快速的检查。

答案 4 :(得分:0)

在断言中添加一条消息,如果断言失败,将显示该消息:

$ python -c "assert 6-(3*2), '6-(3*2)'"
Traceback (most recent call last):
  File "<string>", line 1, in <module>
AssertionError: 6-(3*2)

我能想到的唯一方法就是在过程调用中包含断言,然后检查堆栈以获取该行的源代码。遗憾的是,附加调用会在测试中引入开销,并且不会被-O禁用。

答案 5 :(得分:0)

听起来你真正想做的就是在assert之前设置一个调试器断点,并根据自己喜欢的方式从你喜欢的调试器中进行检查。

答案 6 :(得分:0)

我编写了sys.excepthook的替代品(对于任何未处理的异常调用),这比标准的更加有点花哨。它将分析发生异常的行并打印该行中引用的所有变量(它不会打印所有局部变量,因为这可能是太多噪声 - 也许,重要的var可能是全局的)。

我称之为py_better_exchook(完美名称),它是here

示例文件:

a = 6

def test():
    unrelated_var = 43
    b,c = 2, 3
    assert a - b*c

import better_exchook
better_exchook.install()

test()

输出:

$ python test_error_reporting.py 
EXCEPTION
Traceback (most recent call last):
  File "test_error_reporting.py", line 12, in <module>
    line: test()
    locals:
      test = <local> <function test at 0x7fd91b1a05f0>
  File "test_error_reporting.py", line 7, in test
    line: assert a - b*c
    locals:
      a = <global> 6
      b = <local> 2
      c = <local> 3
AssertionError

还有其他一些选择: