Skip to content

Modernize varnames function #27

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Nov 11, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 27 additions & 27 deletions pluggy.py
Original file line number Diff line number Diff line change
Expand Up @@ -627,50 +627,51 @@ def __repr__(self):
return "<_MultiCall %s, kwargs=%r>" % (status, self.kwargs)


def varnames(func, startindex=None):
""" return argument name tuple for a function, method, class or callable.
def varnames(func):
"""Return argument name tuple for a function, method, class or callable.

In case of a class, its "__init__" method is considered.
For methods the "self" parameter is not included unless you are passing
an unbound method with Python3 (which has no supports for unbound methods)
an unbound method with Python3 (which has no support for unbound methods)
"""
cache = getattr(func, "__dict__", {})
try:
return cache["_varnames"]
except KeyError:
pass

if inspect.isclass(func):
try:
func = func.__init__
except AttributeError:
return ()
startindex = 1
else:
if not inspect.isfunction(func) and not inspect.ismethod(func):
try:
func = getattr(func, '__call__', func)
except Exception:
return ()
if startindex is None:
startindex = int(inspect.ismethod(func))
elif not inspect.isroutine(func): # callable object?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since what version of python does that method exist?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

never mind, i just verified its in 2.6

try:
func = getattr(func, '__call__', func)
except Exception:
return ()

try:
rawcode = func.__code__
except AttributeError:
try: # func MUST be a function or method here or we won't parse any args
spec = inspect.getargspec(func)
except TypeError:
return ()

args, defaults = spec.args, spec.defaults
args = args[:-len(defaults)] if defaults else args

# strip any implicit instance arg
if args:
if inspect.ismethod(func) or (
'.' in getattr(func, '__qualname__', ()) and args[0] == 'self'
):
args = args[1:]

assert "self" not in args # best naming practises check?
try:
x = rawcode.co_varnames[startindex:rawcode.co_argcount]
except AttributeError:
x = ()
else:
defaults = func.__defaults__
if defaults:
x = x[:-len(defaults)]
try:
cache["_varnames"] = x
cache["_varnames"] = args
except TypeError:
pass
return x
return tuple(args)


class _HookRelay:
Expand Down Expand Up @@ -700,8 +701,7 @@ def set_specification(self, specmodule_or_class, spec_opts):
assert not self.has_spec()
self._specmodule_or_class = specmodule_or_class
specfunc = getattr(specmodule_or_class, self.name)
argnames = varnames(specfunc, startindex=inspect.isclass(specmodule_or_class))
assert "self" not in argnames # sanity check
argnames = varnames(specfunc)
self.argnames = ["__multicall__"] + list(argnames)
self.spec_opts = spec_opts
if spec_opts.get("historic"):
Expand Down
24 changes: 21 additions & 3 deletions testing/test_pluggy.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,23 @@ def pm():
return PluginManager("example")


@pytest.fixture
def he_pm(pm):
@pytest.fixture(
params=[
lambda spec: spec,
lambda spec: spec()
],
ids=[
"don't instantiate",
"instatiate"
],
)
def he_pm(request, pm):
class Hooks:
@hookspec
def he_method1(self, arg):
return arg + 1

pm.add_hookspecs(Hooks)
pm.add_hookspecs(request.param(Hooks))
return pm


Expand Down Expand Up @@ -706,8 +715,17 @@ def __init__(self, x):
class D:
pass

class E(object):
def __init__(self, x):
pass

class F(object):
pass

assert varnames(C) == ("x",)
assert varnames(D) == ()
assert varnames(E) == ("x",)
assert varnames(F) == ()


def test_formatdef():
Expand Down