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, 7 months ago  # | flag

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

Jacob Smullyan (author) 17 years, 7 months ago  # | flag

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