Remove assertion reinterpretation
The assertion reinterpretation is an old backwards compatibility mode which was no longer being maintained on feature-parity with the assertion rewriting mode. It was also responsible for some dubious patching of builtins and test with side-effects would suddenly start passing. Since re-writing has been the default for a long time and plugins are now also re-written it is time to retire reinterpretation.
This commit is contained in:
parent
ee374e3b80
commit
d1852a48b7
|
@ -6,6 +6,10 @@
|
||||||
A number of incompatible changes were made in this release, with the intent of removing features deprecated for a long
|
A number of incompatible changes were made in this release, with the intent of removing features deprecated for a long
|
||||||
time or change existing behaviors in order to make them less surprising/more useful.
|
time or change existing behaviors in order to make them less surprising/more useful.
|
||||||
|
|
||||||
|
* Reinterpretation mode has now been removed. Only plain and rewrite
|
||||||
|
mode are available, consequently the ``--assert=reinterp`` option is
|
||||||
|
no longer available. Thanks `@flub`_ for the PR.
|
||||||
|
|
||||||
* The following deprecated commandline options were removed:
|
* The following deprecated commandline options were removed:
|
||||||
|
|
||||||
* ``--genscript``: no longer supported;
|
* ``--genscript``: no longer supported;
|
||||||
|
|
|
@ -4,9 +4,6 @@ from .code import ExceptionInfo # noqa
|
||||||
from .code import Frame # noqa
|
from .code import Frame # noqa
|
||||||
from .code import Traceback # noqa
|
from .code import Traceback # noqa
|
||||||
from .code import getrawcode # noqa
|
from .code import getrawcode # noqa
|
||||||
from .code import patch_builtins # noqa
|
|
||||||
from .code import unpatch_builtins # noqa
|
|
||||||
from .source import Source # noqa
|
from .source import Source # noqa
|
||||||
from .source import compile_ as compile # noqa
|
from .source import compile_ as compile # noqa
|
||||||
from .source import getfslineno # noqa
|
from .source import getfslineno # noqa
|
||||||
|
|
||||||
|
|
|
@ -179,18 +179,6 @@ class TracebackEntry(object):
|
||||||
return self.frame.f_locals
|
return self.frame.f_locals
|
||||||
locals = property(getlocals, None, None, "locals of underlaying frame")
|
locals = property(getlocals, None, None, "locals of underlaying frame")
|
||||||
|
|
||||||
def reinterpret(self):
|
|
||||||
"""Reinterpret the failing statement and returns a detailed information
|
|
||||||
about what operations are performed."""
|
|
||||||
from _pytest.assertion.reinterpret import reinterpret
|
|
||||||
if self.exprinfo is None:
|
|
||||||
source = py.builtin._totext(self.statement).strip()
|
|
||||||
x = reinterpret(source, self.frame, should_fail=True)
|
|
||||||
if not py.builtin._istext(x):
|
|
||||||
raise TypeError("interpret returned non-string %r" % (x,))
|
|
||||||
self.exprinfo = x
|
|
||||||
return self.exprinfo
|
|
||||||
|
|
||||||
def getfirstlinesource(self):
|
def getfirstlinesource(self):
|
||||||
# on Jython this firstlineno can be -1 apparently
|
# on Jython this firstlineno can be -1 apparently
|
||||||
return max(self.frame.code.firstlineno, 0)
|
return max(self.frame.code.firstlineno, 0)
|
||||||
|
@ -830,29 +818,6 @@ class ReprFuncArgs(TerminalRepr):
|
||||||
tw.line("")
|
tw.line("")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
oldbuiltins = {}
|
|
||||||
|
|
||||||
def patch_builtins(assertion=True, compile=True):
|
|
||||||
""" put compile and AssertionError builtins to Python's builtins. """
|
|
||||||
if assertion:
|
|
||||||
from _pytest.assertion import reinterpret
|
|
||||||
l = oldbuiltins.setdefault('AssertionError', [])
|
|
||||||
l.append(py.builtin.builtins.AssertionError)
|
|
||||||
py.builtin.builtins.AssertionError = reinterpret.AssertionError
|
|
||||||
if compile:
|
|
||||||
import _pytest._code
|
|
||||||
l = oldbuiltins.setdefault('compile', [])
|
|
||||||
l.append(py.builtin.builtins.compile)
|
|
||||||
py.builtin.builtins.compile = _pytest._code.compile
|
|
||||||
|
|
||||||
def unpatch_builtins(assertion=True, compile=True):
|
|
||||||
""" remove compile and AssertionError builtins from Python builtins. """
|
|
||||||
if assertion:
|
|
||||||
py.builtin.builtins.AssertionError = oldbuiltins['AssertionError'].pop()
|
|
||||||
if compile:
|
|
||||||
py.builtin.builtins.compile = oldbuiltins['compile'].pop()
|
|
||||||
|
|
||||||
def getrawcode(obj, trycall=True):
|
def getrawcode(obj, trycall=True):
|
||||||
""" return code object for given function. """
|
""" return code object for given function. """
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -14,16 +14,14 @@ def pytest_addoption(parser):
|
||||||
group.addoption('--assert',
|
group.addoption('--assert',
|
||||||
action="store",
|
action="store",
|
||||||
dest="assertmode",
|
dest="assertmode",
|
||||||
choices=("rewrite", "reinterp", "plain",),
|
choices=("rewrite", "plain",),
|
||||||
default="rewrite",
|
default="rewrite",
|
||||||
metavar="MODE",
|
metavar="MODE",
|
||||||
help="""control assertion debugging tools. 'plain'
|
help="""Control assertion debugging tools. 'plain'
|
||||||
performs no assertion debugging. 'reinterp'
|
performs no assertion debugging. 'rewrite'
|
||||||
reinterprets assert statements after they failed
|
(the default) rewrites assert statements in
|
||||||
to provide assertion expression information.
|
test modules on import to provide assert
|
||||||
'rewrite' (the default) rewrites assert
|
expression information.""")
|
||||||
statements in test modules on import to
|
|
||||||
provide assert expression information. """)
|
|
||||||
|
|
||||||
|
|
||||||
def pytest_namespace():
|
def pytest_namespace():
|
||||||
|
@ -60,37 +58,21 @@ class AssertionState:
|
||||||
def __init__(self, config, mode):
|
def __init__(self, config, mode):
|
||||||
self.mode = mode
|
self.mode = mode
|
||||||
self.trace = config.trace.root.get("assertion")
|
self.trace = config.trace.root.get("assertion")
|
||||||
|
self.hook = None
|
||||||
|
|
||||||
|
|
||||||
def install_importhook(config, mode):
|
def install_importhook(config):
|
||||||
if mode == "rewrite":
|
"""Try to install the rewrite hook, raise SystemError if it fails."""
|
||||||
try:
|
|
||||||
import ast # noqa
|
|
||||||
except ImportError:
|
|
||||||
mode = "reinterp"
|
|
||||||
else:
|
|
||||||
# Both Jython and CPython 2.6.0 have AST bugs that make the
|
# Both Jython and CPython 2.6.0 have AST bugs that make the
|
||||||
# assertion rewriting hook malfunction.
|
# assertion rewriting hook malfunction.
|
||||||
if (sys.platform.startswith('java') or
|
if (sys.platform.startswith('java') or
|
||||||
sys.version_info[:3] == (2, 6, 0)):
|
sys.version_info[:3] == (2, 6, 0)):
|
||||||
mode = "reinterp"
|
raise SystemError('rewrite not supported')
|
||||||
|
|
||||||
config._assertstate = AssertionState(config, mode)
|
config._assertstate = AssertionState(config, 'rewrite')
|
||||||
|
config._assertstate.hook = hook = rewrite.AssertionRewritingHook(config)
|
||||||
_load_modules(mode)
|
|
||||||
from _pytest.monkeypatch import MonkeyPatch
|
|
||||||
m = MonkeyPatch()
|
|
||||||
config._cleanup.append(m.undo)
|
|
||||||
m.setattr(py.builtin.builtins, 'AssertionError',
|
|
||||||
reinterpret.AssertionError) # noqa
|
|
||||||
|
|
||||||
hook = None
|
|
||||||
if mode == "rewrite":
|
|
||||||
hook = rewrite.AssertionRewritingHook(config) # noqa
|
|
||||||
sys.meta_path.insert(0, hook)
|
sys.meta_path.insert(0, hook)
|
||||||
|
config._assertstate.trace('installed rewrite import hook')
|
||||||
config._assertstate.hook = hook
|
|
||||||
config._assertstate.trace("configured with mode set to %r" % (mode,))
|
|
||||||
def undo():
|
def undo():
|
||||||
hook = config._assertstate.hook
|
hook = config._assertstate.hook
|
||||||
if hook is not None and hook in sys.meta_path:
|
if hook is not None and hook in sys.meta_path:
|
||||||
|
@ -169,13 +151,5 @@ def pytest_sessionfinish(session):
|
||||||
assertstate.hook.set_session(None)
|
assertstate.hook.set_session(None)
|
||||||
|
|
||||||
|
|
||||||
def _load_modules(mode):
|
|
||||||
"""Lazily import assertion related code."""
|
|
||||||
global rewrite, reinterpret
|
|
||||||
from _pytest.assertion import reinterpret # noqa
|
|
||||||
if mode == "rewrite":
|
|
||||||
from _pytest.assertion import rewrite # noqa
|
|
||||||
|
|
||||||
|
|
||||||
# Expose this plugin's implementation for the pytest_assertrepr_compare hook
|
# Expose this plugin's implementation for the pytest_assertrepr_compare hook
|
||||||
pytest_assertrepr_compare = util.assertrepr_compare
|
pytest_assertrepr_compare = util.assertrepr_compare
|
||||||
|
|
|
@ -1,407 +0,0 @@
|
||||||
"""
|
|
||||||
Find intermediate evalutation results in assert statements through builtin AST.
|
|
||||||
"""
|
|
||||||
import ast
|
|
||||||
import sys
|
|
||||||
|
|
||||||
import _pytest._code
|
|
||||||
import py
|
|
||||||
from _pytest.assertion import util
|
|
||||||
u = py.builtin._totext
|
|
||||||
|
|
||||||
|
|
||||||
class AssertionError(util.BuiltinAssertionError):
|
|
||||||
def __init__(self, *args):
|
|
||||||
util.BuiltinAssertionError.__init__(self, *args)
|
|
||||||
if args:
|
|
||||||
# on Python2.6 we get len(args)==2 for: assert 0, (x,y)
|
|
||||||
# on Python2.7 and above we always get len(args) == 1
|
|
||||||
# with args[0] being the (x,y) tuple.
|
|
||||||
if len(args) > 1:
|
|
||||||
toprint = args
|
|
||||||
else:
|
|
||||||
toprint = args[0]
|
|
||||||
try:
|
|
||||||
self.msg = u(toprint)
|
|
||||||
except Exception:
|
|
||||||
self.msg = u(
|
|
||||||
"<[broken __repr__] %s at %0xd>"
|
|
||||||
% (toprint.__class__, id(toprint)))
|
|
||||||
else:
|
|
||||||
f = _pytest._code.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"
|
|
||||||
|
|
||||||
if sys.platform.startswith("java"):
|
|
||||||
# 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)
|
|
||||||
|
|
||||||
try:
|
|
||||||
_Starred = ast.Starred
|
|
||||||
except AttributeError:
|
|
||||||
# Python 2. Define a dummy class so isinstance() will always be False.
|
|
||||||
class _Starred(object): pass
|
|
||||||
|
|
||||||
|
|
||||||
class Failure(Exception):
|
|
||||||
"""Error found while interpreting AST."""
|
|
||||||
|
|
||||||
def __init__(self, explanation=""):
|
|
||||||
self.cause = sys.exc_info()
|
|
||||||
self.explanation = explanation
|
|
||||||
|
|
||||||
|
|
||||||
def reinterpret(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 --assert=plain)")
|
|
||||||
|
|
||||||
def run(offending_line, frame=None):
|
|
||||||
if frame is None:
|
|
||||||
frame = _pytest._code.Frame(sys._getframe(1))
|
|
||||||
return reinterpret(offending_line, frame)
|
|
||||||
|
|
||||||
def getfailure(e):
|
|
||||||
explanation = util.format_explanation(e.explanation)
|
|
||||||
value = e.cause[1]
|
|
||||||
if str(value):
|
|
||||||
lines = explanation.split('\n')
|
|
||||||
lines[0] += " << %s" % (value,)
|
|
||||||
explanation = '\n'.join(lines)
|
|
||||||
text = "%s: %s" % (e.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 = None
|
|
||||||
if local is None or not self.frame.is_true(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 self.frame.is_true(result):
|
|
||||||
break
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
raise
|
|
||||||
except:
|
|
||||||
break
|
|
||||||
left_explanation, left_result = next_explanation, next_result
|
|
||||||
|
|
||||||
if util._reprcompare is not None:
|
|
||||||
res = util._reprcompare(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)
|
|
||||||
if isinstance(arg, _Starred):
|
|
||||||
arg_name = "__exprinfo_star"
|
|
||||||
ns[arg_name] = arg_result
|
|
||||||
arguments.append("*%s" % (arg_name,))
|
|
||||||
arg_explanations.append("*%s" % (arg_explanation,))
|
|
||||||
else:
|
|
||||||
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)
|
|
||||||
if keyword.arg:
|
|
||||||
arg_name = "__exprinfo_%s" % (len(ns),)
|
|
||||||
keyword_source = "%s=%%s" % (keyword.arg)
|
|
||||||
arguments.append(keyword_source % (arg_name,))
|
|
||||||
arg_explanations.append(keyword_source % (arg_explanation,))
|
|
||||||
else:
|
|
||||||
arg_name = "__exprinfo_kwds"
|
|
||||||
arguments.append("**%s" % (arg_name,))
|
|
||||||
arg_explanations.append("**%s" % (arg_explanation,))
|
|
||||||
|
|
||||||
ns[arg_name] = arg_result
|
|
||||||
|
|
||||||
if getattr(call, 'starargs', None):
|
|
||||||
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 getattr(call, 'kwargs', None):
|
|
||||||
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:
|
|
||||||
try:
|
|
||||||
result = self.frame.eval(co, __exprinfo_expr=source_result)
|
|
||||||
except AttributeError:
|
|
||||||
# Maybe the attribute name needs to be mangled?
|
|
||||||
if not attr.attr.startswith("__") or attr.attr.endswith("__"):
|
|
||||||
raise
|
|
||||||
source = "getattr(__exprinfo_expr.__class__, '__name__', '')"
|
|
||||||
co = self._compile(source)
|
|
||||||
class_name = self.frame.eval(co, __exprinfo_expr=source_result)
|
|
||||||
mangled_attr = "_" + class_name + attr.attr
|
|
||||||
source = "__exprinfo_expr.%s" % (mangled_attr,)
|
|
||||||
co = self._compile(source)
|
|
||||||
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 = None
|
|
||||||
if from_instance is None or self.frame.is_true(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)
|
|
||||||
explanation = "assert %s" % (test_explanation,)
|
|
||||||
if not self.frame.is_true(test_result):
|
|
||||||
try:
|
|
||||||
raise util.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
|
|
||||||
|
|
|
@ -941,10 +941,12 @@ class Config(object):
|
||||||
"""
|
"""
|
||||||
ns, unknown_args = self._parser.parse_known_and_unknown_args(args)
|
ns, unknown_args = self._parser.parse_known_and_unknown_args(args)
|
||||||
mode = ns.assertmode
|
mode = ns.assertmode
|
||||||
self._warn_about_missing_assertion(mode)
|
if mode == 'rewrite':
|
||||||
if mode != 'plain':
|
try:
|
||||||
hook = _pytest.assertion.install_importhook(self, mode)
|
hook = _pytest.assertion.install_importhook(self)
|
||||||
if hook:
|
except SystemError:
|
||||||
|
mode = 'plain'
|
||||||
|
else:
|
||||||
self.pluginmanager.rewrite_hook = hook
|
self.pluginmanager.rewrite_hook = hook
|
||||||
for entrypoint in pkg_resources.iter_entry_points('pytest11'):
|
for entrypoint in pkg_resources.iter_entry_points('pytest11'):
|
||||||
for entry in entrypoint.dist._get_metadata('RECORD'):
|
for entry in entrypoint.dist._get_metadata('RECORD'):
|
||||||
|
@ -957,6 +959,7 @@ class Config(object):
|
||||||
elif is_package:
|
elif is_package:
|
||||||
package_name = os.path.dirname(fn)
|
package_name = os.path.dirname(fn)
|
||||||
hook.mark_rewrite(package_name)
|
hook.mark_rewrite(package_name)
|
||||||
|
self._warn_about_missing_assertion(mode)
|
||||||
|
|
||||||
def _warn_about_missing_assertion(self, mode):
|
def _warn_about_missing_assertion(self, mode):
|
||||||
try:
|
try:
|
||||||
|
@ -964,12 +967,13 @@ class Config(object):
|
||||||
except AssertionError:
|
except AssertionError:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
if mode == "rewrite":
|
if mode == 'plain':
|
||||||
specifically = ("assertions not in test modules or plugins"
|
sys.stderr.write("WARNING: ASSERTIONS ARE NOT EXECUTED"
|
||||||
"will be ignored")
|
" and FAILING TESTS WILL PASS. Are you"
|
||||||
|
" using python -O?")
|
||||||
else:
|
else:
|
||||||
specifically = "failing tests may report as passing"
|
sys.stderr.write("WARNING: assertions not in test modules or"
|
||||||
sys.stderr.write("WARNING: " + specifically +
|
" plugins will be ignored"
|
||||||
" because assert statements are not executed "
|
" because assert statements are not executed "
|
||||||
"by the underlying Python interpreter "
|
"by the underlying Python interpreter "
|
||||||
"(are you using python -O?)\n")
|
"(are you using python -O?)\n")
|
||||||
|
|
|
@ -66,24 +66,6 @@ def test_code_from_func():
|
||||||
assert co.path
|
assert co.path
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def test_builtin_patch_unpatch(monkeypatch):
|
|
||||||
cpy_builtin = py.builtin.builtins
|
|
||||||
comp = cpy_builtin.compile
|
|
||||||
def mycompile(*args, **kwargs):
|
|
||||||
return comp(*args, **kwargs)
|
|
||||||
class Sub(AssertionError):
|
|
||||||
pass
|
|
||||||
monkeypatch.setattr(cpy_builtin, 'AssertionError', Sub)
|
|
||||||
monkeypatch.setattr(cpy_builtin, 'compile', mycompile)
|
|
||||||
_pytest._code.patch_builtins()
|
|
||||||
assert cpy_builtin.AssertionError != Sub
|
|
||||||
assert cpy_builtin.compile != mycompile
|
|
||||||
_pytest._code.unpatch_builtins()
|
|
||||||
assert cpy_builtin.AssertionError is Sub
|
|
||||||
assert cpy_builtin.compile == mycompile
|
|
||||||
|
|
||||||
|
|
||||||
def test_unicode_handling():
|
def test_unicode_handling():
|
||||||
value = py.builtin._totext('\xc4\x85\xc4\x87\n', 'utf-8').encode('utf8')
|
value = py.builtin._totext('\xc4\x85\xc4\x87\n', 'utf-8').encode('utf8')
|
||||||
def f():
|
def f():
|
||||||
|
|
|
@ -274,18 +274,6 @@ class TestTraceback_f_g_h:
|
||||||
assert entry.lineno == co.firstlineno + 2
|
assert entry.lineno == co.firstlineno + 2
|
||||||
assert entry.frame.code.name == 'g'
|
assert entry.frame.code.name == 'g'
|
||||||
|
|
||||||
def hello(x):
|
|
||||||
x + 5
|
|
||||||
|
|
||||||
def test_tbentry_reinterpret():
|
|
||||||
try:
|
|
||||||
hello("hello")
|
|
||||||
except TypeError:
|
|
||||||
excinfo = _pytest._code.ExceptionInfo()
|
|
||||||
tbentry = excinfo.traceback[-1]
|
|
||||||
msg = tbentry.reinterpret()
|
|
||||||
assert msg.startswith("TypeError: ('hello' + 5)")
|
|
||||||
|
|
||||||
def test_excinfo_exconly():
|
def test_excinfo_exconly():
|
||||||
excinfo = pytest.raises(ValueError, h)
|
excinfo = pytest.raises(ValueError, h)
|
||||||
assert excinfo.exconly().startswith('ValueError')
|
assert excinfo.exconly().startswith('ValueError')
|
||||||
|
@ -431,7 +419,7 @@ class TestFormattedExcinfo:
|
||||||
assert lines == [
|
assert lines == [
|
||||||
' def f():',
|
' def f():',
|
||||||
'> assert 0',
|
'> assert 0',
|
||||||
'E assert 0'
|
'E AssertionError'
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -770,23 +758,6 @@ raise ValueError()
|
||||||
assert reprtb.extraline == "!!! Recursion detected (same locals & position)"
|
assert reprtb.extraline == "!!! Recursion detected (same locals & position)"
|
||||||
assert str(reprtb)
|
assert str(reprtb)
|
||||||
|
|
||||||
def test_tb_entry_AssertionError(self, importasmod):
|
|
||||||
# probably this test is a bit redundant
|
|
||||||
# as py/magic/testing/test_assertion.py
|
|
||||||
# already tests correctness of
|
|
||||||
# assertion-reinterpretation logic
|
|
||||||
mod = importasmod("""
|
|
||||||
def somefunc():
|
|
||||||
x = 1
|
|
||||||
assert x == 2
|
|
||||||
""")
|
|
||||||
excinfo = pytest.raises(AssertionError, mod.somefunc)
|
|
||||||
|
|
||||||
p = FormattedExcinfo()
|
|
||||||
reprentry = p.repr_traceback_entry(excinfo.traceback[-1], excinfo)
|
|
||||||
lines = reprentry.lines
|
|
||||||
assert lines[-1] == "E assert 1 == 2"
|
|
||||||
|
|
||||||
def test_reprexcinfo_getrepr(self, importasmod):
|
def test_reprexcinfo_getrepr(self, importasmod):
|
||||||
mod = importasmod("""
|
mod = importasmod("""
|
||||||
def f(x):
|
def f(x):
|
||||||
|
@ -935,21 +906,6 @@ raise ValueError()
|
||||||
repr.toterminal(tw)
|
repr.toterminal(tw)
|
||||||
assert tw.stringio.getvalue()
|
assert tw.stringio.getvalue()
|
||||||
|
|
||||||
|
|
||||||
def test_native_style(self):
|
|
||||||
excinfo = self.excinfo_from_exec("""
|
|
||||||
assert 0
|
|
||||||
""")
|
|
||||||
repr = excinfo.getrepr(style='native')
|
|
||||||
assert "assert 0" in str(repr.reprcrash)
|
|
||||||
s = str(repr)
|
|
||||||
assert s.startswith('Traceback (most recent call last):\n File')
|
|
||||||
assert s.endswith('\nAssertionError: assert 0')
|
|
||||||
assert 'exec (source.compile())' in s
|
|
||||||
# python 2.4 fails to get the source line for the assert
|
|
||||||
if py.std.sys.version_info >= (2, 5):
|
|
||||||
assert s.count('assert 0') == 2
|
|
||||||
|
|
||||||
def test_traceback_repr_style(self, importasmod):
|
def test_traceback_repr_style(self, importasmod):
|
||||||
mod = importasmod("""
|
mod = importasmod("""
|
||||||
def f():
|
def f():
|
||||||
|
|
|
@ -1,274 +0,0 @@
|
||||||
"PYTEST_DONT_REWRITE"
|
|
||||||
import py
|
|
||||||
import pytest
|
|
||||||
from _pytest.assertion import util
|
|
||||||
|
|
||||||
|
|
||||||
def exvalue():
|
|
||||||
return py.std.sys.exc_info()[1]
|
|
||||||
|
|
||||||
def f():
|
|
||||||
return 2
|
|
||||||
|
|
||||||
def test_not_being_rewritten():
|
|
||||||
assert "@py_builtins" not in globals()
|
|
||||||
|
|
||||||
def test_assert():
|
|
||||||
try:
|
|
||||||
assert f() == 3
|
|
||||||
except AssertionError:
|
|
||||||
e = exvalue()
|
|
||||||
s = str(e)
|
|
||||||
assert s.startswith('assert 2 == 3\n')
|
|
||||||
|
|
||||||
def test_assert_with_explicit_message():
|
|
||||||
try:
|
|
||||||
assert f() == 3, "hello"
|
|
||||||
except AssertionError:
|
|
||||||
e = exvalue()
|
|
||||||
assert e.msg == 'hello'
|
|
||||||
|
|
||||||
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")
|
|
||||||
|
|
||||||
|
|
||||||
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")
|
|
||||||
|
|
||||||
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_non_string_message():
|
|
||||||
class A:
|
|
||||||
def __str__(self):
|
|
||||||
return "hello"
|
|
||||||
try:
|
|
||||||
assert 0 == 1, A()
|
|
||||||
except AssertionError:
|
|
||||||
e = exvalue()
|
|
||||||
assert e.msg == "hello"
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
def test_private_class_variable():
|
|
||||||
class X:
|
|
||||||
def __init__(self):
|
|
||||||
self.__v = 41
|
|
||||||
def m(self):
|
|
||||||
assert self.__v == 42
|
|
||||||
try:
|
|
||||||
X().m()
|
|
||||||
except AssertionError:
|
|
||||||
e = exvalue()
|
|
||||||
assert "== 42" 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
|
|
||||||
|
|
||||||
|
|
||||||
def test_assert_customizable_reprcompare(monkeypatch):
|
|
||||||
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("""
|
|
||||||
"PYTEST_DONT_REWRITE"
|
|
||||||
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*",
|
|
||||||
])
|
|
||||||
|
|
||||||
|
|
||||||
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
|
|
|
@ -3,10 +3,8 @@ import sys
|
||||||
import textwrap
|
import textwrap
|
||||||
|
|
||||||
import _pytest.assertion as plugin
|
import _pytest.assertion as plugin
|
||||||
import _pytest._code
|
|
||||||
import py
|
import py
|
||||||
import pytest
|
import pytest
|
||||||
from _pytest.assertion import reinterpret
|
|
||||||
from _pytest.assertion import util
|
from _pytest.assertion import util
|
||||||
|
|
||||||
PY3 = sys.version_info >= (3, 0)
|
PY3 = sys.version_info >= (3, 0)
|
||||||
|
@ -23,14 +21,10 @@ def mock_config():
|
||||||
return Config()
|
return Config()
|
||||||
|
|
||||||
|
|
||||||
def interpret(expr):
|
|
||||||
return reinterpret.reinterpret(expr, _pytest._code.Frame(sys._getframe(1)))
|
|
||||||
|
|
||||||
|
|
||||||
class TestImportHookInstallation:
|
class TestImportHookInstallation:
|
||||||
|
|
||||||
@pytest.mark.parametrize('initial_conftest', [True, False])
|
@pytest.mark.parametrize('initial_conftest', [True, False])
|
||||||
@pytest.mark.parametrize('mode', ['plain', 'rewrite', 'reinterp'])
|
@pytest.mark.parametrize('mode', ['plain', 'rewrite'])
|
||||||
def test_conftest_assertion_rewrite(self, testdir, initial_conftest, mode):
|
def test_conftest_assertion_rewrite(self, testdir, initial_conftest, mode):
|
||||||
"""Test that conftest files are using assertion rewrite on import.
|
"""Test that conftest files are using assertion rewrite on import.
|
||||||
(#1619)
|
(#1619)
|
||||||
|
@ -57,13 +51,11 @@ class TestImportHookInstallation:
|
||||||
expected = 'E AssertionError'
|
expected = 'E AssertionError'
|
||||||
elif mode == 'rewrite':
|
elif mode == 'rewrite':
|
||||||
expected = '*assert 10 == 30*'
|
expected = '*assert 10 == 30*'
|
||||||
elif mode == 'reinterp':
|
|
||||||
expected = '*AssertionError:*was re-run*'
|
|
||||||
else:
|
else:
|
||||||
assert 0
|
assert 0
|
||||||
result.stdout.fnmatch_lines([expected])
|
result.stdout.fnmatch_lines([expected])
|
||||||
|
|
||||||
@pytest.mark.parametrize('mode', ['plain', 'rewrite', 'reinterp'])
|
@pytest.mark.parametrize('mode', ['plain', 'rewrite'])
|
||||||
def test_pytest_plugins_rewrite(self, testdir, mode):
|
def test_pytest_plugins_rewrite(self, testdir, mode):
|
||||||
contents = {
|
contents = {
|
||||||
'conftest.py': """
|
'conftest.py': """
|
||||||
|
@ -88,13 +80,11 @@ class TestImportHookInstallation:
|
||||||
expected = 'E AssertionError'
|
expected = 'E AssertionError'
|
||||||
elif mode == 'rewrite':
|
elif mode == 'rewrite':
|
||||||
expected = '*assert 10 == 30*'
|
expected = '*assert 10 == 30*'
|
||||||
elif mode == 'reinterp':
|
|
||||||
expected = '*AssertionError:*was re-run*'
|
|
||||||
else:
|
else:
|
||||||
assert 0
|
assert 0
|
||||||
result.stdout.fnmatch_lines([expected])
|
result.stdout.fnmatch_lines([expected])
|
||||||
|
|
||||||
@pytest.mark.parametrize('mode', ['plain', 'rewrite', 'reinterp'])
|
@pytest.mark.parametrize('mode', ['plain', 'rewrite'])
|
||||||
def test_installed_plugin_rewrite(self, testdir, mode):
|
def test_installed_plugin_rewrite(self, testdir, mode):
|
||||||
# Make sure the hook is installed early enough so that plugins
|
# Make sure the hook is installed early enough so that plugins
|
||||||
# installed via setuptools are re-written.
|
# installed via setuptools are re-written.
|
||||||
|
@ -161,8 +151,6 @@ class TestImportHookInstallation:
|
||||||
expected = 'E AssertionError'
|
expected = 'E AssertionError'
|
||||||
elif mode == 'rewrite':
|
elif mode == 'rewrite':
|
||||||
expected = '*assert 10 == 30*'
|
expected = '*assert 10 == 30*'
|
||||||
elif mode == 'reinterp':
|
|
||||||
expected = '*AssertionError:*was re-run*'
|
|
||||||
else:
|
else:
|
||||||
assert 0
|
assert 0
|
||||||
result.stdout.fnmatch_lines([expected])
|
result.stdout.fnmatch_lines([expected])
|
||||||
|
@ -206,7 +194,7 @@ class TestImportHookInstallation:
|
||||||
result.stdout.fnmatch_lines(['>*assert a == b*',
|
result.stdout.fnmatch_lines(['>*assert a == b*',
|
||||||
'E*assert 2 == 3*',
|
'E*assert 2 == 3*',
|
||||||
'>*assert l.pop() == 3*',
|
'>*assert l.pop() == 3*',
|
||||||
'E*AssertionError*re-run*'])
|
'E*AssertionError'])
|
||||||
|
|
||||||
|
|
||||||
class TestBinReprIntegration:
|
class TestBinReprIntegration:
|
||||||
|
@ -663,14 +651,6 @@ def test_assertion_options(testdir):
|
||||||
result = testdir.runpytest_subprocess("--assert=plain")
|
result = testdir.runpytest_subprocess("--assert=plain")
|
||||||
assert "3 == 4" not in result.stdout.str()
|
assert "3 == 4" not in result.stdout.str()
|
||||||
|
|
||||||
def test_old_assert_mode(testdir):
|
|
||||||
testdir.makepyfile("""
|
|
||||||
def test_in_old_mode():
|
|
||||||
assert "@py_builtins" not in globals()
|
|
||||||
""")
|
|
||||||
result = testdir.runpytest_subprocess("--assert=reinterp")
|
|
||||||
assert result.ret == 0
|
|
||||||
|
|
||||||
def test_triple_quoted_string_issue113(testdir):
|
def test_triple_quoted_string_issue113(testdir):
|
||||||
testdir.makepyfile("""
|
testdir.makepyfile("""
|
||||||
def test_hello():
|
def test_hello():
|
||||||
|
|
Loading…
Reference in New Issue