Merge pull request #859 from pytest-dev/py35fix-port

Py35fix port
This commit is contained in:
Bruno Oliveira 2015-07-18 13:53:11 -03:00
commit 30ff723d57
6 changed files with 103 additions and 24 deletions

View File

@ -1,5 +1,7 @@
sudo: false sudo: false
language: python language: python
python:
- 'nightly'
# command to install dependencies # command to install dependencies
install: "pip install -U tox" install: "pip install -U tox"
# # command to run tests # # command to run tests
@ -8,18 +10,18 @@ env:
- TESTENV=flakes - TESTENV=flakes
- TESTENV=py26 - TESTENV=py26
- TESTENV=py27 - TESTENV=py27
- TESTENV=py33
- TESTENV=py34 - TESTENV=py34
- TESTENV=py35
- TESTENV=pypy - TESTENV=pypy
- TESTENV=py27-pexpect - TESTENV=py27-pexpect
- TESTENV=py33-pexpect - TESTENV=py34-pexpect
- TESTENV=py27-nobyte - TESTENV=py27-nobyte
- TESTENV=py33
- TESTENV=py27-xdist - TESTENV=py27-xdist
- TESTENV=py33-xdist - TESTENV=py34-xdist
- TESTENV=py27
- TESTENV=py27-trial - TESTENV=py27-trial
- TESTENV=py33 - TESTENV=py33
- TESTENV=py33-trial - TESTENV=py34-trial
# inprocess tests by default were introduced in 2.8 only; # inprocess tests by default were introduced in 2.8 only;
# this TESTENV should be enabled when merged back to master # this TESTENV should be enabled when merged back to master
#- TESTENV=py27-subprocess #- TESTENV=py27-subprocess

View File

@ -1,6 +1,10 @@
2.7.3 (compared to 2.7.2) 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 - fix issue842: applying markers in classes no longer propagate this markers
to superclasses which also have markers. to superclasses which also have markers.
Thanks xmo-odoo for the report and Bruno Oliveira for the PR. Thanks xmo-odoo for the report and Bruno Oliveira for the PR.

View File

@ -33,6 +33,12 @@ else:
def _is_ast_stmt(node): def _is_ast_stmt(node):
return isinstance(node, ast.stmt) 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): class Failure(Exception):
"""Error found while interpreting AST.""" """Error found while interpreting AST."""
@ -232,24 +238,38 @@ class DebugInterpreter(ast.NodeVisitor):
arguments = [] arguments = []
for arg in call.args: for arg in call.args:
arg_explanation, arg_result = self.visit(arg) arg_explanation, arg_result = self.visit(arg)
arg_name = "__exprinfo_%s" % (len(ns),) if isinstance(arg, _Starred):
ns[arg_name] = arg_result arg_name = "__exprinfo_star"
arguments.append(arg_name) ns[arg_name] = arg_result
arg_explanations.append(arg_explanation) 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: for keyword in call.keywords:
arg_explanation, arg_result = self.visit(keyword.value) arg_explanation, arg_result = self.visit(keyword.value)
arg_name = "__exprinfo_%s" % (len(ns),) 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 ns[arg_name] = arg_result
keyword_source = "%s=%%s" % (keyword.arg)
arguments.append(keyword_source % (arg_name,)) if getattr(call, 'starargs', None):
arg_explanations.append(keyword_source % (arg_explanation,))
if call.starargs:
arg_explanation, arg_result = self.visit(call.starargs) arg_explanation, arg_result = self.visit(call.starargs)
arg_name = "__exprinfo_star" arg_name = "__exprinfo_star"
ns[arg_name] = arg_result ns[arg_name] = arg_result
arguments.append("*%s" % (arg_name,)) arguments.append("*%s" % (arg_name,))
arg_explanations.append("*%s" % (arg_explanation,)) arg_explanations.append("*%s" % (arg_explanation,))
if call.kwargs:
if getattr(call, 'kwargs', None):
arg_explanation, arg_result = self.visit(call.kwargs) arg_explanation, arg_result = self.visit(call.kwargs)
arg_name = "__exprinfo_kwds" arg_name = "__exprinfo_kwds"
ns[arg_name] = arg_result ns[arg_name] = arg_result

View File

@ -35,6 +35,12 @@ PYC_TAIL = "." + PYTEST_TAG + PYC_EXT
REWRITE_NEWLINES = sys.version_info[:2] != (2, 7) and sys.version_info < (3, 2) REWRITE_NEWLINES = sys.version_info[:2] != (2, 7) and sys.version_info < (3, 2)
ASCII_IS_DEFAULT_ENCODING = sys.version_info[0] < 3 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): class AssertionRewritingHook(object):
"""PEP302 Import hook which rewrites asserts.""" """PEP302 Import hook which rewrites asserts."""
@ -587,7 +593,7 @@ class AssertionRewriter(ast.NodeVisitor):
"""Call a helper in this module.""" """Call a helper in this module."""
py_name = ast.Name("@pytest_ar", ast.Load()) py_name = ast.Name("@pytest_ar", ast.Load())
attr = ast.Attribute(py_name, "_" + name, 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): def builtin(self, name):
"""Return the builtin called *name*.""" """Return the builtin called *name*."""
@ -677,7 +683,7 @@ class AssertionRewriter(ast.NodeVisitor):
msg = self.pop_format_context(template) msg = self.pop_format_context(template)
fmt = self.helper("format_explanation", msg) fmt = self.helper("format_explanation", msg)
err_name = ast.Name("AssertionError", ast.Load()) 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: if sys.version_info[0] >= 3:
raise_ = ast.Raise(exc, None) raise_ = ast.Raise(exc, None)
else: else:
@ -697,7 +703,7 @@ class AssertionRewriter(ast.NodeVisitor):
def visit_Name(self, name): def visit_Name(self, name):
# Display the repr of the name if it's a local variable or # Display the repr of the name if it's a local variable or
# _should_repr_global_name() thinks it's acceptable. # _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]) inlocs = ast.Compare(ast.Str(name.id), [ast.In()], [locs])
dorepr = self.helper("should_repr_global_name", name) dorepr = self.helper("should_repr_global_name", name)
test = ast.BoolOp(ast.Or(), [inlocs, dorepr]) test = ast.BoolOp(ast.Or(), [inlocs, dorepr])
@ -724,7 +730,7 @@ class AssertionRewriter(ast.NodeVisitor):
res, expl = self.visit(v) res, expl = self.visit(v)
body.append(ast.Assign([ast.Name(res_var, ast.Store())], res)) body.append(ast.Assign([ast.Name(res_var, ast.Store())], res))
expl_format = self.pop_format_context(ast.Str(expl)) 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)) self.on_failure.append(ast.Expr(call))
if i < levels: if i < levels:
cond = res cond = res
@ -753,7 +759,42 @@ class AssertionRewriter(ast.NodeVisitor):
res = self.assign(ast.BinOp(left_expr, binop.op, right_expr)) res = self.assign(ast.BinOp(left_expr, binop.op, right_expr))
return res, explanation 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:
res, expl = self.visit(arg)
arg_expls.append(expl)
new_args.append(res)
for keyword in call.keywords:
res, expl = self.visit(keyword.value)
new_kwargs.append(ast.keyword(keyword.arg, res))
if keyword.arg:
arg_expls.append(keyword.arg + "=" + expl)
else: ## **args have `arg` keywords with an .arg of None
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_Starred(self, starred):
# From Python 3.5, a Starred node can appear in a function call
res, expl = self.visit(starred.value)
return starred, '*' + expl
def visit_Call_legacy(self, call):
"""
visit `ast.Call nodes on 3.4 and below`
"""
new_func, func_expl = self.visit(call.func) new_func, func_expl = self.visit(call.func)
arg_expls = [] arg_expls = []
new_args = [] new_args = []
@ -781,6 +822,15 @@ class AssertionRewriter(ast.NodeVisitor):
outer_expl = "%s\n{%s = %s\n}" % (res_expl, res_expl, expl) outer_expl = "%s\n{%s = %s\n}" % (res_expl, res_expl, expl)
return res, outer_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): def visit_Attribute(self, attr):
if not isinstance(attr.ctx, ast.Load): if not isinstance(attr.ctx, ast.Load):
return self.generic_visit(attr) return self.generic_visit(attr)

View File

@ -502,10 +502,12 @@ class Item(Node):
class NoMatch(Exception): class NoMatch(Exception):
""" raised if matching cannot locate a matching names. """ """ raised if matching cannot locate a matching names. """
class Interrupted(KeyboardInterrupt):
""" signals an interrupted test run. """
__module__ = 'builtins' # for py3
class Session(FSCollector): class Session(FSCollector):
class Interrupted(KeyboardInterrupt): Interrupted = Interrupted
""" signals an interrupted test run. """
__module__ = 'builtins' # for py3
def __init__(self, config): def __init__(self, config):
FSCollector.__init__(self, config.rootdir, parent=None, FSCollector.__init__(self, config.rootdir, parent=None,

View File

@ -2,7 +2,7 @@
minversion=2.0 minversion=2.0
distshare={homedir}/.tox/distshare distshare={homedir}/.tox/distshare
envlist= envlist=
flakes,py26,py27,py33,py34,pypy, flakes,py26,py27,py33,py34,py35,pypy,
{py27,py34}-{pexpect,xdist,trial}, {py27,py34}-{pexpect,xdist,trial},
py27-nobyte,doctesting,py27-cxfreeze py27-nobyte,doctesting,py27-cxfreeze
@ -23,6 +23,7 @@ deps=
commands= py.test --genscript=pytest1 commands= py.test --genscript=pytest1
[testenv:flakes] [testenv:flakes]
basepython = python2.7
deps = pytest-flakes>=0.2 deps = pytest-flakes>=0.2
commands = py.test --flakes -m flakes _pytest testing commands = py.test --flakes -m flakes _pytest testing