Skip to content

Commit

Permalink
Clean up getfuncargnames() and fix tests to be compatible
Browse files Browse the repository at this point in the history
  • Loading branch information
ceridwen committed Oct 19, 2017
1 parent 6e9effc commit 10b998f
Show file tree
Hide file tree
Showing 3 changed files with 26 additions and 72 deletions.
90 changes: 24 additions & 66 deletions _pytest/compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,87 +87,45 @@ def num_mock_patch_args(function):
return len(patchings)


def getfuncargnames(function, startindex=None, cls=None):
"""
Returns the names of a function's unbound arguments.
def getfuncargnames(function, is_method=False, cls=None):
"""Returns the names of a function's mandatory arguments.
This should return the names of all function arguments that:
* Aren't bound to an instance or type as in instance or class methods.
* Don't have default values.
* Aren't bound with functools.partial.
* Aren't replaced with mocks.
@RonnyPfannschmidt: This function should be refactored when we revisit fixtures. The
fixture mechanism should ask the node for the fixture names, and not try to obtain
directly from the function object well after collection has occurred.
"""
# [p for p in inspect.signature(f).parameters.values() if (p.kind is Parameter.POSITIONAL_OR_KEYWORD or p.kind is Parameter.KEYWORD_ONLY) and p.default is Parameter.empty]
The is_method and cls arguments indicate that the function should
be treated as a bound method even though it's not unless, only in
the case of cls, the function is a static method.
new_args = tuple(
@RonnyPfannschmidt: This function should be refactored when we
revisit fixtures. The fixture mechanism should ask the node for
the fixture names, and not try to obtain directly from the
function object well after collection has occurred.
"""
# The parameters attribute of a Signature object contains an
# ordered mapping of parameter names to Parameter instances. This
# creates a tuple of the names of the parameters that don't have
# defaults.
arg_names = tuple(
p.name for p in signature(function).parameters.values()
if (p.kind is Parameter.POSITIONAL_OR_KEYWORD
or p.kind is Parameter.KEYWORD_ONLY) and
p.default is Parameter.empty)
if (startindex or
# If this function should be treated as a bound method even though
# it's passed as an unbound method or function, remove the first
# parameter name.
if (is_method or
(cls and not isinstance(cls.__dict__.get(function.__name__, None),
staticmethod))):
new_args = new_args[1:]
arg_names = arg_names[1:]
# Remove any names that will be replaced with mocks.
if hasattr(function, "__wrapped__"):
new_args = new_args[num_mock_patch_args(function):]

# # DIVIDER
# if startindex is None and cls is not None:
# is_staticmethod = isinstance(cls.__dict__.get(function.__name__, None), staticmethod)
# startindex = 0 if is_staticmethod else 1
# # print('STARTINDEX STATICMETHOD', startindex)
# # XXX merge with main.py's varnames
# # assert not isclass(function)
# realfunction = function
# while hasattr(realfunction, "__wrapped__"):
# realfunction = realfunction.__wrapped__
# if startindex is None:
# startindex = inspect.ismethod(function) and 1 or 0
# # print('STARTINDEX METHOD', startindex)
# if realfunction != function:
# startindex += num_mock_patch_args(function)
# function = realfunction
# # print('STARTINDEX MOCK', startindex)
# if isinstance(function, functools.partial):
# argnames = inspect.getargs(_pytest._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(_pytest._code.getrawcode(function))[0]

# # FINAL SECTION
# defaults = getattr(function, 'func_defaults',
# getattr(function, '__defaults__', None)) or ()
# numdefaults = len(defaults)
# if numdefaults:
# # return tuple(argnames[startindex:-numdefaults])
# args = tuple(argnames[startindex:-numdefaults])
# # new_args = tuple(new_argnames)[new_startindex:-numdefaults]
# # return tuple(argnames[startindex:])
# else:
# args = tuple(argnames[startindex:])
# # new_args = tuple(new_argnames)[new_startindex:]

# if args != new_args:
# print('FUNCTION', function)
# print('ISMETHOD', inspect.ismethod(function))
# if cls is not None:
# print('IS_STATICMETHOD', isinstance(cls.__dict__.get(function.__name__, None), staticmethod))
# print(repr(signature(function)))
# print(argnames)
# # print(new_argnames)
# print(args)
# print(cls)
# print(new_args)
# assert args == new_args
return new_args
arg_names = arg_names[num_mock_patch_args(function):]
return arg_names


if _PY3:
Expand Down
3 changes: 1 addition & 2 deletions _pytest/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -728,8 +728,7 @@ def __init__(self, fixturemanager, baseid, argname, func, scope, params,
where=baseid
)
self.params = params
startindex = unittest and 1 or None
self.argnames = getfuncargnames(func, startindex=startindex)
self.argnames = getfuncargnames(func, is_method=unittest)
self.unittest = unittest
self.ids = ids
self._finalizer = []
Expand Down
5 changes: 1 addition & 4 deletions testing/python/fixture.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,6 @@ def static(arg1, arg2):
pass

assert fixtures.getfuncargnames(A().f) == ('arg1',)
if sys.version_info < (3, 0):
assert fixtures.getfuncargnames(A.f) == ('arg1',)

assert fixtures.getfuncargnames(A.static, cls=A) == ('arg1', 'arg2')


Expand Down Expand Up @@ -2826,7 +2823,7 @@ def test_show_fixtures_indented_in_class(self, testdir):
import pytest
class TestClass:
@pytest.fixture
def fixture1():
def fixture1(self):
"""line1
line2
indented line
Expand Down

0 comments on commit 10b998f

Please sign in to comment.