|
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 |