From d1852a48b7d38cb1cd36135ea92f83742d8782d6 Mon Sep 17 00:00:00 2001 From: Floris Bruynooghe Date: Fri, 15 Jul 2016 00:18:50 +0100 Subject: [PATCH] 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. --- CHANGELOG.rst | 4 + _pytest/_code/__init__.py | 3 - _pytest/_code/code.py | 35 --- _pytest/assertion/__init__.py | 62 ++--- _pytest/assertion/reinterpret.py | 407 ------------------------------- _pytest/config.py | 28 ++- testing/code/test_code.py | 18 -- testing/code/test_excinfo.py | 48 +--- testing/test_assertinterpret.py | 274 --------------------- testing/test_assertion.py | 28 +-- 10 files changed, 44 insertions(+), 863 deletions(-) delete mode 100644 _pytest/assertion/reinterpret.py delete mode 100644 testing/test_assertinterpret.py diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 0bc0e0102..66cf70c30 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -6,6 +6,10 @@ 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. +* 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: * ``--genscript``: no longer supported; diff --git a/_pytest/_code/__init__.py b/_pytest/_code/__init__.py index c046b9716..3463c11ea 100644 --- a/_pytest/_code/__init__.py +++ b/_pytest/_code/__init__.py @@ -4,9 +4,6 @@ from .code import ExceptionInfo # noqa from .code import Frame # noqa from .code import Traceback # 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 compile_ as compile # noqa from .source import getfslineno # noqa - diff --git a/_pytest/_code/code.py b/_pytest/_code/code.py index 0f1ffb918..0af5a3465 100644 --- a/_pytest/_code/code.py +++ b/_pytest/_code/code.py @@ -179,18 +179,6 @@ class TracebackEntry(object): return self.frame.f_locals 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): # on Jython this firstlineno can be -1 apparently return max(self.frame.code.firstlineno, 0) @@ -830,29 +818,6 @@ class ReprFuncArgs(TerminalRepr): 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): """ return code object for given function. """ try: diff --git a/_pytest/assertion/__init__.py b/_pytest/assertion/__init__.py index c231a4769..bca626931 100644 --- a/_pytest/assertion/__init__.py +++ b/_pytest/assertion/__init__.py @@ -14,16 +14,14 @@ def pytest_addoption(parser): group.addoption('--assert', action="store", dest="assertmode", - choices=("rewrite", "reinterp", "plain",), + choices=("rewrite", "plain",), default="rewrite", metavar="MODE", - help="""control assertion debugging tools. 'plain' - performs no assertion debugging. 'reinterp' - reinterprets assert statements after they failed - to provide assertion expression information. - 'rewrite' (the default) rewrites assert - statements in test modules on import to - provide assert expression information. """) + help="""Control assertion debugging tools. 'plain' + performs no assertion debugging. 'rewrite' + (the default) rewrites assert statements in + test modules on import to provide assert + expression information.""") def pytest_namespace(): @@ -60,37 +58,21 @@ class AssertionState: def __init__(self, config, mode): self.mode = mode self.trace = config.trace.root.get("assertion") + self.hook = None -def install_importhook(config, mode): - if mode == "rewrite": - try: - import ast # noqa - except ImportError: - mode = "reinterp" - else: - # Both Jython and CPython 2.6.0 have AST bugs that make the - # assertion rewriting hook malfunction. - if (sys.platform.startswith('java') or - sys.version_info[:3] == (2, 6, 0)): - mode = "reinterp" +def install_importhook(config): + """Try to install the rewrite hook, raise SystemError if it fails.""" + # Both Jython and CPython 2.6.0 have AST bugs that make the + # assertion rewriting hook malfunction. + if (sys.platform.startswith('java') or + sys.version_info[:3] == (2, 6, 0)): + raise SystemError('rewrite not supported') - config._assertstate = AssertionState(config, mode) - - _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) - - config._assertstate.hook = hook - config._assertstate.trace("configured with mode set to %r" % (mode,)) + config._assertstate = AssertionState(config, 'rewrite') + config._assertstate.hook = hook = rewrite.AssertionRewritingHook(config) + sys.meta_path.insert(0, hook) + config._assertstate.trace('installed rewrite import hook') def undo(): hook = config._assertstate.hook if hook is not None and hook in sys.meta_path: @@ -169,13 +151,5 @@ def pytest_sessionfinish(session): 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 pytest_assertrepr_compare = util.assertrepr_compare diff --git a/_pytest/assertion/reinterpret.py b/_pytest/assertion/reinterpret.py deleted file mode 100644 index f4262c3ac..000000000 --- a/_pytest/assertion/reinterpret.py +++ /dev/null @@ -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 "". - if source: - self.msg = reinterpret(source, f, should_fail=True) - else: - self.msg = "" - 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, "", 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 - diff --git a/_pytest/config.py b/_pytest/config.py index aad8e0d04..68a3dafbe 100644 --- a/_pytest/config.py +++ b/_pytest/config.py @@ -941,10 +941,12 @@ class Config(object): """ ns, unknown_args = self._parser.parse_known_and_unknown_args(args) mode = ns.assertmode - self._warn_about_missing_assertion(mode) - if mode != 'plain': - hook = _pytest.assertion.install_importhook(self, mode) - if hook: + if mode == 'rewrite': + try: + hook = _pytest.assertion.install_importhook(self) + except SystemError: + mode = 'plain' + else: self.pluginmanager.rewrite_hook = hook for entrypoint in pkg_resources.iter_entry_points('pytest11'): for entry in entrypoint.dist._get_metadata('RECORD'): @@ -957,6 +959,7 @@ class Config(object): elif is_package: package_name = os.path.dirname(fn) hook.mark_rewrite(package_name) + self._warn_about_missing_assertion(mode) def _warn_about_missing_assertion(self, mode): try: @@ -964,15 +967,16 @@ class Config(object): except AssertionError: pass else: - if mode == "rewrite": - specifically = ("assertions not in test modules or plugins" - "will be ignored") + if mode == 'plain': + sys.stderr.write("WARNING: ASSERTIONS ARE NOT EXECUTED" + " and FAILING TESTS WILL PASS. Are you" + " using python -O?") else: - specifically = "failing tests may report as passing" - sys.stderr.write("WARNING: " + specifically + - " because assert statements are not executed " - "by the underlying Python interpreter " - "(are you using python -O?)\n") + sys.stderr.write("WARNING: assertions not in test modules or" + " plugins will be ignored" + " because assert statements are not executed " + "by the underlying Python interpreter " + "(are you using python -O?)\n") def _preparse(self, args, addopts=True): self._initini(args) diff --git a/testing/code/test_code.py b/testing/code/test_code.py index 0db4ad2ab..6f1d9d3cc 100644 --- a/testing/code/test_code.py +++ b/testing/code/test_code.py @@ -66,24 +66,6 @@ def test_code_from_func(): 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(): value = py.builtin._totext('\xc4\x85\xc4\x87\n', 'utf-8').encode('utf8') def f(): diff --git a/testing/code/test_excinfo.py b/testing/code/test_excinfo.py index 2ccdd7028..64d1ff89e 100644 --- a/testing/code/test_excinfo.py +++ b/testing/code/test_excinfo.py @@ -274,18 +274,6 @@ class TestTraceback_f_g_h: assert entry.lineno == co.firstlineno + 2 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(): excinfo = pytest.raises(ValueError, h) assert excinfo.exconly().startswith('ValueError') @@ -431,7 +419,7 @@ class TestFormattedExcinfo: assert lines == [ ' def f():', '> assert 0', - 'E assert 0' + 'E AssertionError' ] @@ -770,23 +758,6 @@ raise ValueError() assert reprtb.extraline == "!!! Recursion detected (same locals & position)" 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): mod = importasmod(""" def f(x): @@ -935,21 +906,6 @@ raise ValueError() repr.toterminal(tw) 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): mod = importasmod(""" def f(): @@ -1079,4 +1035,4 @@ def test_cwd_deleted(testdir): """) result = testdir.runpytest() result.stdout.fnmatch_lines(['* 1 failed in *']) - assert 'INTERNALERROR' not in result.stdout.str() + result.stderr.str() \ No newline at end of file + assert 'INTERNALERROR' not in result.stdout.str() + result.stderr.str() diff --git a/testing/test_assertinterpret.py b/testing/test_assertinterpret.py deleted file mode 100644 index 67a352ce7..000000000 --- a/testing/test_assertinterpret.py +++ /dev/null @@ -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 '' - -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 "" - def myany(x): - return True - try: - assert not(myany(A() < 0)) - except AssertionError: - e = exvalue() - s = str(e) - assert " < 0" in s diff --git a/testing/test_assertion.py b/testing/test_assertion.py index 215d3e419..63d6ac98e 100644 --- a/testing/test_assertion.py +++ b/testing/test_assertion.py @@ -3,10 +3,8 @@ import sys import textwrap import _pytest.assertion as plugin -import _pytest._code import py import pytest -from _pytest.assertion import reinterpret from _pytest.assertion import util PY3 = sys.version_info >= (3, 0) @@ -23,14 +21,10 @@ def mock_config(): return Config() -def interpret(expr): - return reinterpret.reinterpret(expr, _pytest._code.Frame(sys._getframe(1))) - - class TestImportHookInstallation: @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): """Test that conftest files are using assertion rewrite on import. (#1619) @@ -57,13 +51,11 @@ class TestImportHookInstallation: expected = 'E AssertionError' elif mode == 'rewrite': expected = '*assert 10 == 30*' - elif mode == 'reinterp': - expected = '*AssertionError:*was re-run*' else: assert 0 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): contents = { 'conftest.py': """ @@ -88,13 +80,11 @@ class TestImportHookInstallation: expected = 'E AssertionError' elif mode == 'rewrite': expected = '*assert 10 == 30*' - elif mode == 'reinterp': - expected = '*AssertionError:*was re-run*' else: assert 0 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): # Make sure the hook is installed early enough so that plugins # installed via setuptools are re-written. @@ -161,8 +151,6 @@ class TestImportHookInstallation: expected = 'E AssertionError' elif mode == 'rewrite': expected = '*assert 10 == 30*' - elif mode == 'reinterp': - expected = '*AssertionError:*was re-run*' else: assert 0 result.stdout.fnmatch_lines([expected]) @@ -206,7 +194,7 @@ class TestImportHookInstallation: result.stdout.fnmatch_lines(['>*assert a == b*', 'E*assert 2 == 3*', '>*assert l.pop() == 3*', - 'E*AssertionError*re-run*']) + 'E*AssertionError']) class TestBinReprIntegration: @@ -663,14 +651,6 @@ def test_assertion_options(testdir): result = testdir.runpytest_subprocess("--assert=plain") 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): testdir.makepyfile(""" def test_hello():