From af893aab263627ce98ae842851853bc77a85debb Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 16 Dec 2015 15:55:05 -0200 Subject: [PATCH 1/4] Remove code related to support python <= 2.5 Fix #1226 --- _pytest/_argcomplete.py | 3 --- testing/acceptance_test.py | 3 --- testing/test_argcomplete.py | 4 ---- testing/test_assertinterpret.py | 19 ------------------- testing/test_assertion.py | 9 +-------- testing/test_cache.py | 3 --- testing/test_mark.py | 2 -- testing/test_nose.py | 1 - testing/test_parseopt.py | 6 ------ 9 files changed, 1 insertion(+), 49 deletions(-) diff --git a/_pytest/_argcomplete.py b/_pytest/_argcomplete.py index 4f4eaf925..955855a96 100644 --- a/_pytest/_argcomplete.py +++ b/_pytest/_argcomplete.py @@ -88,9 +88,6 @@ class FastFilesCompleter: return completion if os.environ.get('_ARGCOMPLETE'): - # argcomplete 0.5.6 is not compatible with python 2.5.6: print/with/format - if sys.version_info[:2] < (2, 6): - sys.exit(1) try: import argcomplete.completers except ImportError: diff --git a/testing/acceptance_test.py b/testing/acceptance_test.py index b9a3fa381..4cd731e72 100644 --- a/testing/acceptance_test.py +++ b/testing/acceptance_test.py @@ -450,19 +450,16 @@ class TestInvocationVariants: "*1 passed*", ]) - @pytest.mark.skipif("sys.version_info < (2,5)") def test_python_minus_m_invocation_ok(self, testdir): p1 = testdir.makepyfile("def test_hello(): pass") res = testdir.run(py.std.sys.executable, "-m", "pytest", str(p1)) assert res.ret == 0 - @pytest.mark.skipif("sys.version_info < (2,5)") def test_python_minus_m_invocation_fail(self, testdir): p1 = testdir.makepyfile("def test_fail(): 0/0") res = testdir.run(py.std.sys.executable, "-m", "pytest", str(p1)) assert res.ret == 1 - @pytest.mark.skipif("sys.version_info < (2,5)") def test_python_pytest_package(self, testdir): p1 = testdir.makepyfile("def test_pass(): pass") res = testdir.run(py.std.sys.executable, "-m", "pytest", str(p1)) diff --git a/testing/test_argcomplete.py b/testing/test_argcomplete.py index 9a218a401..ace7d8ceb 100644 --- a/testing/test_argcomplete.py +++ b/testing/test_argcomplete.py @@ -69,11 +69,8 @@ class FilesCompleter(object): completion += [f + '/' for f in anticomp] return completion -# the following barfs with a syntax error on py2.5 -# @pytest.mark.skipif("sys.version_info < (2,6)") class TestArgComplete: @pytest.mark.skipif("sys.platform in ('win32', 'darwin')") - @pytest.mark.skipif("sys.version_info < (2,6)") def test_compare_with_compgen(self): from _pytest._argcomplete import FastFilesCompleter ffc = FastFilesCompleter() @@ -82,7 +79,6 @@ class TestArgComplete: assert equal_with_bash(x, ffc, fc, out=py.std.sys.stdout) @pytest.mark.skipif("sys.platform in ('win32', 'darwin')") - @pytest.mark.skipif("sys.version_info < (2,6)") def test_remove_dir_prefix(self): """this is not compatible with compgen but it is with bash itself: ls /usr/ diff --git a/testing/test_assertinterpret.py b/testing/test_assertinterpret.py index 209bdfd49..ad9bb5c79 100644 --- a/testing/test_assertinterpret.py +++ b/testing/test_assertinterpret.py @@ -79,7 +79,6 @@ def test_is(): assert s.startswith("assert 1 is 2") -@pytest.mark.skipif("sys.version_info < (2,6)") def test_attrib(): class Foo(object): b = 1 @@ -91,7 +90,6 @@ def test_attrib(): 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 @@ -256,7 +254,6 @@ class TestView: assert codelines == ["4 + 5", "getitem('', 'join')", "setattr('x', 'y', 3)", "12 - 1"] -@pytest.mark.skipif("sys.version_info < (2,6)") def test_assert_customizable_reprcompare(monkeypatch): monkeypatch.setattr(util, '_reprcompare', lambda *args: 'hello') try: @@ -306,7 +303,6 @@ def test_assert_raise_alias(testdir): ]) -@pytest.mark.skipif("sys.version_info < (2,5)") def test_assert_raise_subclass(): class SomeEx(AssertionError): def __init__(self, *args): @@ -334,18 +330,3 @@ def test_assert_raises_in_nonzero_of_object_pytest_issue10(): e = exvalue() s = str(e) assert " < 0" in s - -@pytest.mark.skipif("sys.version_info >= (2,6)") -def test_oldinterpret_importation(): - # we had a cyclic import there - # requires pytest on sys.path - res = py.std.subprocess.call([ - py.std.sys.executable, '-c', str(py.code.Source(""" - try: - from _pytest.assertion.newinterpret import interpret - except ImportError: - from _pytest.assertion.oldinterpret import interpret - """)) - ]) - - assert res == 0 diff --git a/testing/test_assertion.py b/testing/test_assertion.py index 914feddf7..cf715470a 100644 --- a/testing/test_assertion.py +++ b/testing/test_assertion.py @@ -7,7 +7,6 @@ import _pytest.assertion as plugin from _pytest.assertion import reinterpret from _pytest.assertion import util -needsnewassert = pytest.mark.skipif("sys.version_info < (2,6)") PY3 = sys.version_info >= (3, 0) @@ -26,7 +25,6 @@ def interpret(expr): return reinterpret.reinterpret(expr, py.code.Frame(sys._getframe(1))) class TestBinReprIntegration: - pytestmark = needsnewassert def test_pytest_assertrepr_compare_called(self, testdir): testdir.makeconftest(""" @@ -361,7 +359,6 @@ def test_python25_compile_issue257(testdir): *1 failed* """) -@needsnewassert def test_rewritten(testdir): testdir.makepyfile(""" def test_rewritten(): @@ -374,7 +371,6 @@ def test_reprcompare_notin(mock_config): mock_config, 'not in', 'foo', 'aaafoobbb')[1:] assert detail == ["'foo' is contained here:", ' aaafoobbb', '? +++'] -@needsnewassert def test_pytest_assertrepr_compare_integration(testdir): testdir.makepyfile(""" def test_hello(): @@ -391,7 +387,6 @@ def test_pytest_assertrepr_compare_integration(testdir): "*E*50*", ]) -@needsnewassert def test_sequence_comparison_uses_repr(testdir): testdir.makepyfile(""" def test_hello(): @@ -410,7 +405,6 @@ def test_sequence_comparison_uses_repr(testdir): ]) -@pytest.mark.xfail("sys.version_info < (2,6)") def test_assert_compare_truncate_longmessage(testdir): testdir.makepyfile(r""" def test_long(): @@ -438,7 +432,6 @@ def test_assert_compare_truncate_longmessage(testdir): ]) -@needsnewassert def test_assertrepr_loaded_per_dir(testdir): testdir.makepyfile(test_base=['def test_base(): assert 1 == 2']) a = testdir.mkdir('a') @@ -547,7 +540,7 @@ def test_traceback_failure(testdir): "*test_traceback_failure.py:4: AssertionError" ]) -@pytest.mark.skipif("sys.version_info < (2,5) or '__pypy__' in sys.builtin_module_names or sys.platform.startswith('java')" ) +@pytest.mark.skipif("'__pypy__' in sys.builtin_module_names or sys.platform.startswith('java')" ) def test_warn_missing(testdir): testdir.makepyfile("") result = testdir.run(sys.executable, "-OO", "-m", "pytest", "-h") diff --git a/testing/test_cache.py b/testing/test_cache.py index 20a6cf78a..adac4a1a6 100755 --- a/testing/test_cache.py +++ b/testing/test_cache.py @@ -129,7 +129,6 @@ def test_cache_show(testdir): class TestLastFailed: - @pytest.mark.skipif("sys.version_info < (2,6)") def test_lastfailed_usecase(self, testdir, monkeypatch): monkeypatch.setenv("PYTHONDONTWRITEBYTECODE", 1) p = testdir.makepyfile(""" @@ -197,7 +196,6 @@ class TestLastFailed: "test_a.py*", ]) - @pytest.mark.skipif("sys.version_info < (2,6)") def test_lastfailed_difference_invocations(self, testdir, monkeypatch): monkeypatch.setenv("PYTHONDONTWRITEBYTECODE", 1) testdir.makepyfile(test_a=""" @@ -233,7 +231,6 @@ class TestLastFailed: "*1 failed*1 desel*", ]) - @pytest.mark.skipif("sys.version_info < (2,6)") def test_lastfailed_usecase_splice(self, testdir, monkeypatch): monkeypatch.setenv("PYTHONDONTWRITEBYTECODE", 1) testdir.makepyfile(""" diff --git a/testing/test_mark.py b/testing/test_mark.py index 1aa336183..1795928f0 100644 --- a/testing/test_mark.py +++ b/testing/test_mark.py @@ -316,7 +316,6 @@ class TestFunctional: assert 'hello' in keywords assert 'world' in keywords - @pytest.mark.skipif("sys.version_info < (2,6)") def test_mark_per_class_decorator(self, testdir): item = testdir.getitem(""" import pytest @@ -328,7 +327,6 @@ class TestFunctional: keywords = item.keywords assert 'hello' in keywords - @pytest.mark.skipif("sys.version_info < (2,6)") def test_mark_per_class_decorator_plus_existing_dec(self, testdir): item = testdir.getitem(""" import pytest diff --git a/testing/test_nose.py b/testing/test_nose.py index 6260aae47..a5162381e 100644 --- a/testing/test_nose.py +++ b/testing/test_nose.py @@ -300,7 +300,6 @@ def test_apiwrapper_problem_issue260(testdir): result = testdir.runpytest() result.assert_outcomes(passed=1) -@pytest.mark.skipif("sys.version_info < (2,6)") def test_setup_teardown_linking_issue265(testdir): # we accidentally didnt integrate nose setupstate with normal setupstate # this test ensures that won't happen again diff --git a/testing/test_parseopt.py b/testing/test_parseopt.py index 15b28225c..e45ee2854 100644 --- a/testing/test_parseopt.py +++ b/testing/test_parseopt.py @@ -171,7 +171,6 @@ class TestParser: assert option.this == 42 assert option.no is False - @pytest.mark.skipif("sys.version_info < (2,5)") def test_drop_short_helper(self): parser = py.std.argparse.ArgumentParser(formatter_class=parseopt.DropShorterLongHelpFormatter) parser.add_argument('-t', '--twoword', '--duo', '--two-word', '--two', @@ -213,20 +212,17 @@ class TestParser: assert args.abc_def is False assert args.klm_hij is True - @pytest.mark.skipif("sys.version_info < (2,5)") def test_drop_short_2(self, parser): parser.addoption('--func-arg', '--doit', action='store_true') args = parser.parse(['--doit']) assert args.func_arg is True - @pytest.mark.skipif("sys.version_info < (2,5)") def test_drop_short_3(self, parser): parser.addoption('--func-arg', '--funcarg', '--doit', action='store_true') args = parser.parse(['abcd']) assert args.func_arg is False assert args.file_or_dir == ['abcd'] - @pytest.mark.skipif("sys.version_info < (2,5)") def test_drop_short_help0(self, parser, capsys): parser.addoption('--func-args', '--doit', help = 'foo', action='store_true') @@ -235,7 +231,6 @@ class TestParser: assert '--func-args, --doit foo' in help # testing would be more helpful with all help generated - @pytest.mark.skipif("sys.version_info < (2,5)") def test_drop_short_help1(self, parser, capsys): group = parser.getgroup("general") group.addoption('--doit', '--func-args', action='store_true', help='foo') @@ -246,7 +241,6 @@ class TestParser: assert '-doit, --func-args foo' in help -@pytest.mark.skipif("sys.version_info < (2,6)") def test_argcomplete(testdir, monkeypatch): if not py.path.local.sysfind('bash'): pytest.skip("bash not available") From 81588d7f63aa8350ccb9f1db16b2a0a8da4a9a28 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 16 Dec 2015 16:05:08 -0200 Subject: [PATCH 2/4] Remove obsolete "oldinterpret" module --- _pytest/assertion/newinterpret.py | 1 - _pytest/assertion/oldinterpret.py | 566 ------------------------------ _pytest/assertion/reinterpret.py | 5 +- testing/test_assertinterpret.py | 59 ---- 4 files changed, 1 insertion(+), 630 deletions(-) delete mode 100644 _pytest/assertion/oldinterpret.py diff --git a/_pytest/assertion/newinterpret.py b/_pytest/assertion/newinterpret.py index d8e741162..fbca118d1 100644 --- a/_pytest/assertion/newinterpret.py +++ b/_pytest/assertion/newinterpret.py @@ -1,6 +1,5 @@ """ Find intermediate evalutation results in assert statements through builtin AST. -This should replace oldinterpret.py eventually. """ import sys diff --git a/_pytest/assertion/oldinterpret.py b/_pytest/assertion/oldinterpret.py deleted file mode 100644 index 8ca1d8f5c..000000000 --- a/_pytest/assertion/oldinterpret.py +++ /dev/null @@ -1,566 +0,0 @@ -import traceback -import types -import py -import sys, inspect -from compiler import parse, ast, pycodegen -from _pytest.assertion.util import format_explanation, BuiltinAssertionError - -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(object.__getattribute__(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 = '' - self.__obj__.filename = '' - 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 = '' - 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: - try: - self.result = frame.eval(source, __exprinfo_expr=expr.result) - except AttributeError: - # Maybe the attribute name needs to be mangled? - if (not self.attrname.startswith("__") or - self.attrname.endswith("__")): - raise - source = "getattr(__exprinfo_expr.__class__, '__name__', '')" - class_name = frame.eval(source, __exprinfo_expr=expr.result) - mangled_attr = "_" + class_name + self.attrname - source = "__exprinfo_expr.%s" % (mangled_attr,) - 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) - # print the result as 'assert ' - 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 = '' - 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): - if frame is None: - frame = sys._getframe(1) - frame = py.code.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) - #print "got module", module - if isinstance(frame, types.FrameType): - frame = py.code.Frame(frame) - try: - module.run(frame) - except Failure: - e = sys.exc_info()[1] - return getfailure(e) - except passthroughex: - raise - except: - 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 --assert=plain)") - else: - return None - -def getmsg(excinfo): - if isinstance(excinfo, tuple): - excinfo = py.code.ExceptionInfo(excinfo) - #frame, line = gettbline(tb) - #frame = py.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): - if frame is None: - frame = sys._getframe(1) - frame = py.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())") diff --git a/_pytest/assertion/reinterpret.py b/_pytest/assertion/reinterpret.py index dfb2fec93..30a41497d 100644 --- a/_pytest/assertion/reinterpret.py +++ b/_pytest/assertion/reinterpret.py @@ -46,7 +46,4 @@ class AssertionError(BuiltinAssertionError): if sys.version_info > (3, 0): AssertionError.__module__ = "builtins" -if sys.version_info >= (2, 6) or sys.platform.startswith("java"): - from _pytest.assertion.newinterpret import interpret as reinterpret -else: - from _pytest.assertion.oldinterpret import interpret as reinterpret +from _pytest.assertion.newinterpret import interpret as reinterpret diff --git a/testing/test_assertinterpret.py b/testing/test_assertinterpret.py index ad9bb5c79..44b4f23c4 100644 --- a/testing/test_assertinterpret.py +++ b/testing/test_assertinterpret.py @@ -195,65 +195,6 @@ def test_power(): assert "assert (2 ** 3) == 7" in e.msg -class TestView: - - def setup_class(cls): - cls.View = pytest.importorskip("_pytest.assertion.oldinterpret").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_assert_customizable_reprcompare(monkeypatch): monkeypatch.setattr(util, '_reprcompare', lambda *args: 'hello') try: From ec597e81a4621b381edefbd2ca2f57895b06677a Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 16 Dec 2015 16:14:36 -0200 Subject: [PATCH 3/4] Remove references to python pre-2.6 from docs --- doc/en/assert.rst | 7 +++---- doc/en/example/markers.rst | 4 ++-- doc/en/faq.rst | 5 ++--- doc/en/skipping.rst | 17 +---------------- doc/en/test/plugin/xdist.rst | 4 ++-- doc/en/usage.rst | 3 +-- doc/en/xdist.rst | 10 +++++----- 7 files changed, 16 insertions(+), 34 deletions(-) diff --git a/doc/en/assert.rst b/doc/en/assert.rst index 795f52586..f65225c92 100644 --- a/doc/en/assert.rst +++ b/doc/en/assert.rst @@ -243,10 +243,9 @@ recording the intermediate values. Which technique is used depends on the location of the assert, ``pytest`` configuration, and Python version being used to run ``pytest``. -By default, if the Python version is greater than or equal to 2.6, ``pytest`` -rewrites assert statements in test modules. Rewritten assert statements put -introspection information into the assertion failure message. ``pytest`` only -rewrites test modules directly discovered by its test collection process, so +By default, ``pytest`` rewrites assert statements in test modules. +Rewritten assert statements put introspection information into the assertion failure message. +``pytest`` only rewrites test modules directly discovered by its test collection process, so asserts in supporting modules which are not themselves test modules will not be rewritten. diff --git a/doc/en/example/markers.rst b/doc/en/example/markers.rst index aaca80fb2..cf8cbb00d 100644 --- a/doc/en/example/markers.rst +++ b/doc/en/example/markers.rst @@ -234,8 +234,8 @@ For an example on how to add and work with markers from a plugin, see Marking whole classes or modules ---------------------------------------------------- -If you are programming with Python 2.6 or later you may use ``pytest.mark`` -decorators with classes to apply markers to all of its test methods:: +You may use ``pytest.mark`` decorators with classes to apply markers to all of +its test methods:: # content of test_mark_classlevel.py import pytest diff --git a/doc/en/faq.rst b/doc/en/faq.rst index dbcaf3fd4..3c019b5e1 100644 --- a/doc/en/faq.rst +++ b/doc/en/faq.rst @@ -66,9 +66,8 @@ This completely avoids previous issues of confusing assertion-reporting. It also means, that you can use Python's ``-O`` optimization without losing assertions in test modules. -``pytest`` contains a second, mostly obsolete, assert debugging technique, -invoked via ``--assert=reinterpret``, activated by default on -Python-2.5: When an ``assert`` statement fails, ``pytest`` re-interprets +``pytest`` contains a second, mostly obsolete, assert debugging technique +invoked via ``--assert=reinterpret``: When an ``assert`` statement fails, ``pytest`` re-interprets the expression part to show intermediate values. This technique suffers from a caveat that the rewriting does not: If your expression has side effects (better to avoid them anyway!) the intermediate values may not diff --git a/doc/en/skipping.rst b/doc/en/skipping.rst index 7464758f5..30f059407 100644 --- a/doc/en/skipping.rst +++ b/doc/en/skipping.rst @@ -89,9 +89,7 @@ between test modules so it's no longer advertised as the primary method. Skip all test functions of a class or module --------------------------------------------- -As with all function :ref:`marking ` you can skip test functions at the -`whole class- or module level`_. If your code targets python2.6 or above you -use the skipif decorator (and any other marker) on classes:: +You can use the ``skipif`` decorator (and any other marker) on classes:: @pytest.mark.skipif(sys.platform == 'win32', reason="does not run on windows") @@ -103,19 +101,6 @@ use the skipif decorator (and any other marker) on classes:: If the condition is true, this marker will produce a skip result for each of the test methods. -If your code targets python2.5 where class-decorators are not available, -you can set the ``pytestmark`` attribute of a class:: - - class TestPosixCalls: - pytestmark = pytest.mark.skipif(sys.platform == 'win32', - reason="does not run on windows") - - def test_function(self): - "will not be setup or run under 'win32' platform" - -As with the class-decorator, the ``pytestmark`` special name tells -``pytest`` to apply it to each test function in the class. - If you want to skip all test functions of a module, you must use the ``pytestmark`` name on the global level: diff --git a/doc/en/test/plugin/xdist.rst b/doc/en/test/plugin/xdist.rst index f2c877be7..7ab6cdc8b 100644 --- a/doc/en/test/plugin/xdist.rst +++ b/doc/en/test/plugin/xdist.rst @@ -126,7 +126,7 @@ Specifying test exec environments in a conftest.py Instead of specifying command line options, you can put options values in a ``conftest.py`` file like this:: - option_tx = ['ssh=myhost//python=python2.5', 'popen//python=python2.5'] + option_tx = ['ssh=myhost//python=python2.7', 'popen//python=python2.7'] option_dist = True Any commandline ``--tx`` specifications will add to the list of @@ -163,7 +163,7 @@ command line options (default) no: run tests inprocess, don't distribute. ``--tx=xspec`` - add a test execution environment. some examples: --tx popen//python=python2.5 --tx socket=192.168.1.102:8888 --tx ssh=user@codespeak.net//chdir=testcache + add a test execution environment. some examples: --tx popen//python=python2.7 --tx socket=192.168.1.102:8888 --tx ssh=user@codespeak.net//chdir=testcache ``-d`` load-balance tests. shortcut for '--dist=load' ``--rsyncdir=dir1`` diff --git a/doc/en/usage.rst b/doc/en/usage.rst index 2deaadb5d..b8abb87e9 100644 --- a/doc/en/usage.rst +++ b/doc/en/usage.rst @@ -12,8 +12,7 @@ Calling pytest through ``python -m pytest`` .. versionadded:: 2.0 -If you use Python-2.5 or later you can invoke testing through the -Python interpreter from the command line:: +You can invoke testing through the Python interpreter from the command line:: python -m pytest [...] diff --git a/doc/en/xdist.rst b/doc/en/xdist.rst index 715aa4749..ee1bd6032 100644 --- a/doc/en/xdist.rst +++ b/doc/en/xdist.rst @@ -61,16 +61,16 @@ a lot of I/O this can lead to considerable speed ups. Running tests in a Python subprocess +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -To instantiate a Python-2.4 subprocess and send tests to it, you may type:: +To instantiate a Python-2.7 subprocess and send tests to it, you may type:: - py.test -d --tx popen//python=python2.4 + py.test -d --tx popen//python=python2.7 -This will start a subprocess which is run with the "python2.4" +This will start a subprocess which is run with the "python2.7" Python interpreter, found in your system binary lookup path. If you prefix the --tx option value like this:: - py.test -d --tx 3*popen//python=python2.4 + py.test -d --tx 3*popen//python=python2.7 then three subprocesses would be created and the tests will be distributed to three subprocesses and run simultanously. @@ -170,7 +170,7 @@ For example, you could make running with three subprocesses your default:: You can also add default environments like this:: [pytest] - addopts = --tx ssh=myhost//python=python2.5 --tx ssh=myhost//python=python2.6 + addopts = --tx ssh=myhost//python=python2.7 --tx ssh=myhost//python=python2.6 and then just type:: From 25c392196f6339b2a5c8e88feca4824de7a18950 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 16 Dec 2015 16:48:49 -0200 Subject: [PATCH 4/4] Mention #1226 in the CHANGELOG --- CHANGELOG | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index d719e6a9b..1c9d85158 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -14,6 +14,10 @@ * New `-rp` and `-rP` reporting options give the summary and full output of passing tests, respectively. Thanks to David Vierra for the PR. +* fix #1226: Removed code and documentation for Python 2.5 or lower versions, + including removal of the obsolete ``_pytest.assertion.oldinterpret`` module. + Thanks Bruno Oliveira for the PR. + 2.8.6.dev1 ----------