Reputation: 169543
I have a dict, which I need to pass key/values as keyword arguments.. For example..
d_args = {'kw1': 'value1', 'kw2': 'value2'}
example(**d_args)
This works fine, but if there are values in the d_args dict that are not accepted by the example
function, it obviously dies.. Say, if the example function is defined as def example(kw2):
This is a problem since I don't control either the generation of the d_args
, or the example
function.. They both come from external modules, and example
only accepts some of the keyword-arguments from the dict..
Ideally I would just do
parsed_kwargs = feedparser.parse(the_url)
valid_kwargs = get_valid_kwargs(parsed_kwargs, valid_for = PyRSS2Gen.RSS2)
PyRSS2Gen.RSS2(**valid_kwargs)
I will probably just filter the dict, from a list of valid keyword-arguments, but I was wondering: Is there a way to programatically list the keyword arguments the a specific function takes?
Upvotes: 125
Views: 82076
Reputation: 1
Not tested a lot but this will work for my case:
import inspect
def example(arg_1: int, arg_2: list, arg_optional="Hello", *, kwarg, kwarg_optional="Hi"):
pass
spec = inspect.getfullargspec(example)
args_required = spec.args
args_optional = args_required[-len(spec.defaults or []):]
args_required = args_required[:-len(args_optional)]
kwargs_required, kwargs_optional = spec.kwonlyargs, (spec.kwonlydefaults or {})
kwargs_required = [key for key in kwargs_required if key not in kwargs_optional]
unlimited_args, unlimited_kwargs = bool(spec.varargs), bool(spec.varkw)
annotations = spec.annotations
print(args_required, args_optional, kwargs_required, kwargs_optional, unlimited_args, unlimited_kwargs, annotations)
Upvotes: 0
Reputation: 57774
This will print names of all passable arguments, keyword and non-keyword ones:
def func(one, two="value"):
y = one, two
return y
print func.func_code.co_varnames[:func.func_code.co_argcount]
This is because first co_varnames
are always parameters (next are local variables, like y
in the example above).
So now you could have a function:
def get_valid_args(func, args_dict):
'''Return dictionary without invalid function arguments.'''
validArgs = func.func_code.co_varnames[:func.func_code.co_argcount]
return dict((key, value) for key, value in args_dict.iteritems()
if key in validArgs)
Which you then could use like this:
>>> func(**get_valid_args(func, args))
if you really need only keyword arguments of a function, you can use the func_defaults
attribute to extract them:
def get_valid_kwargs(func, args_dict):
validArgs = func.func_code.co_varnames[:func.func_code.co_argcount]
kwargsLen = len(func.func_defaults) # number of keyword arguments
validKwargs = validArgs[-kwargsLen:] # because kwargs are last
return dict((key, value) for key, value in args_dict.iteritems()
if key in validKwargs)
You could now call your function with known args, but extracted kwargs, e.g.:
func(param1, param2, **get_valid_kwargs(func, kwargs_dict))
This assumes that func
uses no *args
or **kwargs
magic in its signature.
Upvotes: 35
Reputation: 119211
A little nicer than inspecting the code object directly and working out the variables is to use the inspect module.
>>> import inspect
>>> def func(a,b,c=42, *args, **kwargs): pass
>>> inspect.getargspec(func)
(['a', 'b', 'c'], 'args', 'kwargs', (42,))
If you want to know if its callable with a particular set of args, you need the args without a default already specified. These can be got by:
def get_required_args(func):
args, varargs, varkw, defaults = inspect.getargspec(func)
if defaults:
args = args[:-len(defaults)]
return args # *args and **kwargs are not required, so ignore them.
Then a function to tell what you are missing from your particular dict is:
def missing_args(func, argdict):
return set(get_required_args(func)).difference(argdict)
Similarly, to check for invalid args, use:
def invalid_args(func, argdict):
args, varargs, varkw, defaults = inspect.getargspec(func)
if varkw: return set() # All accepted
return set(argdict) - set(args)
And so a full test if it is callable is :
def is_callable_with_args(func, argdict):
return not missing_args(func, argdict) and not invalid_args(func, argdict)
(This is good only as far as python's arg parsing. Any runtime checks for invalid values in kwargs
obviously can't be detected.)
Upvotes: 169
Reputation: 160377
For a Python 3 solution, you can use inspect.signature
and filter according to the kind of parameters you'd like to know about.
Taking a sample function with positional or keyword, keyword-only, var positional and var keyword parameters:
def spam(a, b=1, *args, c=2, **kwargs):
print(a, b, args, c, kwargs)
You can create a signature object for it:
from inspect import signature
sig = signature(spam)
and then filter with a list comprehension to find out the details you need:
>>> # positional or keyword
>>> [p.name for p in sig.parameters.values() if p.kind == p.POSITIONAL_OR_KEYWORD]
['a', 'b']
>>> # keyword only
>>> [p.name for p in sig.parameters.values() if p.kind == p.KEYWORD_ONLY]
['c']
and, similarly, for var positionals using p.VAR_POSITIONAL
and var keyword with VAR_KEYWORD
.
In addition, you can add a clause to the if to check if a default value exists by checking if p.default
equals p.empty
.
Upvotes: 22
Reputation: 414139
In Python 3.0:
>>> import inspect
>>> import fileinput
>>> print(inspect.getfullargspec(fileinput.input))
FullArgSpec(args=['files', 'inplace', 'backup', 'bufsize', 'mode', 'openhook'],
varargs=None, varkw=None, defaults=(None, 0, '', 0, 'r', None), kwonlyargs=[],
kwdefaults=None, annotations={})
Upvotes: 10
Reputation: 229321
Extending DzinX's answer:
argnames = example.func_code.co_varnames[:func.func_code.co_argcount]
args = dict((key, val) for key,val in d_args.iteritems() if key in argnames)
example(**args)
Upvotes: 3