Welcome, guest | Sign In | My Account | Store | Cart

Sometimes inside a decorator that creates a function with a generic (args, *kwargs) signature, you want to access a value passed for a particular parameter name to a wrapped function, but don't know whether that value will be passed as a positional or keyword argument, or whether the wrapped function defines a default value for the parameter. The following utility function extracts this information for you.

Python, 37 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import inspect

def get_arg_value(func, argname, args, kwargs):
    """

    This function is meant to be used inside decorators, when you want
    to find what value will be available inside a wrapped function for
    a particular argument name.  It handles positional and keyword
    arguments and takes into account default values.  For example:

    >>> def foo(x, y): pass
    ... 
    >>> get_arg_value(foo, 'y', [1, 2], {})
    2
    >>> get_arg_value(foo, 'y', [1], {'y' : 2})
    2
    >>> def foo(x, y, z=300): pass
    ... 
    >>> get_arg_value(foo, 'z', [1], {'y' : 2})
    300
    >>> get_arg_value(foo, 'z', [1], {'y' : 2, 'z' : 5})
    5

    """
    # first check kwargs
    if argname in kwargs:
        return kwargs[argname]
    # OK.  could it be a positional argument?
    regargs, varargs, varkwargs, defaults=inspect.getargspec(func)
    if argname in regargs:
        regdict=dict(zip(regargs, args))
        if argname in regdict:
            return regdict[argname]
    defaultdict=dict(zip(reversed(regargs), defaults))
    if argname in defaultdict:
        return defaultdict[argname]
    raise ValueError("no such argument: %s" % argname)

        

As an example, you might want to write a decorator that checks permissions for some underlying operations, and have a naming convention for function parameters for the methods that implement these operations, so that the parameter "item_id" is always used for items that have permission levels associated with them. However, the signatures of these methods are all different; some may not use the item_id parameter at all. The decorator, then, when it invokes the wrapped method needs to do so in a generic fashion, without specifically passing in item_id. Therefore, some sort of introspection is necessary to obtain the value, if it exists. That's what this gives you; you'd call "item_id = get_arg_value(wrapped_func, 'item_id', args, kwargs)" inside the decorator, taking care to catch any ValueErrors that might result. A variant of this function that accepts a default value might also be useful.

2 comments

Mike Krell 17 years ago  # | flag

mistake in docstring. Your first example should return 2, not 1.

Yes, indeed! I've fixed it. Thanks.

Created by Jacob Smullyan on Tue, 12 Sep 2006 (PSF)
Python recipes (4591)
Jacob Smullyan's recipes (2)

Required Modules

Other Information and Tasks