|
1 # Copyright (c) 2012 netzguerilla.net <iro@netzguerilla.net> |
|
2 # |
|
3 # This file is part of Iro. |
|
4 # |
|
5 # Permission is hereby granted, free of charge, to any person obtaining a copy of |
|
6 # this software and associated documentation files (the "Software"), to deal in |
|
7 # the Software without restriction, including without limitation the rights to use, |
|
8 # copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the |
|
9 # #Software, and to permit persons to whom the Software is furnished to do so, |
|
10 # subject to the following conditions: |
|
11 # |
|
12 # The above copyright notice and this permission notice shall be included in |
|
13 # all copies or substantial portions of the Software. |
|
14 # |
|
15 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, |
|
16 # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A |
|
17 # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT |
|
18 # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
|
19 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE |
|
20 # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
|
21 |
|
22 from inspect import getargspec, ismethod |
|
23 import sys |
|
24 |
|
25 #code just copied form python 2.7.3 |
|
26 |
|
27 def getcallargs(func, *positional, **named): |
|
28 """Get the mapping of arguments to values. |
|
29 |
|
30 A dict is returned, with keys the function argument names (including the |
|
31 names of the * and ** arguments, if any), and values the respective bound |
|
32 values from 'positional' and 'named'.""" |
|
33 args, varargs, varkw, defaults = getargspec(func) |
|
34 f_name = func.__name__ |
|
35 arg2value = {} |
|
36 |
|
37 # The following closures are basically because of tuple parameter unpacking. |
|
38 assigned_tuple_params = [] |
|
39 def assign(arg, value): |
|
40 if isinstance(arg, str): |
|
41 arg2value[arg] = value |
|
42 else: |
|
43 assigned_tuple_params.append(arg) |
|
44 value = iter(value) |
|
45 for i, subarg in enumerate(arg): |
|
46 try: |
|
47 subvalue = next(value) |
|
48 except StopIteration: |
|
49 raise ValueError('need more than %d %s to unpack' % |
|
50 (i, 'values' if i > 1 else 'value')) |
|
51 assign(subarg,subvalue) |
|
52 try: |
|
53 next(value) |
|
54 except StopIteration: |
|
55 pass |
|
56 else: |
|
57 raise ValueError('too many values to unpack') |
|
58 def is_assigned(arg): |
|
59 if isinstance(arg,str): |
|
60 return arg in arg2value |
|
61 return arg in assigned_tuple_params |
|
62 if ismethod(func) and func.im_self is not None: |
|
63 # implicit 'self' (or 'cls' for classmethods) argument |
|
64 positional = (func.im_self,) + positional |
|
65 num_pos = len(positional) |
|
66 num_total = num_pos + len(named) |
|
67 num_args = len(args) |
|
68 num_defaults = len(defaults) if defaults else 0 |
|
69 for arg, value in zip(args, positional): |
|
70 assign(arg, value) |
|
71 if varargs: |
|
72 if num_pos > num_args: |
|
73 assign(varargs, positional[-(num_pos-num_args):]) |
|
74 else: |
|
75 assign(varargs, ()) |
|
76 elif 0 < num_args < num_pos: |
|
77 raise TypeError('%s() takes %s %d %s (%d given)' % ( |
|
78 f_name, 'at most' if defaults else 'exactly', num_args, |
|
79 'arguments' if num_args > 1 else 'argument', num_total)) |
|
80 elif num_args == 0 and num_total: |
|
81 if varkw: |
|
82 if num_pos: |
|
83 # XXX: We should use num_pos, but Python also uses num_total: |
|
84 raise TypeError('%s() takes exactly 0 arguments ' |
|
85 '(%d given)' % (f_name, num_total)) |
|
86 else: |
|
87 raise TypeError('%s() takes no arguments (%d given)' % |
|
88 (f_name, num_total)) |
|
89 for arg in args: |
|
90 if isinstance(arg, str) and arg in named: |
|
91 if is_assigned(arg): |
|
92 raise TypeError("%s() got multiple values for keyword " |
|
93 "argument '%s'" % (f_name, arg)) |
|
94 else: |
|
95 assign(arg, named.pop(arg)) |
|
96 if defaults: # fill in any missing values with the defaults |
|
97 for arg, value in zip(args[-num_defaults:], defaults): |
|
98 if not is_assigned(arg): |
|
99 assign(arg, value) |
|
100 if varkw: |
|
101 assign(varkw, named) |
|
102 elif named: |
|
103 unexpected = next(iter(named)) |
|
104 if isinstance(unexpected, unicode): |
|
105 unexpected = unexpected.encode(sys.getdefaultencoding(), 'replace') |
|
106 raise TypeError("%s() got an unexpected keyword argument '%s'" % |
|
107 (f_name, unexpected)) |
|
108 unassigned = num_args - len([arg for arg in args if is_assigned(arg)]) |
|
109 if unassigned: |
|
110 num_required = num_args - num_defaults |
|
111 raise TypeError('%s() takes %s %d %s (%d given)' % ( |
|
112 f_name, 'at least' if defaults else 'exactly', num_required, |
|
113 'arguments' if num_required > 1 else 'argument', num_total)) |
|
114 return arg2value |
|
115 |
|
116 |