ASPN ActiveState Programmer Network  
ActiveState, a division of Sophos
/ Home / Perl / PHP / Python / Tcl / XSLT /
/ Safari / My ASPN /
Cookbooks | Documentation | Mailing Lists | Modules | News Feeds | Products | User Groups
Submit Recipe
My Recipes

All Recipes
All Cookbooks


View by Category

Title: Introspecting call arguments
Submitter: George Sakkis (other recipes)
Last Updated: 2008/03/18
Version no: 1.0
Category: Algorithms

 

Not Rated yet


Description:

This recipe implements in pure Python the algorithm used by the interpreter for binding the values passed as parameters in a function call to the formal arguments of the function.

This is useful for decorators that want to take into account this binding when wrapping a function (e.g. for type checking).

Source: Text Source

from itertools import izip
from inspect import getargspec, ismethod


def getcallargs(func, *args, **kwds):
    '''Get the actual value bounded to each formal parameter when calling
    `func(*args,**kwds)`.

    It works for methods too (bounded, unbounded, staticmethods, classmethods).

    @rtype: dict
    @returns: A mapping of every formal parameter (including *varargs
    and **kwargs if present) of the function to the respective bounded value.

    Examples::
        >>> def func(a, b='foo', c=None, *x, **y):
        ...     pass
        >>> sorted(getcallargs(func, 5).items())
        [('a', 5), ('b', 'foo'), ('c', None), ('x', ()), ('y', {})]
        >>> sorted(getcallargs(func, 5, 'bar').items())
        [('a', 5), ('b', 'bar'), ('c', None), ('x', ()), ('y', {})]
        >>> sorted(getcallargs(func, 5, c=['a', 'b']).items())
        [('a', 5), ('b', 'foo'), ('c', ['a', 'b']), ('x', ()), ('y', {})]
        >>> sorted(getcallargs(func, 5, 6, 7, 8).items())
        [('a', 5), ('b', 6), ('c', 7), ('x', (8,)), ('y', {})]
        >>> sorted(getcallargs(func, 5, z=3, b=2).items())
        [('a', 5), ('b', 2), ('c', None), ('x', ()), ('y', {'z': 3})]
    '''
    arg2value = {}
    f_name = func.func_name
    spec_args, varargs, varkw, defaults = getargspec(func)
    # handle methods
    if ismethod(func):
        # implicit 'self' (or 'cls' for classmethods) argument: func.im_self
        if func.im_self is not None:
            arg2value[spec_args.pop(0)] = func.im_self
        elif not args or not isinstance(args[0], func.im_class):
            got = args and ('%s instance' % type(args[0]).__name__) or 'nothing'
            raise TypeError('unbound method %s() must be called with %s instance '
                            'as first argument (got %s instead)' %
                            (f_name, func.im_class.__name__, got))
    num_args = len(args)
    has_kwds = bool(kwds)
    num_spec_args = len(spec_args)
    num_defaults = len(defaults or ())
    # get the expected arguments passed positionally
    arg2value.update(izip(spec_args,args))
    # get the expected arguments passed by name
    for arg in spec_args:
        if arg in kwds:
            if arg in arg2value:
                raise TypeError("%s() got multiple values for keyword "
                                "argument '%s'" % (f_name,arg))
            else:
                arg2value[arg] = kwds.pop(arg)
    # fill in any missing values with the defaults
    if defaults:
        for arg,val in izip(spec_args[-num_defaults:],defaults):
            if arg not in arg2value:
                arg2value[arg] = val
    # ensure that all required args have a value
    for arg in spec_args:
        if arg not in arg2value:
            num_required = num_spec_args - num_defaults
            raise TypeError('%s() takes at least %d %s argument%s (%d given)'
                            % (f_name, num_required,
                               has_kwds and 'non-keyword ' or '',
                               num_required>1 and 's' or '', num_args))
    # handle any remaining named arguments
    if varkw:
        arg2value[varkw] = kwds
    elif kwds:
        raise TypeError("%s() got an unexpected keyword argument '%s'" %
                        (f_name, iter(kwds).next()))
    # handle any remaining positional arguments
    if varargs:
        if num_args > num_spec_args:
            arg2value[varargs] = args[-(num_args-num_spec_args):]
        else:
            arg2value[varargs] = ()
    elif num_spec_args < num_args:
        raise TypeError('%s() takes %s %d argument%s (%d given)' %
                        (f_name, defaults and 'at most' or 'exactly',
                         num_spec_args, num_spec_args>1 and 's' or '', num_args))
    return arg2value

Discussion:



Add comment

No comments.



Highest rated recipes:

1. A simple XML-RPC server

2. Web service accessible ...

3. Treat the Win32 Registry ...

4. Watching a directory ...

5. Union Find data structure

6. Function Decorators by ...

7. MS SQL Server log monitor

8. Table objects with ...

9. wx twisted support using ...

10. More accurate sum




Privacy Policy | Email Opt-out | Feedback | Syndication
© 2006 ActiveState Software Inc. All rights reserved.