Merge branch 'pytest-2.7'

This commit is contained in:
Anatoly Bubenkov
2015-07-19 15:25:04 +02:00
16 changed files with 289 additions and 65 deletions

View File

@@ -1,4 +1,5 @@
""" generic mechanism for marking and selecting python functions. """
import inspect
import py
@@ -253,15 +254,17 @@ class MarkDecorator:
otherwise add *args/**kwargs in-place to mark information. """
if args and not kwargs:
func = args[0]
if len(args) == 1 and (istestfunc(func) or
hasattr(func, '__bases__')):
if hasattr(func, '__bases__'):
is_class = inspect.isclass(func)
if len(args) == 1 and (istestfunc(func) or is_class):
if is_class:
if hasattr(func, 'pytestmark'):
l = func.pytestmark
if not isinstance(l, list):
func.pytestmark = [l, self]
else:
l.append(self)
mark_list = func.pytestmark
if not isinstance(mark_list, list):
mark_list = [mark_list]
# always work on a copy to avoid updating pytestmark
# from a superclass by accident
mark_list = mark_list + [self]
func.pytestmark = mark_list
else:
func.pytestmark = [self]
else:

View File

@@ -1,5 +1,6 @@
""" Python test discovery, setup and run of test functions. """
import fnmatch
import functools
import py
import inspect
import sys
@@ -22,15 +23,23 @@ callable = py.builtin.callable
# used to work around a python2 exception info leak
exc_clear = getattr(sys, 'exc_clear', lambda: None)
def filter_traceback(entry):
return entry.path != cutdir1 and not entry.path.relto(cutdir2)
def getfslineno(obj):
# xxx let decorators etc specify a sane ordering
def get_real_func(obj):
"""gets the real function object of the (possibly) wrapped object by
functools.wraps or functools.partial.
"""
while hasattr(obj, "__wrapped__"):
obj = obj.__wrapped__
if isinstance(obj, functools.partial):
obj = obj.func
return obj
def getfslineno(obj):
# xxx let decorators etc specify a sane ordering
obj = get_real_func(obj)
if hasattr(obj, 'place_as'):
obj = obj.place_as
fslineno = py.code.getfslineno(obj)
@@ -606,7 +615,7 @@ class FunctionMixin(PyobjMixin):
def _prunetraceback(self, excinfo):
if hasattr(self, '_obj') and not self.config.option.fulltrace:
code = py.code.Code(self.obj)
code = py.code.Code(get_real_func(self.obj))
path, firstlineno = code.path, code.firstlineno
traceback = excinfo.traceback
ntraceback = traceback.cut(path=path, firstlineno=firstlineno)
@@ -976,21 +985,13 @@ def showfixtures(config):
def _showfixtures_main(config, session):
session.perform_collect()
curdir = py.path.local()
if session.items:
nodeid = session.items[0].nodeid
else:
part = session._initialparts[0]
nodeid = "::".join(map(str, [curdir.bestrelpath(part[0])] + part[1:]))
nodeid.replace(session.fspath.sep, "/")
tw = py.io.TerminalWriter()
verbose = config.getvalue("verbose")
fm = session._fixturemanager
available = []
for argname in fm._arg2fixturedefs:
fixturedefs = fm.getfixturedefs(argname, nodeid)
for argname, fixturedefs in fm._arg2fixturedefs.items():
assert fixturedefs is not None
if not fixturedefs:
continue
@@ -1582,7 +1583,7 @@ class FixtureLookupError(LookupError):
for function in stack:
fspath, lineno = getfslineno(function)
try:
lines, _ = inspect.getsourcelines(function)
lines, _ = inspect.getsourcelines(get_real_func(function))
except IOError:
error_msg = "file %s, line %s: source code not available"
addline(error_msg % (fspath, lineno+1))
@@ -1970,7 +1971,15 @@ def getfuncargnames(function, startindex=None):
if realfunction != function:
startindex += num_mock_patch_args(function)
function = realfunction
argnames = inspect.getargs(py.code.getrawcode(function))[0]
if isinstance(function, functools.partial):
argnames = inspect.getargs(py.code.getrawcode(function.func))[0]
partial = function
argnames = argnames[len(partial.args):]
if partial.keywords:
for kw in partial.keywords:
argnames.remove(kw)
else:
argnames = inspect.getargs(py.code.getrawcode(function))[0]
defaults = getattr(function, 'func_defaults',
getattr(function, '__defaults__', None)) or ()
numdefaults = len(defaults)

View File

@@ -45,8 +45,8 @@ def deprecated_call(func, *args, **kwargs):
try:
ret = func(*args, **kwargs)
finally:
warnings.warn_explicit = warn_explicit
warnings.warn = warn
warnings.warn_explicit = oldwarn_explicit
warnings.warn = oldwarn
if not l:
__tracebackhide__ = True
raise AssertionError("%r did not produce DeprecationWarning" %(func,))

View File

@@ -98,24 +98,36 @@ class MarkEvaluator:
return d
def _istrue(self):
if hasattr(self, 'result'):
return self.result
if self.holder:
d = self._getglobals()
if self.holder.args:
self.result = False
for expr in self.holder.args:
self.expr = expr
if isinstance(expr, py.builtin._basestring):
result = cached_eval(self.item.config, expr, d)
else:
if self.get("reason") is None:
# XXX better be checked at collection time
pytest.fail("you need to specify reason=STRING "
"when using booleans as conditions.")
result = bool(expr)
if result:
self.result = True
# "holder" might be a MarkInfo or a MarkDecorator; only
# MarkInfo keeps track of all parameters it received in an
# _arglist attribute
if hasattr(self.holder, '_arglist'):
arglist = self.holder._arglist
else:
arglist = [(self.holder.args, self.holder.kwargs)]
for args, kwargs in arglist:
for expr in args:
self.expr = expr
break
if isinstance(expr, py.builtin._basestring):
result = cached_eval(self.item.config, expr, d)
else:
if "reason" not in kwargs:
# XXX better be checked at collection time
msg = "you need to specify reason=STRING " \
"when using booleans as conditions."
pytest.fail(msg)
result = bool(expr)
if result:
self.result = True
self.reason = kwargs.get('reason', None)
self.expr = expr
return self.result
else:
self.result = True
return getattr(self, 'result', False)
@@ -124,7 +136,7 @@ class MarkEvaluator:
return self.holder.kwargs.get(attr, default)
def getexplanation(self):
expl = self.get('reason', None)
expl = getattr(self, 'reason', None) or self.get('reason', None)
if not expl:
if not hasattr(self, 'expr'):
return ""

View File

@@ -52,7 +52,14 @@ class TempdirFactory:
basetemp.remove()
basetemp.mkdir()
else:
basetemp = py.path.local.make_numbered_dir(prefix='pytest-')
# use a sub-directory in the temproot to speed-up
# make_numbered_dir() call
import getpass
temproot = py.path.local.get_temproot()
rootdir = temproot.join('pytest-%s' % getpass.getuser())
rootdir.ensure(dir=1)
basetemp = py.path.local.make_numbered_dir(prefix='pytest-',
rootdir=rootdir)
self._basetemp = t = basetemp.realpath()
self.trace("new basetemp", t)
return t