[ACCEPTED]-Python assert -- improved introspection of failure?-syntactic-sugar
Install your of function as sys.excepthook
-- see the docs. Your 15 function, if the second argument is AssertionError
, can 14 introspect to your heart's contents; in 13 particular, through the third argument, the 12 traceback, it can get the frame and exact 11 spot in which the assert failed, getting 10 the failing exception through the source 9 or bytecode, the value of all relevant variables, etc. Module 8 inspect helps.
Doing it in full generality is quite 7 a piece of work, but depending on what constraints 6 you're willing to accept in how you write 5 your assert
s it can be lightened substantially 4 (e.g. restricting them to only local or 3 global variables makes introspection easier 2 than if nonlocal variables of a closure 1 could be involved, and so forth).
You can attach a message to an assert
:
assert 6-(3*2), "always fails"
The message 2 can also be built dynamically:
assert x != 0, "x is not equal to zero (%d)" % x
See The assert
statement in the 1 Python documentation for more information.
As @Mark Rushakoff said nose
can evaluate failed asserts. It works 1 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)
The nose testing suite applies introspection to asserts.
However, AFAICT, you have to call their asserts 5 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 4 the AssertionError there. When my line was 3 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 2 if their asserts are skipped with -O
, but 1 that would be a very quick check.
I coded a replacement for sys.excepthook
(which is called 10 for any unhandled exception) which is a 9 bit more fancy than the standard one. It 8 will analyze the line where the exception 7 occured and print all variables which are 6 referred to in this line (it does not print 5 all local variables because that might be 4 too much noise - also, maybe the important 3 var is global or so).
I called it py_better_exchook 2 (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 1 are a few other alternatives:
- (Presented here) https://github.com/albertz/py_better_exchook/
- https://github.com/patrys/great-justice
- Nose does something similar for assertion failures, see here.
- IPython has something similar (this). Do this:
from IPython.core import ultratb; sys.excepthook = ultratb.VerboseTB()
. - Ka-Ping Yee's "cgitb.py", which is part of Python, see here, code here.
Add a message to your assertion, which will 8 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 7 only way I can think of to provide this 6 automatically would be to contain the assertion 5 in a procedure call, and then inspect the 4 stack to get the source code for that line. The 3 additional call would, unfortunately, introduce 2 overhead into the test and would not be 1 disabled with -O
.
It sounds like what you really want to do 3 is to set up a debugger breakpoint just 2 before the assert
and inspect from your favorite 1 debugger as much as you like.
More Related questions
We use cookies to improve the performance of the site. By staying on our site, you agree to the terms of use of cookies.