|
|
- # This is a copy of the inspect.getcallargs() function from Python 2.7
- # so we can provide it for use under Python 2.6. As the code in this
- # file derives from the Python distribution, it falls under the version
- # of the PSF license used for Python 2.7.
-
- from inspect import getargspec, ismethod
- import sys
-
- def getcallargs(func, *positional, **named):
- """Get the mapping of arguments to values.
-
- A dict is returned, with keys the function argument names (including the
- names of the * and ** arguments, if any), and values the respective bound
- values from 'positional' and 'named'."""
- args, varargs, varkw, defaults = getargspec(func)
- f_name = func.__name__
- arg2value = {}
-
- # The following closures are basically because of tuple parameter unpacking.
- assigned_tuple_params = []
- def assign(arg, value):
- if isinstance(arg, str):
- arg2value[arg] = value
- else:
- assigned_tuple_params.append(arg)
- value = iter(value)
- for i, subarg in enumerate(arg):
- try:
- subvalue = next(value)
- except StopIteration:
- raise ValueError('need more than %d %s to unpack' %
- (i, 'values' if i > 1 else 'value'))
- assign(subarg, subvalue)
- try:
- next(value)
- except StopIteration:
- pass
- else:
- raise ValueError('too many values to unpack')
- def is_assigned(arg):
- if isinstance(arg, str):
- return arg in arg2value
- return arg in assigned_tuple_params
- if ismethod(func) and func.im_self is not None:
- # implicit 'self' (or 'cls' for classmethods) argument
- positional = (func.im_self,) + positional
- num_pos = len(positional)
- num_total = num_pos + len(named)
- num_args = len(args)
- num_defaults = len(defaults) if defaults else 0
- for arg, value in zip(args, positional):
- assign(arg, value)
- if varargs:
- if num_pos > num_args:
- assign(varargs, positional[-(num_pos-num_args):])
- else:
- assign(varargs, ())
- elif 0 < num_args < num_pos:
- raise TypeError('%s() takes %s %d %s (%d given)' % (
- f_name, 'at most' if defaults else 'exactly', num_args,
- 'arguments' if num_args > 1 else 'argument', num_total))
- elif num_args == 0 and num_total:
- if varkw:
- if num_pos:
- # XXX: We should use num_pos, but Python also uses num_total:
- raise TypeError('%s() takes exactly 0 arguments '
- '(%d given)' % (f_name, num_total))
- else:
- raise TypeError('%s() takes no arguments (%d given)' %
- (f_name, num_total))
- for arg in args:
- if isinstance(arg, str) and arg in named:
- if is_assigned(arg):
- raise TypeError("%s() got multiple values for keyword "
- "argument '%s'" % (f_name, arg))
- else:
- assign(arg, named.pop(arg))
- if defaults: # fill in any missing values with the defaults
- for arg, value in zip(args[-num_defaults:], defaults):
- if not is_assigned(arg):
- assign(arg, value)
- if varkw:
- assign(varkw, named)
- elif named:
- unexpected = next(iter(named))
- if isinstance(unexpected, unicode):
- unexpected = unexpected.encode(sys.getdefaultencoding(), 'replace')
- raise TypeError("%s() got an unexpected keyword argument '%s'" %
- (f_name, unexpected))
- unassigned = num_args - len([arg for arg in args if is_assigned(arg)])
- if unassigned:
- num_required = num_args - num_defaults
- raise TypeError('%s() takes %s %d %s (%d given)' % (
- f_name, 'at least' if defaults else 'exactly', num_required,
- 'arguments' if num_required > 1 else 'argument', num_total))
- return arg2value
|