From 43451e5cc767747b64d7b5700800c001b2d1d895 Mon Sep 17 00:00:00 2001 From: Joshua Harlow Date: Wed, 14 Oct 2015 11:39:09 -0700 Subject: [PATCH] Expose function signature fetching function This function is the recommended way to get information about functions going forward, so all the variations of getargspec can just use this instead (including the variations of this used in openstack). This exposes a 'get_signature' function that correctly aliases the exposed function in python3.x and the backwards compat library (funcsigs) that exposes this same information in python2.x Internally in the other reflection functions we now switch away from using getargspec as well, and replace it with using this new signature object/function instead. Depends-On: I1aa0054089ca57fc2d68779f4ee133a9750fec2a Change-Id: I910b353f5db290832ef87bf7c5e5bb2e9788e3ec --- oslo_utils/reflection.py | 45 ++++++++++++++++++++-------------------- requirements.txt | 1 + 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/oslo_utils/reflection.py b/oslo_utils/reflection.py index 34714fc0..5baf4963 100644 --- a/oslo_utils/reflection.py +++ b/oslo_utils/reflection.py @@ -29,6 +29,17 @@ except AttributeError: # others)... _BUILTIN_MODULES = ('builtins', '__builtin__', '__builtins__', 'exceptions') +if six.PY3: + Parameter = inspect.Parameter + Signature = inspect.Signature + get_signature = inspect.signature +else: + # Provide an equivalent but use funcsigs instead... + import funcsigs + Parameter = funcsigs.Parameter + Signature = funcsigs.Signature + get_signature = funcsigs.signature + def get_members(obj, exclude_hidden=True): """Yields the members of an object, filtering by hidden/not hidden.""" @@ -173,19 +184,6 @@ def is_subclass(obj, cls): return inspect.isclass(obj) and issubclass(obj, cls) -def _get_arg_spec(function): - if isinstance(function, _TYPE_TYPE): - bound = True - function = function.__init__ - elif isinstance(function, (types.FunctionType, types.MethodType)): - bound = is_bound_method(function) - function = getattr(function, '__wrapped__', function) - else: - function = function.__call__ - bound = is_bound_method(function) - return inspect.getargspec(function), bound - - def get_callable_args(function, required_only=False): """Get names of callable arguments. @@ -195,16 +193,17 @@ def get_callable_args(function, required_only=False): If required_only is True, optional arguments (with default values) are not included into output. """ - argspec, bound = _get_arg_spec(function) - f_args = argspec.args - if required_only and argspec.defaults: - f_args = f_args[:-len(argspec.defaults)] - if bound: - f_args = f_args[1:] - return f_args + sig = get_signature(function) + function_args = list(six.iterkeys(sig.parameters)) + for param_name, p in six.iteritems(sig.parameters): + if (p.kind in (Parameter.VAR_POSITIONAL, Parameter.VAR_KEYWORD) + or (required_only and p.default is not Parameter.empty)): + function_args.remove(param_name) + return function_args def accepts_kwargs(function): - """Returns True if function accepts kwargs.""" - argspec, _bound = _get_arg_spec(function) - return bool(argspec.keywords) + """Returns ``True`` if function accepts kwargs otherwise ``False``.""" + sig = get_signature(function) + return any(p.kind == Parameter.VAR_KEYWORD + for p in six.itervalues(sig.parameters)) diff --git a/requirements.txt b/requirements.txt index 03ccbe8d..cc0898a3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,6 +10,7 @@ pbr>=1.6 Babel>=1.3 six>=1.9.0 +funcsigs>=0.4;python_version=='2.7' or python_version=='2.6' iso8601>=0.1.9 oslo.i18n>=1.5.0 # Apache-2.0 monotonic>=0.3 # Apache-2.0