commit
30ff723d57
12
.travis.yml
12
.travis.yml
|
@ -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
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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,
|
||||||
|
|
3
tox.ini
3
tox.ini
|
@ -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
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue