summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthias Bussonnier <bussonniermatthias@gmail.com>2015-06-23 18:52:05 +0200
committerBruno Oliveira <nicoddemus@gmail.com>2015-07-18 13:15:46 -0300
commitb18e6439bdf60ed0b9d41dbb65c5ae5da2efd01e (patch)
tree659819d5a8441f20e78bdfa502db1e78a9eec50b
parent360c09a1e7e3ab667c3c4f5092e1d1cb534a064d (diff)
downloadpytest-b18e6439bdf60ed0b9d41dbb65c5ae5da2efd01e.tar.gz
Ast Call signature changed on 3.5
fix issue 744 on bitbucket port of merge request 296 https://bitbucket.org/pytest-dev/pytest/pull-request/296/astcall-signature-changed-on-35 https://bitbucket.org/pytest-dev/pytest/issue/744/ Conflicts: CHANGELOG
-rw-r--r--.travis.yml1
-rw-r--r--CHANGELOG4
-rw-r--r--_pytest/assertion/rewrite.py62
3 files changed, 62 insertions, 5 deletions
diff --git a/.travis.yml b/.travis.yml
index a6fc5757d..4ad059331 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -9,6 +9,7 @@ env:
- TESTENV=py26
- TESTENV=py27
- TESTENV=py34
+ - TESTENV=py35
- TESTENV=pypy
- TESTENV=py27-pexpect
- TESTENV=py33-pexpect
diff --git a/CHANGELOG b/CHANGELOG
index 8b6feb3a2..03c804514 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,10 @@
2.7.3 (compared to 2.7.2)
-----------------------------
+- fix issue744: fix for ast.Call changes in Python 3.5+. Thanks
+ Guido van Rossum, Matthias Bussonnier, Stefan Zimmermann and
+ Thomas Kluyver.
+
- fix issue842: applying markers in classes no longer propagate this markers
to superclasses which also have markers.
Thanks xmo-odoo for the report and Bruno Oliveira for the PR.
diff --git a/_pytest/assertion/rewrite.py b/_pytest/assertion/rewrite.py
index 62046d146..29d18d813 100644
--- a/_pytest/assertion/rewrite.py
+++ b/_pytest/assertion/rewrite.py
@@ -35,6 +35,12 @@ PYC_TAIL = "." + PYTEST_TAG + PYC_EXT
REWRITE_NEWLINES = sys.version_info[:2] != (2, 7) and sys.version_info < (3, 2)
ASCII_IS_DEFAULT_ENCODING = sys.version_info[0] < 3
+if sys.version_info >= (3,5):
+ ast_Call = ast.Call
+else :
+ ast_Call = lambda a,b,c : ast.Call(a, b, c, None, None)
+
+
class AssertionRewritingHook(object):
"""PEP302 Import hook which rewrites asserts."""
@@ -587,7 +593,7 @@ class AssertionRewriter(ast.NodeVisitor):
"""Call a helper in this module."""
py_name = ast.Name("@pytest_ar", ast.Load())
attr = ast.Attribute(py_name, "_" + name, ast.Load())
- return ast.Call(attr, list(args), [], None, None)
+ return ast_Call(attr, list(args), [])
def builtin(self, name):
"""Return the builtin called *name*."""
@@ -677,7 +683,7 @@ class AssertionRewriter(ast.NodeVisitor):
msg = self.pop_format_context(template)
fmt = self.helper("format_explanation", msg)
err_name = ast.Name("AssertionError", ast.Load())
- exc = ast.Call(err_name, [fmt], [], None, None)
+ exc = ast_Call(err_name, [fmt], [])
if sys.version_info[0] >= 3:
raise_ = ast.Raise(exc, None)
else:
@@ -697,7 +703,7 @@ class AssertionRewriter(ast.NodeVisitor):
def visit_Name(self, name):
# Display the repr of the name if it's a local variable or
# _should_repr_global_name() thinks it's acceptable.
- locs = ast.Call(self.builtin("locals"), [], [], None, None)
+ locs = ast_Call(self.builtin("locals"), [], [])
inlocs = ast.Compare(ast.Str(name.id), [ast.In()], [locs])
dorepr = self.helper("should_repr_global_name", name)
test = ast.BoolOp(ast.Or(), [inlocs, dorepr])
@@ -724,7 +730,7 @@ class AssertionRewriter(ast.NodeVisitor):
res, expl = self.visit(v)
body.append(ast.Assign([ast.Name(res_var, ast.Store())], res))
expl_format = self.pop_format_context(ast.Str(expl))
- call = ast.Call(app, [expl_format], [], None, None)
+ call = ast_Call(app, [expl_format], [])
self.on_failure.append(ast.Expr(call))
if i < levels:
cond = res
@@ -753,7 +759,44 @@ class AssertionRewriter(ast.NodeVisitor):
res = self.assign(ast.BinOp(left_expr, binop.op, right_expr))
return res, explanation
- def visit_Call(self, call):
+ def visit_Call_35(self, call):
+ """
+ visit `ast.Call` nodes on Python3.5 and after
+ """
+ new_func, func_expl = self.visit(call.func)
+ arg_expls = []
+ new_args = []
+ new_kwargs = []
+ for arg in call.args:
+ if type(arg) is ast.Starred:
+ new_star, expl = self.visit(arg)
+ arg_expls.append("*" + expl)
+ new_args.append(new_star)
+ else:
+ res, expl = self.visit(arg)
+ new_args.append(res)
+ arg_expls.append(expl)
+ for keyword in call.keywords:
+ if keyword.arg:
+ res, expl = self.visit(keyword.value)
+ new_kwargs.append(ast.keyword(keyword.arg, res))
+ arg_expls.append(keyword.arg + "=" + expl)
+ else: ## **args have `arg` keywords with an .arg of None
+ res, expl = self.visit(keyword.value)
+ new_kwargs.append(ast.keyword(keyword.arg, res))
+ arg_expls.append("**" + expl)
+
+ expl = "%s(%s)" % (func_expl, ', '.join(arg_expls))
+ new_call = ast.Call(new_func, new_args, new_kwargs)
+ res = self.assign(new_call)
+ res_expl = self.explanation_param(self.display(res))
+ outer_expl = "%s\n{%s = %s\n}" % (res_expl, res_expl, expl)
+ return res, outer_expl
+
+ def visit_Call_legacy(self, call):
+ """
+ visit `ast.Call nodes on 3.4 and below`
+ """
new_func, func_expl = self.visit(call.func)
arg_expls = []
new_args = []
@@ -781,6 +824,15 @@ class AssertionRewriter(ast.NodeVisitor):
outer_expl = "%s\n{%s = %s\n}" % (res_expl, res_expl, expl)
return res, outer_expl
+ # ast.Call signature changed on 3.5,
+ # conditionally change which methods is named
+ # visit_Call depending on Python version
+ if sys.version_info >= (3, 5):
+ visit_Call = visit_Call_35
+ else:
+ visit_Call = visit_Call_legacy
+
+
def visit_Attribute(self, attr):
if not isinstance(attr.ctx, ast.Load):
return self.generic_visit(attr)