import sys
from inspect import ArgSpec
if sys.version_info[0] >= 3: # pragma: no cover
from inspect import getfullargspec
unicode = str
else: # pragma: no cover
from collections import namedtuple
FullArgSpec = namedtuple('FullArgSpec', 'args varargs varkw defaults'
' kwonlyargs kwonlydefaults annotations')
from inspect import getargspec as _getargspec
[docs] def getargspec(function):
# print 'hasattr im_func', hasattr(function, 'im_func')
if hasattr(function, 'im_func'):
# print('this is a special function : %s' % function)
# For methods or classmethods drop the first
# argument from the returned list because
# python supplies that automatically for us.
# Note that this differs from what
# inspect.getargspec() returns for methods.
# NB: We use im_func so we work with
# instancemethod objects also.
x = _getargspec(function.im_func)
new_args = x.args[1:]
spec = ArgSpec(args=new_args, varargs=x.varargs,
keywords=x.keywords, defaults=x.defaults)
return spec
# print 'calling normal %s' % function
return _getargspec(function)
[docs] def getfullargspec(function):
spec = getargspec(function)
fullspec = FullArgSpec(args=spec.args, varargs=spec.varargs,
varkw=spec.keywords,
defaults=spec.defaults, kwonlyargs=[],
kwonlydefaults=None,
annotations={})
return fullspec
# Backport inspect.getcallargs from Python 2.7 to 2.6
if sys.version_info[:2] == (2, 7):
# noinspection PyUnresolvedReferences
from inspect import getcallargs
else: # pragma: no cover
inPy3k = sys.version_info[0] == 3
from inspect import ismethod
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, \
kwonlyargs, kwonlydefaults, annotations = getfullargspec(func)
if kwonlyargs:
raise ValueError("I'm sorry, I don't have the logic to use kwonlyargs. "
"Perhapse you can help PyContracts and implement this? Thanks.")
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 not inPy3k:
im_self = getattr(func, 'im_self', None)
else:
im_self = getattr(func, '__self__', None)
if ismethod(func) and im_self is not None:
# implicit 'self' (or 'cls' for classmethods) argument
positional = (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:
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