parent
330de0a93d
commit
dcdc823dd2
|
@ -18,10 +18,19 @@ callable = py.builtin.callable
|
||||||
# used to work around a python2 exception info leak
|
# used to work around a python2 exception info leak
|
||||||
exc_clear = getattr(sys, 'exc_clear', lambda: None)
|
exc_clear = getattr(sys, 'exc_clear', lambda: None)
|
||||||
|
|
||||||
def getfslineno(obj):
|
def get_real_func(obj):
|
||||||
# xxx let decorators etc specify a sane ordering
|
"""gets the real function object of the (possibly) wrapped object by
|
||||||
|
functools.wraps or functools.partial.
|
||||||
|
"""
|
||||||
while hasattr(obj, "__wrapped__"):
|
while hasattr(obj, "__wrapped__"):
|
||||||
obj = obj.__wrapped__
|
obj = obj.__wrapped__
|
||||||
|
if isinstance(obj, py.std.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'):
|
if hasattr(obj, 'place_as'):
|
||||||
obj = obj.place_as
|
obj = obj.place_as
|
||||||
fslineno = py.code.getfslineno(obj)
|
fslineno = py.code.getfslineno(obj)
|
||||||
|
@ -594,7 +603,10 @@ class FunctionMixin(PyobjMixin):
|
||||||
|
|
||||||
def _prunetraceback(self, excinfo):
|
def _prunetraceback(self, excinfo):
|
||||||
if hasattr(self, '_obj') and not self.config.option.fulltrace:
|
if hasattr(self, '_obj') and not self.config.option.fulltrace:
|
||||||
code = py.code.Code(self.obj)
|
if isinstance(self.obj, py.std.functools.partial):
|
||||||
|
code = py.code.Code(self.obj.func)
|
||||||
|
else:
|
||||||
|
code = py.code.Code(self.obj)
|
||||||
path, firstlineno = code.path, code.firstlineno
|
path, firstlineno = code.path, code.firstlineno
|
||||||
traceback = excinfo.traceback
|
traceback = excinfo.traceback
|
||||||
ntraceback = traceback.cut(path=path, firstlineno=firstlineno)
|
ntraceback = traceback.cut(path=path, firstlineno=firstlineno)
|
||||||
|
@ -1537,7 +1549,7 @@ class FixtureLookupError(LookupError):
|
||||||
for function in stack:
|
for function in stack:
|
||||||
fspath, lineno = getfslineno(function)
|
fspath, lineno = getfslineno(function)
|
||||||
try:
|
try:
|
||||||
lines, _ = inspect.getsourcelines(function)
|
lines, _ = inspect.getsourcelines(get_real_func(function))
|
||||||
except IOError:
|
except IOError:
|
||||||
error_msg = "file %s, line %s: source code not available"
|
error_msg = "file %s, line %s: source code not available"
|
||||||
addline(error_msg % (fspath, lineno+1))
|
addline(error_msg % (fspath, lineno+1))
|
||||||
|
@ -1937,7 +1949,15 @@ def getfuncargnames(function, startindex=None):
|
||||||
if realfunction != function:
|
if realfunction != function:
|
||||||
startindex += num_mock_patch_args(function)
|
startindex += num_mock_patch_args(function)
|
||||||
function = realfunction
|
function = realfunction
|
||||||
argnames = inspect.getargs(py.code.getrawcode(function))[0]
|
if isinstance(function, py.std.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',
|
defaults = getattr(function, 'func_defaults',
|
||||||
getattr(function, '__defaults__', None)) or ()
|
getattr(function, '__defaults__', None)) or ()
|
||||||
numdefaults = len(defaults)
|
numdefaults = len(defaults)
|
||||||
|
|
|
@ -851,3 +851,47 @@ def test_unorderable_types(testdir):
|
||||||
result = testdir.runpytest()
|
result = testdir.runpytest()
|
||||||
assert "TypeError" not in result.stdout.str()
|
assert "TypeError" not in result.stdout.str()
|
||||||
assert result.ret == 0
|
assert result.ret == 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_collect_functools_partial(testdir):
|
||||||
|
"""
|
||||||
|
Test that collection of functools.partial object works, and arguments
|
||||||
|
to the wrapped functions are dealt correctly (see #811).
|
||||||
|
"""
|
||||||
|
testdir.makepyfile("""
|
||||||
|
import functools
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def fix1():
|
||||||
|
return 'fix1'
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def fix2():
|
||||||
|
return 'fix2'
|
||||||
|
|
||||||
|
def check1(i, fix1):
|
||||||
|
assert i == 2
|
||||||
|
assert fix1 == 'fix1'
|
||||||
|
|
||||||
|
def check2(fix1, i):
|
||||||
|
assert i == 2
|
||||||
|
assert fix1 == 'fix1'
|
||||||
|
|
||||||
|
def check3(fix1, i, fix2):
|
||||||
|
assert i == 2
|
||||||
|
assert fix1 == 'fix1'
|
||||||
|
assert fix2 == 'fix2'
|
||||||
|
|
||||||
|
test_ok_1 = functools.partial(check1, i=2)
|
||||||
|
test_ok_2 = functools.partial(check1, i=2, fix1='fix1')
|
||||||
|
test_ok_3 = functools.partial(check1, 2)
|
||||||
|
test_ok_4 = functools.partial(check2, i=2)
|
||||||
|
test_ok_5 = functools.partial(check3, i=2)
|
||||||
|
test_ok_6 = functools.partial(check3, i=2, fix1='fix1')
|
||||||
|
|
||||||
|
test_fail_1 = functools.partial(check2, 2)
|
||||||
|
test_fail_2 = functools.partial(check3, 2)
|
||||||
|
""")
|
||||||
|
result = testdir.inline_run()
|
||||||
|
result.assertoutcome(passed=6, failed=2)
|
||||||
|
|
Loading…
Reference in New Issue