Drop assertionnew and assertionold from _pytest._code
This commit is contained in:
parent
a912d3745b
commit
7a6f902f6f
|
@ -1,9 +1,4 @@
|
||||||
""" python inspection/code generation API """
|
""" python inspection/code generation API """
|
||||||
from .assertion import AssertionError as _AssertionError # noqa
|
|
||||||
from .assertion import _format_explanation # noqa
|
|
||||||
from .assertion import _reprcompare # noqa
|
|
||||||
from .assertion import reinterpret as _reinterpret # noqa
|
|
||||||
from .assertion import reinterpret_old as _reinterpret_old # noqa
|
|
||||||
from .code import Code # noqa
|
from .code import Code # noqa
|
||||||
from .code import ExceptionInfo # noqa
|
from .code import ExceptionInfo # noqa
|
||||||
from .code import Frame # noqa
|
from .code import Frame # noqa
|
||||||
|
|
|
@ -1,339 +0,0 @@
|
||||||
"""
|
|
||||||
Find intermediate evalutation results in assert statements through builtin AST.
|
|
||||||
This should replace _assertionold.py eventually.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import ast
|
|
||||||
import sys
|
|
||||||
|
|
||||||
from .assertion import _format_explanation, BuiltinAssertionError
|
|
||||||
|
|
||||||
if sys.platform.startswith("java") and sys.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) for name in _exprs)
|
|
||||||
_stmt_nodes = set(getattr(ast, name) for name in _stmts)
|
|
||||||
def _is_ast_expr(node):
|
|
||||||
return node.__class__ in _expr_nodes
|
|
||||||
def _is_ast_stmt(node):
|
|
||||||
return node.__class__ in _stmt_nodes
|
|
||||||
else:
|
|
||||||
def _is_ast_expr(node):
|
|
||||||
return isinstance(node, ast.expr)
|
|
||||||
def _is_ast_stmt(node):
|
|
||||||
return isinstance(node, ast.stmt)
|
|
||||||
|
|
||||||
|
|
||||||
class Failure(Exception):
|
|
||||||
"""Error found while interpreting AST."""
|
|
||||||
|
|
||||||
def __init__(self, explanation=""):
|
|
||||||
self.cause = sys.exc_info()
|
|
||||||
self.explanation = explanation
|
|
||||||
|
|
||||||
|
|
||||||
def interpret(source, frame, should_fail=False):
|
|
||||||
mod = ast.parse(source)
|
|
||||||
visitor = DebugInterpreter(frame)
|
|
||||||
try:
|
|
||||||
visitor.visit(mod)
|
|
||||||
except Failure:
|
|
||||||
failure = sys.exc_info()[1]
|
|
||||||
return getfailure(failure)
|
|
||||||
if should_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)")
|
|
||||||
|
|
||||||
def run(offending_line, frame=None):
|
|
||||||
from .code import Frame
|
|
||||||
if frame is None:
|
|
||||||
frame = Frame(sys._getframe(1))
|
|
||||||
return interpret(offending_line, frame)
|
|
||||||
|
|
||||||
def getfailure(failure):
|
|
||||||
explanation = _format_explanation(failure.explanation)
|
|
||||||
value = failure.cause[1]
|
|
||||||
if str(value):
|
|
||||||
lines = explanation.splitlines()
|
|
||||||
if not lines:
|
|
||||||
lines.append("")
|
|
||||||
lines[0] += " << %s" % (value,)
|
|
||||||
explanation = "\n".join(lines)
|
|
||||||
text = "%s: %s" % (failure.cause[0].__name__, explanation)
|
|
||||||
if text.startswith("AssertionError: assert "):
|
|
||||||
text = text[16:]
|
|
||||||
return text
|
|
||||||
|
|
||||||
|
|
||||||
operator_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"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class DebugInterpreter(ast.NodeVisitor):
|
|
||||||
"""Interpret AST nodes to gleam useful debugging information. """
|
|
||||||
|
|
||||||
def __init__(self, frame):
|
|
||||||
self.frame = frame
|
|
||||||
|
|
||||||
def generic_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)
|
|
||||||
except Exception:
|
|
||||||
raise Failure()
|
|
||||||
explanation = self.frame.repr(result)
|
|
||||||
return explanation, result
|
|
||||||
elif _is_ast_stmt(node):
|
|
||||||
mod = ast.Module([node])
|
|
||||||
co = self._compile(mod, "exec")
|
|
||||||
try:
|
|
||||||
self.frame.exec_(co)
|
|
||||||
except Exception:
|
|
||||||
raise Failure()
|
|
||||||
return None, None
|
|
||||||
else:
|
|
||||||
raise AssertionError("can't handle %s" %(node,))
|
|
||||||
|
|
||||||
def _compile(self, source, mode="eval"):
|
|
||||||
return compile(source, "<assertion interpretation>", mode)
|
|
||||||
|
|
||||||
def visit_Expr(self, expr):
|
|
||||||
return self.visit(expr.value)
|
|
||||||
|
|
||||||
def visit_Module(self, mod):
|
|
||||||
for stmt in mod.body:
|
|
||||||
self.visit(stmt)
|
|
||||||
|
|
||||||
def visit_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)
|
|
||||||
except Exception:
|
|
||||||
# have to assume it isn't
|
|
||||||
local = False
|
|
||||||
if not local:
|
|
||||||
return name.id, result
|
|
||||||
return explanation, result
|
|
||||||
|
|
||||||
def visit_Compare(self, comp):
|
|
||||||
left = comp.left
|
|
||||||
left_explanation, left_result = self.visit(left)
|
|
||||||
for op, next_op in zip(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)
|
|
||||||
except Exception:
|
|
||||||
raise Failure(explanation)
|
|
||||||
try:
|
|
||||||
if not result:
|
|
||||||
break
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
raise
|
|
||||||
except:
|
|
||||||
break
|
|
||||||
left_explanation, left_result = next_explanation, next_result
|
|
||||||
|
|
||||||
import _pytest._code
|
|
||||||
rcomp = _pytest._code._reprcompare
|
|
||||||
if rcomp:
|
|
||||||
res = rcomp(op_symbol, left_result, next_result)
|
|
||||||
if res:
|
|
||||||
explanation = res
|
|
||||||
return explanation, result
|
|
||||||
|
|
||||||
def visit_BoolOp(self, boolop):
|
|
||||||
is_or = isinstance(boolop.op, ast.Or)
|
|
||||||
explanations = []
|
|
||||||
for operand in boolop.values:
|
|
||||||
explanation, result = self.visit(operand)
|
|
||||||
explanations.append(explanation)
|
|
||||||
if result == is_or:
|
|
||||||
break
|
|
||||||
name = is_or and " or " or " and "
|
|
||||||
explanation = "(" + name.join(explanations) + ")"
|
|
||||||
return explanation, result
|
|
||||||
|
|
||||||
def visit_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)
|
|
||||||
except Exception:
|
|
||||||
raise Failure(explanation)
|
|
||||||
return explanation, result
|
|
||||||
|
|
||||||
def visit_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)
|
|
||||||
except Exception:
|
|
||||||
raise Failure(explanation)
|
|
||||||
return explanation, result
|
|
||||||
|
|
||||||
def visit_Call(self, call):
|
|
||||||
func_explanation, func = self.visit(call.func)
|
|
||||||
arg_explanations = []
|
|
||||||
ns = {"__exprinfo_func" : func}
|
|
||||||
arguments = []
|
|
||||||
for arg in call.args:
|
|
||||||
arg_explanation, arg_result = self.visit(arg)
|
|
||||||
arg_name = "__exprinfo_%s" % (len(ns),)
|
|
||||||
ns[arg_name] = arg_result
|
|
||||||
arguments.append(arg_name)
|
|
||||||
arg_explanations.append(arg_explanation)
|
|
||||||
for keyword in call.keywords:
|
|
||||||
arg_explanation, arg_result = self.visit(keyword.value)
|
|
||||||
arg_name = "__exprinfo_%s" % (len(ns),)
|
|
||||||
ns[arg_name] = arg_result
|
|
||||||
keyword_source = "%s=%%s" % (keyword.arg)
|
|
||||||
arguments.append(keyword_source % (arg_name,))
|
|
||||||
arg_explanations.append(keyword_source % (arg_explanation,))
|
|
||||||
if call.starargs:
|
|
||||||
arg_explanation, arg_result = self.visit(call.starargs)
|
|
||||||
arg_name = "__exprinfo_star"
|
|
||||||
ns[arg_name] = arg_result
|
|
||||||
arguments.append("*%s" % (arg_name,))
|
|
||||||
arg_explanations.append("*%s" % (arg_explanation,))
|
|
||||||
if call.kwargs:
|
|
||||||
arg_explanation, arg_result = self.visit(call.kwargs)
|
|
||||||
arg_name = "__exprinfo_kwds"
|
|
||||||
ns[arg_name] = arg_result
|
|
||||||
arguments.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)
|
|
||||||
except Exception:
|
|
||||||
raise Failure(explanation)
|
|
||||||
pattern = "%s\n{%s = %s\n}"
|
|
||||||
rep = self.frame.repr(result)
|
|
||||||
explanation = pattern % (rep, rep, explanation)
|
|
||||||
return explanation, result
|
|
||||||
|
|
||||||
def _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:
|
|
||||||
return self.frame.eval(co)
|
|
||||||
except Exception:
|
|
||||||
return False
|
|
||||||
|
|
||||||
def visit_Attribute(self, attr):
|
|
||||||
if not isinstance(attr.ctx, ast.Load):
|
|
||||||
return self.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)
|
|
||||||
except Exception:
|
|
||||||
raise Failure(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)
|
|
||||||
except Exception:
|
|
||||||
from_instance = True
|
|
||||||
if from_instance:
|
|
||||||
rep = self.frame.repr(result)
|
|
||||||
pattern = "%s\n{%s = %s\n}"
|
|
||||||
explanation = pattern % (rep, rep, explanation)
|
|
||||||
return explanation, result
|
|
||||||
|
|
||||||
def visit_Assert(self, assrt):
|
|
||||||
test_explanation, test_result = self.visit(assrt.test)
|
|
||||||
if test_explanation.startswith("False\n{False =") and \
|
|
||||||
test_explanation.endswith("\n"):
|
|
||||||
test_explanation = test_explanation[15:-2]
|
|
||||||
explanation = "assert %s" % (test_explanation,)
|
|
||||||
if not test_result:
|
|
||||||
try:
|
|
||||||
raise BuiltinAssertionError
|
|
||||||
except Exception:
|
|
||||||
raise Failure(explanation)
|
|
||||||
return explanation, test_result
|
|
||||||
|
|
||||||
def visit_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)
|
|
||||||
except Exception:
|
|
||||||
raise Failure(explanation)
|
|
||||||
return explanation, value_result
|
|
|
@ -1,561 +0,0 @@
|
||||||
import inspect
|
|
||||||
import sys
|
|
||||||
|
|
||||||
from compiler import parse, ast, pycodegen
|
|
||||||
|
|
||||||
import py
|
|
||||||
from _pytest._code.assertion import BuiltinAssertionError, _format_explanation
|
|
||||||
|
|
||||||
passthroughex = py.builtin._sysex
|
|
||||||
|
|
||||||
class Failure:
|
|
||||||
def __init__(self, node):
|
|
||||||
self.exc, self.value, self.tb = sys.exc_info()
|
|
||||||
self.node = node
|
|
||||||
|
|
||||||
class View(object):
|
|
||||||
"""View base class.
|
|
||||||
|
|
||||||
If C is a subclass of View, then C(x) creates a proxy object around
|
|
||||||
the object x. The actual class of the proxy is not C in general,
|
|
||||||
but a *subclass* of C determined by the rules below. To avoid confusion
|
|
||||||
we call view class the class of the proxy (a subclass of C, so of View)
|
|
||||||
and object class the class of x.
|
|
||||||
|
|
||||||
Attributes and methods not found in the proxy are automatically read on x.
|
|
||||||
Other operations like setting attributes are performed on the proxy, as
|
|
||||||
determined by its view class. The object x is available from the proxy
|
|
||||||
as its __obj__ attribute.
|
|
||||||
|
|
||||||
The view class selection is determined by the __view__ tuples and the
|
|
||||||
optional __viewkey__ method. By default, the selected view class is the
|
|
||||||
most specific subclass of C whose __view__ mentions the class of x.
|
|
||||||
If no such subclass is found, the search proceeds with the parent
|
|
||||||
object classes. For example, C(True) will first look for a subclass
|
|
||||||
of C with __view__ = (..., bool, ...) and only if it doesn't find any
|
|
||||||
look for one with __view__ = (..., int, ...), and then ..., object,...
|
|
||||||
If everything fails the class C itself is considered to be the default.
|
|
||||||
|
|
||||||
Alternatively, the view class selection can be driven by another aspect
|
|
||||||
of the object x, instead of the class of x, by overriding __viewkey__.
|
|
||||||
See last example at the end of this module.
|
|
||||||
"""
|
|
||||||
|
|
||||||
_viewcache = {}
|
|
||||||
__view__ = ()
|
|
||||||
|
|
||||||
def __new__(rootclass, obj, *args, **kwds):
|
|
||||||
self = object.__new__(rootclass)
|
|
||||||
self.__obj__ = obj
|
|
||||||
self.__rootclass__ = rootclass
|
|
||||||
key = self.__viewkey__()
|
|
||||||
try:
|
|
||||||
self.__class__ = self._viewcache[key]
|
|
||||||
except KeyError:
|
|
||||||
self.__class__ = self._selectsubclass(key)
|
|
||||||
return self
|
|
||||||
|
|
||||||
def __getattr__(self, attr):
|
|
||||||
# attributes not found in the normal hierarchy rooted on View
|
|
||||||
# are looked up in the object's real class
|
|
||||||
return getattr(self.__obj__, attr)
|
|
||||||
|
|
||||||
def __viewkey__(self):
|
|
||||||
return self.__obj__.__class__
|
|
||||||
|
|
||||||
def __matchkey__(self, key, subclasses):
|
|
||||||
if inspect.isclass(key):
|
|
||||||
keys = inspect.getmro(key)
|
|
||||||
else:
|
|
||||||
keys = [key]
|
|
||||||
for key in keys:
|
|
||||||
result = [C for C in subclasses if key in C.__view__]
|
|
||||||
if result:
|
|
||||||
return result
|
|
||||||
return []
|
|
||||||
|
|
||||||
def _selectsubclass(self, key):
|
|
||||||
subclasses = list(enumsubclasses(self.__rootclass__))
|
|
||||||
for C in subclasses:
|
|
||||||
if not isinstance(C.__view__, tuple):
|
|
||||||
C.__view__ = (C.__view__,)
|
|
||||||
choices = self.__matchkey__(key, subclasses)
|
|
||||||
if not choices:
|
|
||||||
return self.__rootclass__
|
|
||||||
elif len(choices) == 1:
|
|
||||||
return choices[0]
|
|
||||||
else:
|
|
||||||
# combine the multiple choices
|
|
||||||
return type('?', tuple(choices), {})
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return '%s(%r)' % (self.__rootclass__.__name__, self.__obj__)
|
|
||||||
|
|
||||||
|
|
||||||
def enumsubclasses(cls):
|
|
||||||
for subcls in cls.__subclasses__():
|
|
||||||
for subsubclass in enumsubclasses(subcls):
|
|
||||||
yield subsubclass
|
|
||||||
yield cls
|
|
||||||
|
|
||||||
|
|
||||||
class Interpretable(View):
|
|
||||||
"""A parse tree node with a few extra methods."""
|
|
||||||
explanation = None
|
|
||||||
|
|
||||||
def is_builtin(self, frame):
|
|
||||||
return False
|
|
||||||
|
|
||||||
def eval(self, frame):
|
|
||||||
# fall-back for unknown expression nodes
|
|
||||||
try:
|
|
||||||
expr = ast.Expression(self.__obj__)
|
|
||||||
expr.filename = '<eval>'
|
|
||||||
self.__obj__.filename = '<eval>'
|
|
||||||
co = pycodegen.ExpressionCodeGenerator(expr).getCode()
|
|
||||||
result = frame.eval(co)
|
|
||||||
except passthroughex:
|
|
||||||
raise
|
|
||||||
except:
|
|
||||||
raise Failure(self)
|
|
||||||
self.result = result
|
|
||||||
self.explanation = self.explanation or frame.repr(self.result)
|
|
||||||
|
|
||||||
def run(self, frame):
|
|
||||||
# fall-back for unknown statement nodes
|
|
||||||
try:
|
|
||||||
expr = ast.Module(None, ast.Stmt([self.__obj__]))
|
|
||||||
expr.filename = '<run>'
|
|
||||||
co = pycodegen.ModuleCodeGenerator(expr).getCode()
|
|
||||||
frame.exec_(co)
|
|
||||||
except passthroughex:
|
|
||||||
raise
|
|
||||||
except:
|
|
||||||
raise Failure(self)
|
|
||||||
|
|
||||||
def nice_explanation(self):
|
|
||||||
return _format_explanation(self.explanation)
|
|
||||||
|
|
||||||
|
|
||||||
class Name(Interpretable):
|
|
||||||
__view__ = ast.Name
|
|
||||||
|
|
||||||
def is_local(self, frame):
|
|
||||||
source = '%r in locals() is not globals()' % self.name
|
|
||||||
try:
|
|
||||||
return frame.is_true(frame.eval(source))
|
|
||||||
except passthroughex:
|
|
||||||
raise
|
|
||||||
except:
|
|
||||||
return False
|
|
||||||
|
|
||||||
def is_global(self, frame):
|
|
||||||
source = '%r in globals()' % self.name
|
|
||||||
try:
|
|
||||||
return frame.is_true(frame.eval(source))
|
|
||||||
except passthroughex:
|
|
||||||
raise
|
|
||||||
except:
|
|
||||||
return False
|
|
||||||
|
|
||||||
def is_builtin(self, frame):
|
|
||||||
source = '%r not in locals() and %r not in globals()' % (
|
|
||||||
self.name, self.name)
|
|
||||||
try:
|
|
||||||
return frame.is_true(frame.eval(source))
|
|
||||||
except passthroughex:
|
|
||||||
raise
|
|
||||||
except:
|
|
||||||
return False
|
|
||||||
|
|
||||||
def eval(self, frame):
|
|
||||||
super(Name, self).eval(frame)
|
|
||||||
if not self.is_local(frame):
|
|
||||||
self.explanation = self.name
|
|
||||||
|
|
||||||
class Compare(Interpretable):
|
|
||||||
__view__ = ast.Compare
|
|
||||||
|
|
||||||
def eval(self, frame):
|
|
||||||
expr = Interpretable(self.expr)
|
|
||||||
expr.eval(frame)
|
|
||||||
for operation, expr2 in self.ops:
|
|
||||||
if hasattr(self, 'result'):
|
|
||||||
# shortcutting in chained expressions
|
|
||||||
if not frame.is_true(self.result):
|
|
||||||
break
|
|
||||||
expr2 = Interpretable(expr2)
|
|
||||||
expr2.eval(frame)
|
|
||||||
self.explanation = "%s %s %s" % (
|
|
||||||
expr.explanation, operation, expr2.explanation)
|
|
||||||
source = "__exprinfo_left %s __exprinfo_right" % operation
|
|
||||||
try:
|
|
||||||
self.result = frame.eval(source,
|
|
||||||
__exprinfo_left=expr.result,
|
|
||||||
__exprinfo_right=expr2.result)
|
|
||||||
except passthroughex:
|
|
||||||
raise
|
|
||||||
except:
|
|
||||||
raise Failure(self)
|
|
||||||
expr = expr2
|
|
||||||
|
|
||||||
class And(Interpretable):
|
|
||||||
__view__ = ast.And
|
|
||||||
|
|
||||||
def eval(self, frame):
|
|
||||||
explanations = []
|
|
||||||
for expr in self.nodes:
|
|
||||||
expr = Interpretable(expr)
|
|
||||||
expr.eval(frame)
|
|
||||||
explanations.append(expr.explanation)
|
|
||||||
self.result = expr.result
|
|
||||||
if not frame.is_true(expr.result):
|
|
||||||
break
|
|
||||||
self.explanation = '(' + ' and '.join(explanations) + ')'
|
|
||||||
|
|
||||||
class Or(Interpretable):
|
|
||||||
__view__ = ast.Or
|
|
||||||
|
|
||||||
def eval(self, frame):
|
|
||||||
explanations = []
|
|
||||||
for expr in self.nodes:
|
|
||||||
expr = Interpretable(expr)
|
|
||||||
expr.eval(frame)
|
|
||||||
explanations.append(expr.explanation)
|
|
||||||
self.result = expr.result
|
|
||||||
if frame.is_true(expr.result):
|
|
||||||
break
|
|
||||||
self.explanation = '(' + ' or '.join(explanations) + ')'
|
|
||||||
|
|
||||||
|
|
||||||
# == Unary operations ==
|
|
||||||
keepalive = []
|
|
||||||
for astclass, astpattern in {
|
|
||||||
ast.Not : 'not __exprinfo_expr',
|
|
||||||
ast.Invert : '(~__exprinfo_expr)',
|
|
||||||
}.items():
|
|
||||||
|
|
||||||
class UnaryArith(Interpretable):
|
|
||||||
__view__ = astclass
|
|
||||||
|
|
||||||
def eval(self, frame, astpattern=astpattern):
|
|
||||||
expr = Interpretable(self.expr)
|
|
||||||
expr.eval(frame)
|
|
||||||
self.explanation = astpattern.replace('__exprinfo_expr',
|
|
||||||
expr.explanation)
|
|
||||||
try:
|
|
||||||
self.result = frame.eval(astpattern,
|
|
||||||
__exprinfo_expr=expr.result)
|
|
||||||
except passthroughex:
|
|
||||||
raise
|
|
||||||
except:
|
|
||||||
raise Failure(self)
|
|
||||||
|
|
||||||
keepalive.append(UnaryArith)
|
|
||||||
|
|
||||||
# == Binary operations ==
|
|
||||||
for astclass, astpattern in {
|
|
||||||
ast.Add : '(__exprinfo_left + __exprinfo_right)',
|
|
||||||
ast.Sub : '(__exprinfo_left - __exprinfo_right)',
|
|
||||||
ast.Mul : '(__exprinfo_left * __exprinfo_right)',
|
|
||||||
ast.Div : '(__exprinfo_left / __exprinfo_right)',
|
|
||||||
ast.Mod : '(__exprinfo_left % __exprinfo_right)',
|
|
||||||
ast.Power : '(__exprinfo_left ** __exprinfo_right)',
|
|
||||||
}.items():
|
|
||||||
|
|
||||||
class BinaryArith(Interpretable):
|
|
||||||
__view__ = astclass
|
|
||||||
|
|
||||||
def eval(self, frame, astpattern=astpattern):
|
|
||||||
left = Interpretable(self.left)
|
|
||||||
left.eval(frame)
|
|
||||||
right = Interpretable(self.right)
|
|
||||||
right.eval(frame)
|
|
||||||
self.explanation = (astpattern
|
|
||||||
.replace('__exprinfo_left', left .explanation)
|
|
||||||
.replace('__exprinfo_right', right.explanation))
|
|
||||||
try:
|
|
||||||
self.result = frame.eval(astpattern,
|
|
||||||
__exprinfo_left=left.result,
|
|
||||||
__exprinfo_right=right.result)
|
|
||||||
except passthroughex:
|
|
||||||
raise
|
|
||||||
except:
|
|
||||||
raise Failure(self)
|
|
||||||
|
|
||||||
keepalive.append(BinaryArith)
|
|
||||||
|
|
||||||
|
|
||||||
class CallFunc(Interpretable):
|
|
||||||
__view__ = ast.CallFunc
|
|
||||||
|
|
||||||
def is_bool(self, frame):
|
|
||||||
source = 'isinstance(__exprinfo_value, bool)'
|
|
||||||
try:
|
|
||||||
return frame.is_true(frame.eval(source,
|
|
||||||
__exprinfo_value=self.result))
|
|
||||||
except passthroughex:
|
|
||||||
raise
|
|
||||||
except:
|
|
||||||
return False
|
|
||||||
|
|
||||||
def eval(self, frame):
|
|
||||||
node = Interpretable(self.node)
|
|
||||||
node.eval(frame)
|
|
||||||
explanations = []
|
|
||||||
vars = {'__exprinfo_fn': node.result}
|
|
||||||
source = '__exprinfo_fn('
|
|
||||||
for a in self.args:
|
|
||||||
if isinstance(a, ast.Keyword):
|
|
||||||
keyword = a.name
|
|
||||||
a = a.expr
|
|
||||||
else:
|
|
||||||
keyword = None
|
|
||||||
a = Interpretable(a)
|
|
||||||
a.eval(frame)
|
|
||||||
argname = '__exprinfo_%d' % len(vars)
|
|
||||||
vars[argname] = a.result
|
|
||||||
if keyword is None:
|
|
||||||
source += argname + ','
|
|
||||||
explanations.append(a.explanation)
|
|
||||||
else:
|
|
||||||
source += '%s=%s,' % (keyword, argname)
|
|
||||||
explanations.append('%s=%s' % (keyword, a.explanation))
|
|
||||||
if self.star_args:
|
|
||||||
star_args = Interpretable(self.star_args)
|
|
||||||
star_args.eval(frame)
|
|
||||||
argname = '__exprinfo_star'
|
|
||||||
vars[argname] = star_args.result
|
|
||||||
source += '*' + argname + ','
|
|
||||||
explanations.append('*' + star_args.explanation)
|
|
||||||
if self.dstar_args:
|
|
||||||
dstar_args = Interpretable(self.dstar_args)
|
|
||||||
dstar_args.eval(frame)
|
|
||||||
argname = '__exprinfo_kwds'
|
|
||||||
vars[argname] = dstar_args.result
|
|
||||||
source += '**' + argname + ','
|
|
||||||
explanations.append('**' + dstar_args.explanation)
|
|
||||||
self.explanation = "%s(%s)" % (
|
|
||||||
node.explanation, ', '.join(explanations))
|
|
||||||
if source.endswith(','):
|
|
||||||
source = source[:-1]
|
|
||||||
source += ')'
|
|
||||||
try:
|
|
||||||
self.result = frame.eval(source, **vars)
|
|
||||||
except passthroughex:
|
|
||||||
raise
|
|
||||||
except:
|
|
||||||
raise Failure(self)
|
|
||||||
if not node.is_builtin(frame) or not self.is_bool(frame):
|
|
||||||
r = frame.repr(self.result)
|
|
||||||
self.explanation = '%s\n{%s = %s\n}' % (r, r, self.explanation)
|
|
||||||
|
|
||||||
class Getattr(Interpretable):
|
|
||||||
__view__ = ast.Getattr
|
|
||||||
|
|
||||||
def eval(self, frame):
|
|
||||||
expr = Interpretable(self.expr)
|
|
||||||
expr.eval(frame)
|
|
||||||
source = '__exprinfo_expr.%s' % self.attrname
|
|
||||||
try:
|
|
||||||
self.result = frame.eval(source, __exprinfo_expr=expr.result)
|
|
||||||
except passthroughex:
|
|
||||||
raise
|
|
||||||
except:
|
|
||||||
raise Failure(self)
|
|
||||||
self.explanation = '%s.%s' % (expr.explanation, self.attrname)
|
|
||||||
# if the attribute comes from the instance, its value is interesting
|
|
||||||
source = ('hasattr(__exprinfo_expr, "__dict__") and '
|
|
||||||
'%r in __exprinfo_expr.__dict__' % self.attrname)
|
|
||||||
try:
|
|
||||||
from_instance = frame.is_true(
|
|
||||||
frame.eval(source, __exprinfo_expr=expr.result))
|
|
||||||
except passthroughex:
|
|
||||||
raise
|
|
||||||
except:
|
|
||||||
from_instance = True
|
|
||||||
if from_instance:
|
|
||||||
r = frame.repr(self.result)
|
|
||||||
self.explanation = '%s\n{%s = %s\n}' % (r, r, self.explanation)
|
|
||||||
|
|
||||||
# == Re-interpretation of full statements ==
|
|
||||||
|
|
||||||
class Assert(Interpretable):
|
|
||||||
__view__ = ast.Assert
|
|
||||||
|
|
||||||
def run(self, frame):
|
|
||||||
test = Interpretable(self.test)
|
|
||||||
test.eval(frame)
|
|
||||||
# simplify 'assert False where False = ...'
|
|
||||||
if (test.explanation.startswith('False\n{False = ') and
|
|
||||||
test.explanation.endswith('\n}')):
|
|
||||||
test.explanation = test.explanation[15:-2]
|
|
||||||
# print the result as 'assert <explanation>'
|
|
||||||
self.result = test.result
|
|
||||||
self.explanation = 'assert ' + test.explanation
|
|
||||||
if not frame.is_true(test.result):
|
|
||||||
try:
|
|
||||||
raise BuiltinAssertionError
|
|
||||||
except passthroughex:
|
|
||||||
raise
|
|
||||||
except:
|
|
||||||
raise Failure(self)
|
|
||||||
|
|
||||||
class Assign(Interpretable):
|
|
||||||
__view__ = ast.Assign
|
|
||||||
|
|
||||||
def run(self, frame):
|
|
||||||
expr = Interpretable(self.expr)
|
|
||||||
expr.eval(frame)
|
|
||||||
self.result = expr.result
|
|
||||||
self.explanation = '... = ' + expr.explanation
|
|
||||||
# fall-back-run the rest of the assignment
|
|
||||||
ass = ast.Assign(self.nodes, ast.Name('__exprinfo_expr'))
|
|
||||||
mod = ast.Module(None, ast.Stmt([ass]))
|
|
||||||
mod.filename = '<run>'
|
|
||||||
co = pycodegen.ModuleCodeGenerator(mod).getCode()
|
|
||||||
try:
|
|
||||||
frame.exec_(co, __exprinfo_expr=expr.result)
|
|
||||||
except passthroughex:
|
|
||||||
raise
|
|
||||||
except:
|
|
||||||
raise Failure(self)
|
|
||||||
|
|
||||||
class Discard(Interpretable):
|
|
||||||
__view__ = ast.Discard
|
|
||||||
|
|
||||||
def run(self, frame):
|
|
||||||
expr = Interpretable(self.expr)
|
|
||||||
expr.eval(frame)
|
|
||||||
self.result = expr.result
|
|
||||||
self.explanation = expr.explanation
|
|
||||||
|
|
||||||
class Stmt(Interpretable):
|
|
||||||
__view__ = ast.Stmt
|
|
||||||
|
|
||||||
def run(self, frame):
|
|
||||||
for stmt in self.nodes:
|
|
||||||
stmt = Interpretable(stmt)
|
|
||||||
stmt.run(frame)
|
|
||||||
|
|
||||||
|
|
||||||
def report_failure(e):
|
|
||||||
explanation = e.node.nice_explanation()
|
|
||||||
if explanation:
|
|
||||||
explanation = ", in: " + explanation
|
|
||||||
else:
|
|
||||||
explanation = ""
|
|
||||||
sys.stdout.write("%s: %s%s\n" % (e.exc.__name__, e.value, explanation))
|
|
||||||
|
|
||||||
def check(s, frame=None):
|
|
||||||
from _pytest._code import Frame
|
|
||||||
if frame is None:
|
|
||||||
frame = sys._getframe(1)
|
|
||||||
frame = Frame(frame)
|
|
||||||
expr = parse(s, 'eval')
|
|
||||||
assert isinstance(expr, ast.Expression)
|
|
||||||
node = Interpretable(expr.node)
|
|
||||||
try:
|
|
||||||
node.eval(frame)
|
|
||||||
except passthroughex:
|
|
||||||
raise
|
|
||||||
except Failure:
|
|
||||||
e = sys.exc_info()[1]
|
|
||||||
report_failure(e)
|
|
||||||
else:
|
|
||||||
if not frame.is_true(node.result):
|
|
||||||
sys.stderr.write("assertion failed: %s\n" % node.nice_explanation())
|
|
||||||
|
|
||||||
|
|
||||||
###########################################################
|
|
||||||
# API / Entry points
|
|
||||||
# #########################################################
|
|
||||||
|
|
||||||
def interpret(source, frame, should_fail=False):
|
|
||||||
module = Interpretable(parse(source, 'exec').node)
|
|
||||||
import _pytest._code
|
|
||||||
if isinstance(frame, py.std.types.FrameType):
|
|
||||||
frame = _pytest._code.Frame(frame)
|
|
||||||
try:
|
|
||||||
module.run(frame)
|
|
||||||
except Failure:
|
|
||||||
e = sys.exc_info()[1]
|
|
||||||
return getfailure(e)
|
|
||||||
except passthroughex:
|
|
||||||
raise
|
|
||||||
except:
|
|
||||||
import traceback
|
|
||||||
traceback.print_exc()
|
|
||||||
if should_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 --nomagic)")
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
def getmsg(excinfo):
|
|
||||||
import _pytest._code
|
|
||||||
if isinstance(excinfo, tuple):
|
|
||||||
excinfo = _pytest._code.ExceptionInfo(excinfo)
|
|
||||||
#frame, line = gettbline(tb)
|
|
||||||
#frame = pytest.code.Frame(frame)
|
|
||||||
#return interpret(line, frame)
|
|
||||||
|
|
||||||
tb = excinfo.traceback[-1]
|
|
||||||
source = str(tb.statement).strip()
|
|
||||||
x = interpret(source, tb.frame, should_fail=True)
|
|
||||||
if not isinstance(x, str):
|
|
||||||
raise TypeError("interpret returned non-string %r" % (x,))
|
|
||||||
return x
|
|
||||||
|
|
||||||
def getfailure(e):
|
|
||||||
explanation = e.node.nice_explanation()
|
|
||||||
if str(e.value):
|
|
||||||
lines = explanation.split('\n')
|
|
||||||
lines[0] += " << %s" % (e.value,)
|
|
||||||
explanation = '\n'.join(lines)
|
|
||||||
text = "%s: %s" % (e.exc.__name__, explanation)
|
|
||||||
if text.startswith('AssertionError: assert '):
|
|
||||||
text = text[16:]
|
|
||||||
return text
|
|
||||||
|
|
||||||
def run(s, frame=None):
|
|
||||||
import _pytest._code
|
|
||||||
if frame is None:
|
|
||||||
frame = sys._getframe(1)
|
|
||||||
frame = _pytest._code.Frame(frame)
|
|
||||||
module = Interpretable(parse(s, 'exec').node)
|
|
||||||
try:
|
|
||||||
module.run(frame)
|
|
||||||
except Failure:
|
|
||||||
e = sys.exc_info()[1]
|
|
||||||
report_failure(e)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
# example:
|
|
||||||
def f():
|
|
||||||
return 5
|
|
||||||
def g():
|
|
||||||
return 3
|
|
||||||
def h(x):
|
|
||||||
return 'never'
|
|
||||||
check("f() * g() == 5")
|
|
||||||
check("not f()")
|
|
||||||
check("not (f() and g() or 0)")
|
|
||||||
check("f() == g()")
|
|
||||||
i = 4
|
|
||||||
check("i == f()")
|
|
||||||
check("len(f()) == 0")
|
|
||||||
check("isinstance(2+3+4, float)")
|
|
||||||
|
|
||||||
run("x = i")
|
|
||||||
check("x == 5")
|
|
||||||
|
|
||||||
run("assert not f(), 'oops'")
|
|
||||||
run("a, b, c = 1, 2")
|
|
||||||
run("a, b, c = f()")
|
|
||||||
|
|
||||||
check("max([f(),g()]) == 4")
|
|
||||||
check("'hello'[g()] == 'h'")
|
|
||||||
run("'guk%d' % h(f())")
|
|
|
@ -1,96 +0,0 @@
|
||||||
import sys
|
|
||||||
|
|
||||||
import py
|
|
||||||
from .code import Frame
|
|
||||||
|
|
||||||
BuiltinAssertionError = py.builtin.builtins.AssertionError
|
|
||||||
|
|
||||||
_reprcompare = None # if set, will be called by assert reinterp for comparison ops
|
|
||||||
|
|
||||||
def _format_explanation(explanation):
|
|
||||||
"""This formats an explanation
|
|
||||||
|
|
||||||
Normally all embedded newlines are escaped, however there are
|
|
||||||
three exceptions: \n{, \n} and \n~. The first two are intended
|
|
||||||
cover nested explanations, see function and attribute explanations
|
|
||||||
for examples (.visit_Call(), visit_Attribute()). The last one is
|
|
||||||
for when one explanation needs to span multiple lines, e.g. when
|
|
||||||
displaying diffs.
|
|
||||||
"""
|
|
||||||
raw_lines = (explanation or '').split('\n')
|
|
||||||
# escape newlines not followed by {, } and ~
|
|
||||||
lines = [raw_lines[0]]
|
|
||||||
for l in raw_lines[1:]:
|
|
||||||
if l.startswith('{') or l.startswith('}') or l.startswith('~'):
|
|
||||||
lines.append(l)
|
|
||||||
else:
|
|
||||||
lines[-1] += '\\n' + l
|
|
||||||
|
|
||||||
result = lines[:1]
|
|
||||||
stack = [0]
|
|
||||||
stackcnt = [0]
|
|
||||||
for line in lines[1:]:
|
|
||||||
if line.startswith('{'):
|
|
||||||
if stackcnt[-1]:
|
|
||||||
s = 'and '
|
|
||||||
else:
|
|
||||||
s = 'where '
|
|
||||||
stack.append(len(result))
|
|
||||||
stackcnt[-1] += 1
|
|
||||||
stackcnt.append(0)
|
|
||||||
result.append(' +' + ' '*(len(stack)-1) + s + line[1:])
|
|
||||||
elif line.startswith('}'):
|
|
||||||
assert line.startswith('}')
|
|
||||||
stack.pop()
|
|
||||||
stackcnt.pop()
|
|
||||||
result[stack[-1]] += line[1:]
|
|
||||||
else:
|
|
||||||
assert line.startswith('~')
|
|
||||||
result.append(' '*len(stack) + line[1:])
|
|
||||||
assert len(stack) == 1
|
|
||||||
return '\n'.join(result)
|
|
||||||
|
|
||||||
|
|
||||||
class AssertionError(BuiltinAssertionError):
|
|
||||||
def __init__(self, *args):
|
|
||||||
BuiltinAssertionError.__init__(self, *args)
|
|
||||||
if args:
|
|
||||||
try:
|
|
||||||
self.msg = str(args[0])
|
|
||||||
except py.builtin._sysex:
|
|
||||||
raise
|
|
||||||
except:
|
|
||||||
self.msg = "<[broken __repr__] %s at %0xd>" %(
|
|
||||||
args[0].__class__, id(args[0]))
|
|
||||||
else:
|
|
||||||
f = Frame(sys._getframe(1))
|
|
||||||
try:
|
|
||||||
source = f.code.fullsource
|
|
||||||
if source is not None:
|
|
||||||
try:
|
|
||||||
source = source.getstatement(f.lineno, assertion=True)
|
|
||||||
except IndexError:
|
|
||||||
source = None
|
|
||||||
else:
|
|
||||||
source = str(source.deindent()).strip()
|
|
||||||
except py.error.ENOENT:
|
|
||||||
source = None
|
|
||||||
# this can also occur during reinterpretation, when the
|
|
||||||
# co_filename is set to "<run>".
|
|
||||||
if source:
|
|
||||||
self.msg = reinterpret(source, f, should_fail=True)
|
|
||||||
else:
|
|
||||||
self.msg = "<could not determine information>"
|
|
||||||
if not self.args:
|
|
||||||
self.args = (self.msg,)
|
|
||||||
|
|
||||||
if sys.version_info > (3, 0):
|
|
||||||
AssertionError.__module__ = "builtins"
|
|
||||||
reinterpret_old = "old reinterpretation not available for py3"
|
|
||||||
else:
|
|
||||||
from _pytest._code._assertionold import interpret as reinterpret_old
|
|
||||||
if sys.version_info >= (2, 6) or (sys.platform.startswith("java")):
|
|
||||||
from _pytest._code._assertionnew import interpret as reinterpret
|
|
||||||
else:
|
|
||||||
reinterpret = reinterpret_old
|
|
||||||
|
|
|
@ -177,11 +177,11 @@ class TracebackEntry(object):
|
||||||
def reinterpret(self):
|
def reinterpret(self):
|
||||||
"""Reinterpret the failing statement and returns a detailed information
|
"""Reinterpret the failing statement and returns a detailed information
|
||||||
about what operations are performed."""
|
about what operations are performed."""
|
||||||
import _pytest._code
|
from _pytest.assertion.reinterpret import reinterpret
|
||||||
if self.exprinfo is None:
|
if self.exprinfo is None:
|
||||||
source = str(self.statement).strip()
|
source = py.builtin._totext(self.statement).strip()
|
||||||
x = _pytest._code._reinterpret(source, self.frame, should_fail=True)
|
x = reinterpret(source, self.frame, should_fail=True)
|
||||||
if not isinstance(x, str):
|
if not py.builtin._istext(x):
|
||||||
raise TypeError("interpret returned non-string %r" % (x,))
|
raise TypeError("interpret returned non-string %r" % (x,))
|
||||||
self.exprinfo = x
|
self.exprinfo = x
|
||||||
return self.exprinfo
|
return self.exprinfo
|
||||||
|
@ -760,10 +760,10 @@ oldbuiltins = {}
|
||||||
def patch_builtins(assertion=True, compile=True):
|
def patch_builtins(assertion=True, compile=True):
|
||||||
""" put compile and AssertionError builtins to Python's builtins. """
|
""" put compile and AssertionError builtins to Python's builtins. """
|
||||||
if assertion:
|
if assertion:
|
||||||
from _pytest._code import assertion
|
from _pytest.assertion import reinterpret
|
||||||
l = oldbuiltins.setdefault('AssertionError', [])
|
l = oldbuiltins.setdefault('AssertionError', [])
|
||||||
l.append(py.builtin.builtins.AssertionError)
|
l.append(py.builtin.builtins.AssertionError)
|
||||||
py.builtin.builtins.AssertionError = assertion.AssertionError
|
py.builtin.builtins.AssertionError = reinterpret.AssertionError
|
||||||
if compile:
|
if compile:
|
||||||
import _pytest._code
|
import _pytest._code
|
||||||
l = oldbuiltins.setdefault('compile', [])
|
l = oldbuiltins.setdefault('compile', [])
|
||||||
|
|
|
@ -1,309 +0,0 @@
|
||||||
import pytest, py
|
|
||||||
|
|
||||||
def exvalue():
|
|
||||||
return py.std.sys.exc_info()[1]
|
|
||||||
|
|
||||||
def f():
|
|
||||||
return 2
|
|
||||||
|
|
||||||
def test_assert():
|
|
||||||
try:
|
|
||||||
assert f() == 3
|
|
||||||
except AssertionError:
|
|
||||||
e = exvalue()
|
|
||||||
s = str(e)
|
|
||||||
assert s.startswith('assert 2 == 3\n')
|
|
||||||
|
|
||||||
|
|
||||||
def test_assert_within_finally():
|
|
||||||
excinfo = pytest.raises(ZeroDivisionError, """
|
|
||||||
try:
|
|
||||||
1/0
|
|
||||||
finally:
|
|
||||||
i = 42
|
|
||||||
""")
|
|
||||||
s = excinfo.exconly()
|
|
||||||
assert py.std.re.search("division.+by zero", s) is not None
|
|
||||||
|
|
||||||
#def g():
|
|
||||||
# A.f()
|
|
||||||
#excinfo = getexcinfo(TypeError, g)
|
|
||||||
#msg = getmsg(excinfo)
|
|
||||||
#assert msg.find("must be called with A") != -1
|
|
||||||
|
|
||||||
|
|
||||||
def test_assert_multiline_1():
|
|
||||||
try:
|
|
||||||
assert (f() ==
|
|
||||||
3)
|
|
||||||
except AssertionError:
|
|
||||||
e = exvalue()
|
|
||||||
s = str(e)
|
|
||||||
assert s.startswith('assert 2 == 3\n')
|
|
||||||
|
|
||||||
def test_assert_multiline_2():
|
|
||||||
try:
|
|
||||||
assert (f() == (4,
|
|
||||||
3)[-1])
|
|
||||||
except AssertionError:
|
|
||||||
e = exvalue()
|
|
||||||
s = str(e)
|
|
||||||
assert s.startswith('assert 2 ==')
|
|
||||||
|
|
||||||
def test_in():
|
|
||||||
try:
|
|
||||||
assert "hi" in [1, 2]
|
|
||||||
except AssertionError:
|
|
||||||
e = exvalue()
|
|
||||||
s = str(e)
|
|
||||||
assert s.startswith("assert 'hi' in")
|
|
||||||
|
|
||||||
def test_is():
|
|
||||||
try:
|
|
||||||
assert 1 is 2
|
|
||||||
except AssertionError:
|
|
||||||
e = exvalue()
|
|
||||||
s = str(e)
|
|
||||||
assert s.startswith("assert 1 is 2")
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skipif("sys.version_info < (2,6)")
|
|
||||||
def test_attrib():
|
|
||||||
class Foo(object):
|
|
||||||
b = 1
|
|
||||||
i = Foo()
|
|
||||||
try:
|
|
||||||
assert i.b == 2
|
|
||||||
except AssertionError:
|
|
||||||
e = exvalue()
|
|
||||||
s = str(e)
|
|
||||||
assert s.startswith("assert 1 == 2")
|
|
||||||
|
|
||||||
@pytest.mark.skipif("sys.version_info < (2,6)")
|
|
||||||
def test_attrib_inst():
|
|
||||||
class Foo(object):
|
|
||||||
b = 1
|
|
||||||
try:
|
|
||||||
assert Foo().b == 2
|
|
||||||
except AssertionError:
|
|
||||||
e = exvalue()
|
|
||||||
s = str(e)
|
|
||||||
assert s.startswith("assert 1 == 2")
|
|
||||||
|
|
||||||
def test_len():
|
|
||||||
l = list(range(42))
|
|
||||||
try:
|
|
||||||
assert len(l) == 100
|
|
||||||
except AssertionError:
|
|
||||||
e = exvalue()
|
|
||||||
s = str(e)
|
|
||||||
assert s.startswith("assert 42 == 100")
|
|
||||||
assert "where 42 = len([" in s
|
|
||||||
|
|
||||||
|
|
||||||
def test_assert_keyword_arg():
|
|
||||||
def f(x=3):
|
|
||||||
return False
|
|
||||||
try:
|
|
||||||
assert f(x=5)
|
|
||||||
except AssertionError:
|
|
||||||
e = exvalue()
|
|
||||||
assert "x=5" in e.msg
|
|
||||||
|
|
||||||
# These tests should both fail, but should fail nicely...
|
|
||||||
class WeirdRepr:
|
|
||||||
def __repr__(self):
|
|
||||||
return '<WeirdRepr\nsecond line>'
|
|
||||||
|
|
||||||
def bug_test_assert_repr():
|
|
||||||
v = WeirdRepr()
|
|
||||||
try:
|
|
||||||
assert v == 1
|
|
||||||
except AssertionError:
|
|
||||||
e = exvalue()
|
|
||||||
assert e.msg.find('WeirdRepr') != -1
|
|
||||||
assert e.msg.find('second line') != -1
|
|
||||||
assert 0
|
|
||||||
|
|
||||||
def test_assert_non_string():
|
|
||||||
try:
|
|
||||||
assert 0, ['list']
|
|
||||||
except AssertionError:
|
|
||||||
e = exvalue()
|
|
||||||
assert e.msg.find("list") != -1
|
|
||||||
|
|
||||||
def test_assert_implicit_multiline():
|
|
||||||
try:
|
|
||||||
x = [1,2,3]
|
|
||||||
assert x != [1,
|
|
||||||
2, 3]
|
|
||||||
except AssertionError:
|
|
||||||
e = exvalue()
|
|
||||||
assert e.msg.find('assert [1, 2, 3] !=') != -1
|
|
||||||
|
|
||||||
|
|
||||||
def test_assert_with_brokenrepr_arg():
|
|
||||||
class BrokenRepr:
|
|
||||||
def __repr__(self): 0 / 0
|
|
||||||
e = AssertionError(BrokenRepr())
|
|
||||||
if e.msg.find("broken __repr__") == -1:
|
|
||||||
pytest.fail("broken __repr__ not handle correctly")
|
|
||||||
|
|
||||||
def test_multiple_statements_per_line():
|
|
||||||
try:
|
|
||||||
a = 1; assert a == 2
|
|
||||||
except AssertionError:
|
|
||||||
e = exvalue()
|
|
||||||
assert "assert 1 == 2" in e.msg
|
|
||||||
|
|
||||||
def test_power():
|
|
||||||
try:
|
|
||||||
assert 2**3 == 7
|
|
||||||
except AssertionError:
|
|
||||||
e = exvalue()
|
|
||||||
assert "assert (2 ** 3) == 7" in e.msg
|
|
||||||
|
|
||||||
|
|
||||||
class TestView:
|
|
||||||
|
|
||||||
def setup_class(cls):
|
|
||||||
cls.View = pytest.importorskip("_pytest._code._assertionold").View
|
|
||||||
|
|
||||||
def test_class_dispatch(self):
|
|
||||||
### Use a custom class hierarchy with existing instances
|
|
||||||
|
|
||||||
class Picklable(self.View):
|
|
||||||
pass
|
|
||||||
|
|
||||||
class Simple(Picklable):
|
|
||||||
__view__ = object
|
|
||||||
def pickle(self):
|
|
||||||
return repr(self.__obj__)
|
|
||||||
|
|
||||||
class Seq(Picklable):
|
|
||||||
__view__ = list, tuple, dict
|
|
||||||
def pickle(self):
|
|
||||||
return ';'.join(
|
|
||||||
[Picklable(item).pickle() for item in self.__obj__])
|
|
||||||
|
|
||||||
class Dict(Seq):
|
|
||||||
__view__ = dict
|
|
||||||
def pickle(self):
|
|
||||||
return Seq.pickle(self) + '!' + Seq(self.values()).pickle()
|
|
||||||
|
|
||||||
assert Picklable(123).pickle() == '123'
|
|
||||||
assert Picklable([1,[2,3],4]).pickle() == '1;2;3;4'
|
|
||||||
assert Picklable({1:2}).pickle() == '1!2'
|
|
||||||
|
|
||||||
def test_viewtype_class_hierarchy(self):
|
|
||||||
# Use a custom class hierarchy based on attributes of existing instances
|
|
||||||
class Operation:
|
|
||||||
"Existing class that I don't want to change."
|
|
||||||
def __init__(self, opname, *args):
|
|
||||||
self.opname = opname
|
|
||||||
self.args = args
|
|
||||||
|
|
||||||
existing = [Operation('+', 4, 5),
|
|
||||||
Operation('getitem', '', 'join'),
|
|
||||||
Operation('setattr', 'x', 'y', 3),
|
|
||||||
Operation('-', 12, 1)]
|
|
||||||
|
|
||||||
class PyOp(self.View):
|
|
||||||
def __viewkey__(self):
|
|
||||||
return self.opname
|
|
||||||
def generate(self):
|
|
||||||
return '%s(%s)' % (self.opname, ', '.join(map(repr, self.args)))
|
|
||||||
|
|
||||||
class PyBinaryOp(PyOp):
|
|
||||||
__view__ = ('+', '-', '*', '/')
|
|
||||||
def generate(self):
|
|
||||||
return '%s %s %s' % (self.args[0], self.opname, self.args[1])
|
|
||||||
|
|
||||||
codelines = [PyOp(op).generate() for op in existing]
|
|
||||||
assert codelines == ["4 + 5", "getitem('', 'join')",
|
|
||||||
"setattr('x', 'y', 3)", "12 - 1"]
|
|
||||||
|
|
||||||
def test_underscore_api():
|
|
||||||
import _pytest._code
|
|
||||||
_pytest._code._AssertionError
|
|
||||||
_pytest._code._reinterpret_old # used by pypy
|
|
||||||
_pytest._code._reinterpret
|
|
||||||
|
|
||||||
@pytest.mark.skipif("sys.version_info < (2,6)")
|
|
||||||
def test_assert_customizable_reprcompare(monkeypatch):
|
|
||||||
util = pytest.importorskip("_pytest.assertion.util")
|
|
||||||
monkeypatch.setattr(util, '_reprcompare', lambda *args: 'hello')
|
|
||||||
try:
|
|
||||||
assert 3 == 4
|
|
||||||
except AssertionError:
|
|
||||||
e = exvalue()
|
|
||||||
s = str(e)
|
|
||||||
assert "hello" in s
|
|
||||||
|
|
||||||
def test_assert_long_source_1():
|
|
||||||
try:
|
|
||||||
assert len == [
|
|
||||||
(None, ['somet text', 'more text']),
|
|
||||||
]
|
|
||||||
except AssertionError:
|
|
||||||
e = exvalue()
|
|
||||||
s = str(e)
|
|
||||||
assert 're-run' not in s
|
|
||||||
assert 'somet text' in s
|
|
||||||
|
|
||||||
def test_assert_long_source_2():
|
|
||||||
try:
|
|
||||||
assert(len == [
|
|
||||||
(None, ['somet text', 'more text']),
|
|
||||||
])
|
|
||||||
except AssertionError:
|
|
||||||
e = exvalue()
|
|
||||||
s = str(e)
|
|
||||||
assert 're-run' not in s
|
|
||||||
assert 'somet text' in s
|
|
||||||
|
|
||||||
def test_assert_raise_alias(testdir):
|
|
||||||
testdir.makepyfile("""
|
|
||||||
import sys
|
|
||||||
EX = AssertionError
|
|
||||||
def test_hello():
|
|
||||||
raise EX("hello"
|
|
||||||
"multi"
|
|
||||||
"line")
|
|
||||||
""")
|
|
||||||
result = testdir.runpytest()
|
|
||||||
result.stdout.fnmatch_lines([
|
|
||||||
"*def test_hello*",
|
|
||||||
"*raise EX*",
|
|
||||||
"*1 failed*",
|
|
||||||
])
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skipif("sys.version_info < (2,5)")
|
|
||||||
def test_assert_raise_subclass():
|
|
||||||
class SomeEx(AssertionError):
|
|
||||||
def __init__(self, *args):
|
|
||||||
super(SomeEx, self).__init__()
|
|
||||||
try:
|
|
||||||
raise SomeEx("hello")
|
|
||||||
except AssertionError:
|
|
||||||
s = str(exvalue())
|
|
||||||
assert 're-run' not in s
|
|
||||||
assert 'could not determine' in s
|
|
||||||
|
|
||||||
def test_assert_raises_in_nonzero_of_object_pytest_issue10():
|
|
||||||
class A(object):
|
|
||||||
def __nonzero__(self):
|
|
||||||
raise ValueError(42)
|
|
||||||
def __lt__(self, other):
|
|
||||||
return A()
|
|
||||||
def __repr__(self):
|
|
||||||
return "<MY42 object>"
|
|
||||||
def myany(x):
|
|
||||||
return True
|
|
||||||
try:
|
|
||||||
assert not(myany(A() < 0))
|
|
||||||
except AssertionError:
|
|
||||||
e = exvalue()
|
|
||||||
s = str(e)
|
|
||||||
assert "<MY42 object> < 0" in s
|
|
Loading…
Reference in New Issue