PageRenderTime 39ms CodeModel.GetById 10ms RepoModel.GetById 0ms app.codeStats 0ms

/py/_code/_assertionnew.py

https://bitbucket.org/kkris/pypy
Python | 339 lines | 315 code | 17 blank | 7 comment | 45 complexity | 6868a3e1bf0d73e540ae76a3effc7ab7 MD5 | raw file
  1. """
  2. Find intermediate evalutation results in assert statements through builtin AST.
  3. This should replace _assertionold.py eventually.
  4. """
  5. import sys
  6. import ast
  7. import py
  8. from py._code.assertion import _format_explanation, BuiltinAssertionError
  9. if sys.platform.startswith("java") and sys.version_info < (2, 5, 2):
  10. # See http://bugs.jython.org/issue1497
  11. _exprs = ("BoolOp", "BinOp", "UnaryOp", "Lambda", "IfExp", "Dict",
  12. "ListComp", "GeneratorExp", "Yield", "Compare", "Call",
  13. "Repr", "Num", "Str", "Attribute", "Subscript", "Name",
  14. "List", "Tuple")
  15. _stmts = ("FunctionDef", "ClassDef", "Return", "Delete", "Assign",
  16. "AugAssign", "Print", "For", "While", "If", "With", "Raise",
  17. "TryExcept", "TryFinally", "Assert", "Import", "ImportFrom",
  18. "Exec", "Global", "Expr", "Pass", "Break", "Continue")
  19. _expr_nodes = set(getattr(ast, name) for name in _exprs)
  20. _stmt_nodes = set(getattr(ast, name) for name in _stmts)
  21. def _is_ast_expr(node):
  22. return node.__class__ in _expr_nodes
  23. def _is_ast_stmt(node):
  24. return node.__class__ in _stmt_nodes
  25. else:
  26. def _is_ast_expr(node):
  27. return isinstance(node, ast.expr)
  28. def _is_ast_stmt(node):
  29. return isinstance(node, ast.stmt)
  30. class Failure(Exception):
  31. """Error found while interpreting AST."""
  32. def __init__(self, explanation=""):
  33. self.cause = sys.exc_info()
  34. self.explanation = explanation
  35. def interpret(source, frame, should_fail=False):
  36. mod = ast.parse(source)
  37. visitor = DebugInterpreter(frame)
  38. try:
  39. visitor.visit(mod)
  40. except Failure:
  41. failure = sys.exc_info()[1]
  42. return getfailure(failure)
  43. if should_fail:
  44. return ("(assertion failed, but when it was re-run for "
  45. "printing intermediate values, it did not fail. Suggestions: "
  46. "compute assert expression before the assert or use --no-assert)")
  47. def run(offending_line, frame=None):
  48. if frame is None:
  49. frame = py.code.Frame(sys._getframe(1))
  50. return interpret(offending_line, frame)
  51. def getfailure(failure):
  52. explanation = _format_explanation(failure.explanation)
  53. value = failure.cause[1]
  54. if str(value):
  55. lines = explanation.splitlines()
  56. if not lines:
  57. lines.append("")
  58. lines[0] += " << %s" % (value,)
  59. explanation = "\n".join(lines)
  60. text = "%s: %s" % (failure.cause[0].__name__, explanation)
  61. if text.startswith("AssertionError: assert "):
  62. text = text[16:]
  63. return text
  64. operator_map = {
  65. ast.BitOr : "|",
  66. ast.BitXor : "^",
  67. ast.BitAnd : "&",
  68. ast.LShift : "<<",
  69. ast.RShift : ">>",
  70. ast.Add : "+",
  71. ast.Sub : "-",
  72. ast.Mult : "*",
  73. ast.Div : "/",
  74. ast.FloorDiv : "//",
  75. ast.Mod : "%",
  76. ast.Eq : "==",
  77. ast.NotEq : "!=",
  78. ast.Lt : "<",
  79. ast.LtE : "<=",
  80. ast.Gt : ">",
  81. ast.GtE : ">=",
  82. ast.Pow : "**",
  83. ast.Is : "is",
  84. ast.IsNot : "is not",
  85. ast.In : "in",
  86. ast.NotIn : "not in"
  87. }
  88. unary_map = {
  89. ast.Not : "not %s",
  90. ast.Invert : "~%s",
  91. ast.USub : "-%s",
  92. ast.UAdd : "+%s"
  93. }
  94. class DebugInterpreter(ast.NodeVisitor):
  95. """Interpret AST nodes to gleam useful debugging information. """
  96. def __init__(self, frame):
  97. self.frame = frame
  98. def generic_visit(self, node):
  99. # Fallback when we don't have a special implementation.
  100. if _is_ast_expr(node):
  101. mod = ast.Expression(node)
  102. co = self._compile(mod)
  103. try:
  104. result = self.frame.eval(co)
  105. except Exception:
  106. raise Failure()
  107. explanation = self.frame.repr(result)
  108. return explanation, result
  109. elif _is_ast_stmt(node):
  110. mod = ast.Module([node])
  111. co = self._compile(mod, "exec")
  112. try:
  113. self.frame.exec_(co)
  114. except Exception:
  115. raise Failure()
  116. return None, None
  117. else:
  118. raise AssertionError("can't handle %s" %(node,))
  119. def _compile(self, source, mode="eval"):
  120. return compile(source, "<assertion interpretation>", mode)
  121. def visit_Expr(self, expr):
  122. return self.visit(expr.value)
  123. def visit_Module(self, mod):
  124. for stmt in mod.body:
  125. self.visit(stmt)
  126. def visit_Name(self, name):
  127. explanation, result = self.generic_visit(name)
  128. # See if the name is local.
  129. source = "%r in locals() is not globals()" % (name.id,)
  130. co = self._compile(source)
  131. try:
  132. local = self.frame.eval(co)
  133. except Exception:
  134. # have to assume it isn't
  135. local = False
  136. if not local:
  137. return name.id, result
  138. return explanation, result
  139. def visit_Compare(self, comp):
  140. left = comp.left
  141. left_explanation, left_result = self.visit(left)
  142. for op, next_op in zip(comp.ops, comp.comparators):
  143. next_explanation, next_result = self.visit(next_op)
  144. op_symbol = operator_map[op.__class__]
  145. explanation = "%s %s %s" % (left_explanation, op_symbol,
  146. next_explanation)
  147. source = "__exprinfo_left %s __exprinfo_right" % (op_symbol,)
  148. co = self._compile(source)
  149. try:
  150. result = self.frame.eval(co, __exprinfo_left=left_result,
  151. __exprinfo_right=next_result)
  152. except Exception:
  153. raise Failure(explanation)
  154. try:
  155. if not result:
  156. break
  157. except KeyboardInterrupt:
  158. raise
  159. except:
  160. break
  161. left_explanation, left_result = next_explanation, next_result
  162. rcomp = py.code._reprcompare
  163. if rcomp:
  164. res = rcomp(op_symbol, left_result, next_result)
  165. if res:
  166. explanation = res
  167. return explanation, result
  168. def visit_BoolOp(self, boolop):
  169. is_or = isinstance(boolop.op, ast.Or)
  170. explanations = []
  171. for operand in boolop.values:
  172. explanation, result = self.visit(operand)
  173. explanations.append(explanation)
  174. if result == is_or:
  175. break
  176. name = is_or and " or " or " and "
  177. explanation = "(" + name.join(explanations) + ")"
  178. return explanation, result
  179. def visit_UnaryOp(self, unary):
  180. pattern = unary_map[unary.op.__class__]
  181. operand_explanation, operand_result = self.visit(unary.operand)
  182. explanation = pattern % (operand_explanation,)
  183. co = self._compile(pattern % ("__exprinfo_expr",))
  184. try:
  185. result = self.frame.eval(co, __exprinfo_expr=operand_result)
  186. except Exception:
  187. raise Failure(explanation)
  188. return explanation, result
  189. def visit_BinOp(self, binop):
  190. left_explanation, left_result = self.visit(binop.left)
  191. right_explanation, right_result = self.visit(binop.right)
  192. symbol = operator_map[binop.op.__class__]
  193. explanation = "(%s %s %s)" % (left_explanation, symbol,
  194. right_explanation)
  195. source = "__exprinfo_left %s __exprinfo_right" % (symbol,)
  196. co = self._compile(source)
  197. try:
  198. result = self.frame.eval(co, __exprinfo_left=left_result,
  199. __exprinfo_right=right_result)
  200. except Exception:
  201. raise Failure(explanation)
  202. return explanation, result
  203. def visit_Call(self, call):
  204. func_explanation, func = self.visit(call.func)
  205. arg_explanations = []
  206. ns = {"__exprinfo_func" : func}
  207. arguments = []
  208. for arg in call.args:
  209. arg_explanation, arg_result = self.visit(arg)
  210. arg_name = "__exprinfo_%s" % (len(ns),)
  211. ns[arg_name] = arg_result
  212. arguments.append(arg_name)
  213. arg_explanations.append(arg_explanation)
  214. for keyword in call.keywords:
  215. arg_explanation, arg_result = self.visit(keyword.value)
  216. arg_name = "__exprinfo_%s" % (len(ns),)
  217. ns[arg_name] = arg_result
  218. keyword_source = "%s=%%s" % (keyword.arg)
  219. arguments.append(keyword_source % (arg_name,))
  220. arg_explanations.append(keyword_source % (arg_explanation,))
  221. if call.starargs:
  222. arg_explanation, arg_result = self.visit(call.starargs)
  223. arg_name = "__exprinfo_star"
  224. ns[arg_name] = arg_result
  225. arguments.append("*%s" % (arg_name,))
  226. arg_explanations.append("*%s" % (arg_explanation,))
  227. if call.kwargs:
  228. arg_explanation, arg_result = self.visit(call.kwargs)
  229. arg_name = "__exprinfo_kwds"
  230. ns[arg_name] = arg_result
  231. arguments.append("**%s" % (arg_name,))
  232. arg_explanations.append("**%s" % (arg_explanation,))
  233. args_explained = ", ".join(arg_explanations)
  234. explanation = "%s(%s)" % (func_explanation, args_explained)
  235. args = ", ".join(arguments)
  236. source = "__exprinfo_func(%s)" % (args,)
  237. co = self._compile(source)
  238. try:
  239. result = self.frame.eval(co, **ns)
  240. except Exception:
  241. raise Failure(explanation)
  242. pattern = "%s\n{%s = %s\n}"
  243. rep = self.frame.repr(result)
  244. explanation = pattern % (rep, rep, explanation)
  245. return explanation, result
  246. def _is_builtin_name(self, name):
  247. pattern = "%r not in globals() and %r not in locals()"
  248. source = pattern % (name.id, name.id)
  249. co = self._compile(source)
  250. try:
  251. return self.frame.eval(co)
  252. except Exception:
  253. return False
  254. def visit_Attribute(self, attr):
  255. if not isinstance(attr.ctx, ast.Load):
  256. return self.generic_visit(attr)
  257. source_explanation, source_result = self.visit(attr.value)
  258. explanation = "%s.%s" % (source_explanation, attr.attr)
  259. source = "__exprinfo_expr.%s" % (attr.attr,)
  260. co = self._compile(source)
  261. try:
  262. result = self.frame.eval(co, __exprinfo_expr=source_result)
  263. except Exception:
  264. raise Failure(explanation)
  265. explanation = "%s\n{%s = %s.%s\n}" % (self.frame.repr(result),
  266. self.frame.repr(result),
  267. source_explanation, attr.attr)
  268. # Check if the attr is from an instance.
  269. source = "%r in getattr(__exprinfo_expr, '__dict__', {})"
  270. source = source % (attr.attr,)
  271. co = self._compile(source)
  272. try:
  273. from_instance = self.frame.eval(co, __exprinfo_expr=source_result)
  274. except Exception:
  275. from_instance = True
  276. if from_instance:
  277. rep = self.frame.repr(result)
  278. pattern = "%s\n{%s = %s\n}"
  279. explanation = pattern % (rep, rep, explanation)
  280. return explanation, result
  281. def visit_Assert(self, assrt):
  282. test_explanation, test_result = self.visit(assrt.test)
  283. if test_explanation.startswith("False\n{False =") and \
  284. test_explanation.endswith("\n"):
  285. test_explanation = test_explanation[15:-2]
  286. explanation = "assert %s" % (test_explanation,)
  287. if not test_result:
  288. try:
  289. raise BuiltinAssertionError
  290. except Exception:
  291. raise Failure(explanation)
  292. return explanation, test_result
  293. def visit_Assign(self, assign):
  294. value_explanation, value_result = self.visit(assign.value)
  295. explanation = "... = %s" % (value_explanation,)
  296. name = ast.Name("__exprinfo_expr", ast.Load(),
  297. lineno=assign.value.lineno,
  298. col_offset=assign.value.col_offset)
  299. new_assign = ast.Assign(assign.targets, name, lineno=assign.lineno,
  300. col_offset=assign.col_offset)
  301. mod = ast.Module([new_assign])
  302. co = self._compile(mod, "exec")
  303. try:
  304. self.frame.exec_(co, __exprinfo_expr=value_result)
  305. except Exception:
  306. raise Failure(explanation)
  307. return explanation, value_result