我发现自己写了这样的断言:
if f(x, y) != z:
print(repr(x))
print(repr(y))
print(repr(z))
raise MyException('Expected: f(x, y) == z')
我想知道是否有办法编写一个函数来接受一个有效的Python表达式和一个异常类作为输入,计算表达式,如果它发现它是假的,打印出每个的表示形式表达式中最低级别的术语并引发给定的异常?
# validate is the mystery function
validate('f(x, y) == z', MyException)
答案 0 :(得分:2)
断言怎么样?
assert f(x, y) != z, 'Expected: f(%r, %r) == %r'%(x,y,z)
修改强>
将%r添加到print repr中 - 感谢您的评论。
答案 1 :(得分:2)
这是一个实现:
import inspect, keyword, pprint, sys, tokenize
def value_in_frame(name, frame):
try:
return frame.f_locals[name]
except KeyError:
try:
return frame.f_globals[name]
except KeyError:
raise ValueError("Couldn't find value for %s" % name)
def validate(expr, exc_class=AssertionError):
"""Evaluate `expr` in the caller's frame, raise `exc_class` if false."""
frame = inspect.stack()[1][0]
val = eval(expr, frame.f_globals, frame.f_locals)
if not val:
rl = iter([expr]).next
for typ, tok, _, _, _ in tokenize.generate_tokens(rl):
if typ == tokenize.NAME and not keyword.iskeyword(tok):
try:
val = value_in_frame(tok, frame)
except ValueError:
val = '???'
else:
val = repr(val)
print " %s: %s" % (tok, val)
raise exc_class("Failed to validate: %s" % expr)
if __name__ == '__main__':
a = b = 3
validate("a + b == 5")
答案 2 :(得分:1)
这是可能的。您可以使用Python编译器(版本之间的细节不同,因此这只是一个概述)将给定的表达式编译为AST。然后你可以将AST编译成代码对象并对其进行评估(或者首先调用eval
,无论如何)。然后,如果值为falsy,请检查AST以查看表达式的构造方式。根据AST打印出按名称访问的表达式中每个项目的值。
答案 3 :(得分:1)
可能有办法破解某些事情来做你所要求的事情,但替代方案至少要容易得多:
如您所知,手动扩展感兴趣的值。这可能更可靠,特别是对于复杂的表达式,因为你更容易说出有趣的东西而不是计算机来推断它。特别是,您可能希望包含一个甚至不在表达式中的值。
if f(x, y) != z:
raise MyException("Unexpected: the foobar is glowing; " +
"f(%r, %r) != %r" % (x, y, z))
在这里,你似乎想要x和y而不是(调用的结果)f(x,y),而在其他情况下可能会被翻转,或者你可能想要全部三个。算法很难确定您的偏好。
您可以在检测到故障的位置启动pdb,检查您喜欢的内容(甚至调用堆栈),然后恢复“正常”执行。这只有在某些情况下才有可能;例如可能不适用于网络应用。
if f(x, y) != z:
import pdb
pdb.set_trace()
raise MyException("Unexpected: ...")
日志记录模块而不是print语句或异常消息。
答案 4 :(得分:1)
nose
测试运行器有一个名为“failure detail”的插件,它正好提供了这项服务:http://somethingaboutorange.com/mrl/projects/nose/0.11.1/plugins/failuredetail.html。这个解决方案比你要求的更好,因为表达式不是一个字符串,它是一个实际的断言,稍后会被内省以找到要分析的源代码。
一个例子是older docs:
换句话说,如果你有一个测试 像:
def test_integers():
a = 2
assert a == 4, "assert 2 is 4"
您将获得如下输出:
File "/path/to/file.py", line XX, in test_integers:
assert a == 4, "assert 2 is 4"
AssertionError: assert 2 is 4
>> assert 2 == 4, "assert 2 is 4"
答案 5 :(得分:1)
你可以反过来这样做:
expr = 'f(%r, %r) != %r' % (x,y,z)
if eval(expr):
raise MyException(expr)
或换句话说:
def validate(expr,myexception):
if eval(expr):
raise myexception(expr)
虽然相当脏:)