Python断言——改进了失败的内省?

发布于 2024-08-02 06:40:37 字数 323 浏览 5 评论 0 原文

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

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

Python 中是否有更好、更奇特的 assert 实现?它不得在执行过程中引入额外的开销(除非断言失败)..并且如果使用 -O 标志,则必须关闭。

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

This is a rather useless assertion error; it does not tell the values of the expression involved (assume constants used are actually variable names):

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

Is there a better assert implementation in Python that is more fancy? It must not introduce additional overhead over execution (except when assert fails) .. and must turn off if -O flag is used.

Edit: I know about assert's second argument as a string. I don't want to write one .. as that is encoded in the expression that is being asserted. DRY (Don't Repeat Yourself).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

耳钉梦 2024-08-09 06:40:37

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

完全通用地做到这一点是一项相当大的工作,但是根据您在编写 assert 时愿意接受的限制,它可以大大减轻(例如,将它们限制为仅本地或与涉及闭包的非局部变量相比,全局变量使内省更加容易,等等)。

Install your of function as sys.excepthook -- see the docs. Your function, if the second argument is AssertionError, can introspect to your heart's contents; in particular, through the third argument, the traceback, it can get the frame and exact spot in which the assert failed, getting the failing exception through the source or bytecode, the value of all relevant variables, etc. Module inspect helps.

Doing it in full generality is quite a piece of work, but depending on what constraints you're willing to accept in how you write your asserts it can be lightened substantially (e.g. restricting them to only local or global variables makes introspection easier than if nonlocal variables of a closure could be involved, and so forth).

掩耳倾听 2024-08-09 06:40:37

您可以将消息附加到断言

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

该消息也可以动态构建:

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

请参阅Python 文档中的 assert 声明 了解更多信息。

You can attach a message to an assert:

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

The message can also be built dynamically:

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

See The assert statement in the Python documentation for more information.

疾风者 2024-08-09 06:40:37

正如 @Mark Rushakoff 所说 鼻子 可以评估失败的断言。它也适用于标准 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)

As @Mark Rushakoff said nose can evaluate failed asserts. It works on the standard assert too.

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

nosetests' help:

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

Example:

$ 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)
坚持沉默 2024-08-09 06:40:37

nose 测试套件将内省应用于断言

但是,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 跳过,但是那将是一个非常快速的检查。

The nose testing suite applies introspection to asserts.

However, AFAICT, you have to call their asserts to get the introspection:

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

results in

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))

Notice the AssertionError there. When my line was just assert 6 == 5+2, I would get:

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

Also, I'm not sure offhand if their asserts are skipped with -O, but that would be a very quick check.

南风起 2024-08-09 06:40:37

我编写了 sys.excepthook 的替代代码(任何未处理的异常都会调用它),它比标准的更奇特。它将分析发生异常的行并打印该行中引用的所有变量(它不会打印所有局部变量,因为这可能会产生太多噪音 - 而且,也许重要的 var 是全局变量等)。

我将其命名为 py_better_exchook(完美的名称),它位于此处

示例文件:

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

还有一些其他替代方案:(

I coded a replacement for sys.excepthook (which is called for any unhandled exception) which is a bit more fancy than the standard one. It will analyze the line where the exception occured and print all variables which are referred to in this line (it does not print all local variables because that might be too much noise - also, maybe the important var is global or so).

I called it py_better_exchook (perfect name) and it's here.

Example file:

a = 6

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

import better_exchook
better_exchook.install()

test()

Output:

$ 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

There are a few other alternatives:

南…巷孤猫 2024-08-09 06:40:37

听起来您真正想做的是在断言之前设置一个调试器断点,并根据您喜欢的方式从您最喜欢的调试器进行检查。

It sounds like what you really want to do is to set up a debugger breakpoint just before the assert and inspect from your favorite debugger as much as you like.

残龙傲雪 2024-08-09 06:40:37

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

$ 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 禁用。

Add a message to your assertion, which will be displayed if the assertion fails:

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

The only way I can think of to provide this automatically would be to contain the assertion in a procedure call, and then inspect the stack to get the source code for that line. The additional call would, unfortunately, introduce overhead into the test and would not be disabled with -O.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文