"""Find intermediate evalutation results in assert statements through builtin AST.This should replace _assertionold.py eventually."""importsysimportastimportpyfrompy._code.assertionimport_format_explanation,BuiltinAssertionErrorifsys.platform.startswith("java")andsys.version_info<(2,5,2):# See http://bugs.jython.org/issue1497_exprs=("BoolOp","BinOp","UnaryOp","Lambda","IfExp","Dict","ListComp","GeneratorExp","Yield","Compare","Call","Repr","Num","Str","Attribute","Subscript","Name","List","Tuple")_stmts=("FunctionDef","ClassDef","Return","Delete","Assign","AugAssign","Print","For","While","If","With","Raise","TryExcept","TryFinally","Assert","Import","ImportFrom","Exec","Global","Expr","Pass","Break","Continue")_expr_nodes=set(getattr(ast,name)fornamein_exprs)_stmt_nodes=set(getattr(ast,name)fornamein_stmts)def_is_ast_expr(node):returnnode.__class__in_expr_nodesdef_is_ast_stmt(node):returnnode.__class__in_stmt_nodeselse:def_is_ast_expr(node):returnisinstance(node,ast.expr)def_is_ast_stmt(node):returnisinstance(node,ast.stmt)classFailure(Exception):"""Error found while interpreting AST."""def__init__(self,explanation=""):self.cause=sys.exc_info()self.explanation=explanationdefinterpret(source,frame,should_fail=False):mod=ast.parse(source)visitor=DebugInterpreter(frame)try:visitor.visit(mod)exceptFailure:failure=sys.exc_info()[1]returngetfailure(failure)ifshould_fail:return("(assertion failed, but when it was re-run for ""printing intermediate values, it did not fail. Suggestions: ""compute assert expression before the assert or use --no-assert)")defrun(offending_line,frame=None):ifframeisNone:frame=py.code.Frame(sys._getframe(1))returninterpret(offending_line,frame)defgetfailure(failure):explanation=_format_explanation(failure.explanation)value=failure.cause[1]ifstr(value):lines=explanation.splitlines()ifnotlines:lines.append("")lines[0]+=" << %s"%(value,)explanation="\n".join(lines)text="%s: %s"%(failure.cause[0].__name__,explanation)iftext.startswith("AssertionError: assert "):text=text[16:]returntextoperator_map={ast.BitOr:"|",ast.BitXor:"^",ast.BitAnd:"&",ast.LShift:"<<",ast.RShift:">>",ast.Add:"+",ast.Sub:"-",ast.Mult:"*",ast.Div:"/",ast.FloorDiv:"//",ast.Mod:"%",ast.Eq:"==",ast.NotEq:"!=",ast.Lt:"<",ast.LtE:"<=",ast.Gt:">",ast.GtE:">=",ast.Pow:"**",ast.Is:"is",ast.IsNot:"is not",ast.In:"in",ast.NotIn:"not in"}unary_map={ast.Not:"not %s",ast.Invert:"~%s",ast.USub:"-%s",ast.UAdd:"+%s"}classDebugInterpreter(ast.NodeVisitor):"""Interpret AST nodes to gleam useful debugging information. """def__init__(self,frame):self.frame=framedefgeneric_visit(self,node):# Fallback when we don't have a special implementation.if_is_ast_expr(node):mod=ast.Expression(node)co=self._compile(mod)try:result=self.frame.eval(co)exceptException:raiseFailure()explanation=self.frame.repr(result)returnexplanation,resultelif_is_ast_stmt(node):mod=ast.Module([node])co=self._compile(mod,"exec")try:self.frame.exec_(co)exceptException:raiseFailure()returnNone,Noneelse:raiseAssertionError("can't handle %s"%(node,))def_compile(self,source,mode="eval"):returncompile(source,"<assertion interpretation>",mode)defvisit_Expr(self,expr):returnself.visit(expr.value)defvisit_Module(self,mod):forstmtinmod.body:self.visit(stmt)defvisit_Name(self,name):explanation,result=self.generic_visit(name)# See if the name is local.source="%r in locals() is not globals()"%(name.id,)co=self._compile(source)try:local=self.frame.eval(co)exceptException:# have to assume it isn'tlocal=Falseifnotlocal:returnname.id,resultreturnexplanation,resultdefvisit_Compare(self,comp):left=comp.leftleft_explanation,left_result=self.visit(left)forop,next_opinzip(comp.ops,comp.comparators):next_explanation,next_result=self.visit(next_op)op_symbol=operator_map[op.__class__]explanation="%s%s%s"%(left_explanation,op_symbol,next_explanation)source="__exprinfo_left %s __exprinfo_right"%(op_symbol,)co=self._compile(source)try:result=self.frame.eval(co,__exprinfo_left=left_result,__exprinfo_right=next_result)exceptException:raiseFailure(explanation)try:ifnotresult:breakexceptKeyboardInterrupt:raiseexcept:breakleft_explanation,left_result=next_explanation,next_resultrcomp=py.code._reprcompareifrcomp:res=rcomp(op_symbol,left_result,next_result)ifres:explanation=resreturnexplanation,resultdefvisit_BoolOp(self,boolop):is_or=isinstance(boolop.op,ast.Or)explanations=[]foroperandinboolop.values:explanation,result=self.visit(operand)explanations.append(explanation)ifresult==is_or:breakname=is_orand" or "or" and "explanation="("+name.join(explanations)+")"returnexplanation,resultdefvisit_UnaryOp(self,unary):pattern=unary_map[unary.op.__class__]operand_explanation,operand_result=self.visit(unary.operand)explanation=pattern%(operand_explanation,)co=self._compile(pattern%("__exprinfo_expr",))try:result=self.frame.eval(co,__exprinfo_expr=operand_result)exceptException:raiseFailure(explanation)returnexplanation,resultdefvisit_BinOp(self,binop):left_explanation,left_result=self.visit(binop.left)right_explanation,right_result=self.visit(binop.right)symbol=operator_map[binop.op.__class__]explanation="(%s%s%s)"%(left_explanation,symbol,right_explanation)source="__exprinfo_left %s __exprinfo_right"%(symbol,)co=self._compile(source)try:result=self.frame.eval(co,__exprinfo_left=left_result,__exprinfo_right=right_result)exceptException:raiseFailure(explanation)returnexplanation,resultdefvisit_Call(self,call):func_explanation,func=self.visit(call.func)arg_explanations=[]ns={"__exprinfo_func":func}arguments=[]forargincall.args:arg_explanation,arg_result=self.visit(arg)arg_name="__exprinfo_%s"%(len(ns),)ns[arg_name]=arg_resultarguments.append(arg_name)arg_explanations.append(arg_explanation)forkeywordincall.keywords:arg_explanation,arg_result=self.visit(keyword.value)arg_name="__exprinfo_%s"%(len(ns),)ns[arg_name]=arg_resultkeyword_source="%s=%%s"%(keyword.arg)arguments.append(keyword_source%(arg_name,))arg_explanations.append(keyword_source%(arg_explanation,))ifcall.starargs:arg_explanation,arg_result=self.visit(call.starargs)arg_name="__exprinfo_star"ns[arg_name]=arg_resultarguments.append("*%s"%(arg_name,))arg_explanations.append("*%s"%(arg_explanation,))ifcall.kwargs:arg_explanation,arg_result=self.visit(call.kwargs)arg_name="__exprinfo_kwds"ns[arg_name]=arg_resultarguments.append("**%s"%(arg_name,))arg_explanations.append("**%s"%(arg_explanation,))args_explained=", ".join(arg_explanations)explanation="%s(%s)"%(func_explanation,args_explained)args=", ".join(arguments)source="__exprinfo_func(%s)"%(args,)co=self._compile(source)try:result=self.frame.eval(co,**ns)exceptException:raiseFailure(explanation)# Only show result explanation if it's not a builtin call or returns a# bool.ifnotisinstance(call.func,ast.Name)or \
notself._is_builtin_name(call.func):source="isinstance(__exprinfo_value, bool)"co=self._compile(source)try:is_bool=self.frame.eval(co,__exprinfo_value=result)exceptException:is_bool=Falseifnotis_bool:pattern="%s\n{%s = %s\n}"rep=self.frame.repr(result)explanation=pattern%(rep,rep,explanation)returnexplanation,resultdef_is_builtin_name(self,name):pattern="%r not in globals() and %r not in locals()"source=pattern%(name.id,name.id)co=self._compile(source)try:returnself.frame.eval(co)exceptException:returnFalsedefvisit_Attribute(self,attr):ifnotisinstance(attr.ctx,ast.Load):returnself.generic_visit(attr)source_explanation,source_result=self.visit(attr.value)explanation="%s.%s"%(source_explanation,attr.attr)source="__exprinfo_expr.%s"%(attr.attr,)co=self._compile(source)try:result=self.frame.eval(co,__exprinfo_expr=source_result)exceptException:raiseFailure(explanation)explanation="%s\n{%s = %s.%s\n}"%(self.frame.repr(result),self.frame.repr(result),source_explanation,attr.attr)# Check if the attr is from an instance.source="%r in getattr(__exprinfo_expr, '__dict__', {})"source=source%(attr.attr,)co=self._compile(source)try:from_instance=self.frame.eval(co,__exprinfo_expr=source_result)exceptException:from_instance=Trueiffrom_instance:rep=self.frame.repr(result)pattern="%s\n{%s = %s\n}"explanation=pattern%(rep,rep,explanation)returnexplanation,resultdefvisit_Assert(self,assrt):test_explanation,test_result=self.visit(assrt.test)iftest_explanation.startswith("False\n{False =")and \
test_explanation.endswith("\n"):test_explanation=test_explanation[15:-2]explanation="assert %s"%(test_explanation,)ifnottest_result:try:raiseBuiltinAssertionErrorexceptException:raiseFailure(explanation)returnexplanation,test_resultdefvisit_Assign(self,assign):value_explanation,value_result=self.visit(assign.value)explanation="... = %s"%(value_explanation,)name=ast.Name("__exprinfo_expr",ast.Load(),lineno=assign.value.lineno,col_offset=assign.value.col_offset)new_assign=ast.Assign(assign.targets,name,lineno=assign.lineno,col_offset=assign.col_offset)mod=ast.Module([new_assign])co=self._compile(mod,"exec")try:self.frame.exec_(co,__exprinfo_expr=value_result)exceptException:raiseFailure(explanation)returnexplanation,value_result