iro/inspect_getcallargs.py
branchdevel
changeset 282 50cc13814bfb
child 294 0e75bd39767d
equal deleted inserted replaced
281:76572d14bd27 282:50cc13814bfb
       
     1 from inspect import getargspec, ismethod
       
     2 import sys
       
     3 
       
     4 #code just copied form python 2.7.3 
       
     5 
       
     6 def getcallargs(func, *positional, **named):
       
     7     """Get the mapping of arguments to values.
       
     8 
       
     9     A dict is returned, with keys the function argument names (including the
       
    10     names of the * and ** arguments, if any), and values the respective bound
       
    11     values from 'positional' and 'named'."""
       
    12     args, varargs, varkw, defaults = getargspec(func)
       
    13     f_name = func.__name__
       
    14     arg2value = {}
       
    15 
       
    16     # The following closures are basically because of tuple parameter unpacking.
       
    17     assigned_tuple_params = []
       
    18     def assign(arg, value):
       
    19         if isinstance(arg, str):
       
    20             arg2value[arg] = value
       
    21         else:
       
    22             assigned_tuple_params.append(arg)
       
    23             value = iter(value)
       
    24             for i, subarg in enumerate(arg):
       
    25                 try:
       
    26                     subvalue = next(value)
       
    27                 except StopIteration:
       
    28                     raise ValueError('need more than %d %s to unpack' %
       
    29                                      (i, 'values' if i > 1 else 'value'))
       
    30                 assign(subarg,subvalue)
       
    31             try:
       
    32                 next(value)
       
    33             except StopIteration:
       
    34                 pass
       
    35             else:
       
    36                 raise ValueError('too many values to unpack')
       
    37     def is_assigned(arg):
       
    38         if isinstance(arg,str):
       
    39             return arg in arg2value
       
    40         return arg in assigned_tuple_params
       
    41     if ismethod(func) and func.im_self is not None:
       
    42         # implicit 'self' (or 'cls' for classmethods) argument
       
    43         positional = (func.im_self,) + positional
       
    44     num_pos = len(positional)
       
    45     num_total = num_pos + len(named)
       
    46     num_args = len(args)
       
    47     num_defaults = len(defaults) if defaults else 0
       
    48     for arg, value in zip(args, positional):
       
    49         assign(arg, value)
       
    50     if varargs:
       
    51         if num_pos > num_args:
       
    52             assign(varargs, positional[-(num_pos-num_args):])
       
    53         else:
       
    54             assign(varargs, ())
       
    55     elif 0 < num_args < num_pos:
       
    56         raise TypeError('%s() takes %s %d %s (%d given)' % (
       
    57             f_name, 'at most' if defaults else 'exactly', num_args,
       
    58             'arguments' if num_args > 1 else 'argument', num_total))
       
    59     elif num_args == 0 and num_total:
       
    60         if varkw:
       
    61             if num_pos:
       
    62                 # XXX: We should use num_pos, but Python also uses num_total:
       
    63                 raise TypeError('%s() takes exactly 0 arguments '
       
    64                                 '(%d given)' % (f_name, num_total))
       
    65         else:
       
    66             raise TypeError('%s() takes no arguments (%d given)' %
       
    67                             (f_name, num_total))
       
    68     for arg in args:
       
    69         if isinstance(arg, str) and arg in named:
       
    70             if is_assigned(arg):
       
    71                 raise TypeError("%s() got multiple values for keyword "
       
    72                                 "argument '%s'" % (f_name, arg))
       
    73             else:
       
    74                 assign(arg, named.pop(arg))
       
    75     if defaults:    # fill in any missing values with the defaults
       
    76         for arg, value in zip(args[-num_defaults:], defaults):
       
    77             if not is_assigned(arg):
       
    78                 assign(arg, value)
       
    79     if varkw:
       
    80         assign(varkw, named)
       
    81     elif named:
       
    82         unexpected = next(iter(named))
       
    83         if isinstance(unexpected, unicode):
       
    84             unexpected = unexpected.encode(sys.getdefaultencoding(), 'replace')
       
    85         raise TypeError("%s() got an unexpected keyword argument '%s'" %
       
    86                         (f_name, unexpected))
       
    87     unassigned = num_args - len([arg for arg in args if is_assigned(arg)])
       
    88     if unassigned:
       
    89         num_required = num_args - num_defaults
       
    90         raise TypeError('%s() takes %s %d %s (%d given)' % (
       
    91             f_name, 'at least' if defaults else 'exactly', num_required,
       
    92             'arguments' if num_required > 1 else 'argument', num_total))
       
    93     return arg2value
       
    94 
       
    95