1import rope.base.exceptions 2import rope.base.pyobjects 3from rope.base.builtins import Lambda 4from rope.base import worder 5 6 7class DefinitionInfo(object): 8 9 def __init__(self, function_name, is_method, args_with_defaults, 10 args_arg, keywords_arg): 11 self.function_name = function_name 12 self.is_method = is_method 13 self.args_with_defaults = args_with_defaults 14 self.args_arg = args_arg 15 self.keywords_arg = keywords_arg 16 17 def to_string(self): 18 return '%s(%s)' % (self.function_name, self.arguments_to_string()) 19 20 def arguments_to_string(self, from_index=0): 21 params = [] 22 for arg, default in self.args_with_defaults: 23 if default is not None: 24 params.append('%s=%s' % (arg, default)) 25 else: 26 params.append(arg) 27 if self.args_arg is not None: 28 params.append('*' + self.args_arg) 29 if self.keywords_arg: 30 params.append('**' + self.keywords_arg) 31 return ', '.join(params[from_index:]) 32 33 @staticmethod 34 def _read(pyfunction, code): 35 kind = pyfunction.get_kind() 36 is_method = kind == 'method' 37 is_lambda = kind == 'lambda' 38 info = _FunctionParser(code, is_method, is_lambda) 39 args, keywords = info.get_parameters() 40 args_arg = None 41 keywords_arg = None 42 if args and args[-1].startswith('**'): 43 keywords_arg = args[-1][2:] 44 del args[-1] 45 if args and args[-1].startswith('*'): 46 args_arg = args[-1][1:] 47 del args[-1] 48 args_with_defaults = [(name, None) for name in args] 49 args_with_defaults.extend(keywords) 50 return DefinitionInfo(info.get_function_name(), is_method, 51 args_with_defaults, args_arg, keywords_arg) 52 53 @staticmethod 54 def read(pyfunction): 55 pymodule = pyfunction.get_module() 56 word_finder = worder.Worder(pymodule.source_code) 57 lineno = pyfunction.get_ast().lineno 58 start = pymodule.lines.get_line_start(lineno) 59 if isinstance(pyfunction, Lambda): 60 call = word_finder.get_lambda_and_args(start) 61 else: 62 call = word_finder.get_function_and_args_in_header(start) 63 return DefinitionInfo._read(pyfunction, call) 64 65 66class CallInfo(object): 67 68 def __init__(self, function_name, args, keywords, args_arg, 69 keywords_arg, implicit_arg, constructor): 70 self.function_name = function_name 71 self.args = args 72 self.keywords = keywords 73 self.args_arg = args_arg 74 self.keywords_arg = keywords_arg 75 self.implicit_arg = implicit_arg 76 self.constructor = constructor 77 78 def to_string(self): 79 function = self.function_name 80 if self.implicit_arg: 81 function = self.args[0] + '.' + self.function_name 82 params = [] 83 start = 0 84 if self.implicit_arg or self.constructor: 85 start = 1 86 if self.args[start:]: 87 params.extend(self.args[start:]) 88 if self.keywords: 89 params.extend(['%s=%s' % (name, value) 90 for name, value in self.keywords]) 91 if self.args_arg is not None: 92 params.append('*' + self.args_arg) 93 if self.keywords_arg: 94 params.append('**' + self.keywords_arg) 95 return '%s(%s)' % (function, ', '.join(params)) 96 97 @staticmethod 98 def read(primary, pyname, definition_info, code): 99 is_method_call = CallInfo._is_method_call(primary, pyname) 100 is_constructor = CallInfo._is_class(pyname) 101 is_classmethod = CallInfo._is_classmethod(pyname) 102 info = _FunctionParser(code, is_method_call or is_classmethod) 103 args, keywords = info.get_parameters() 104 args_arg = None 105 keywords_arg = None 106 if args and args[-1].startswith('**'): 107 keywords_arg = args[-1][2:] 108 del args[-1] 109 if args and args[-1].startswith('*'): 110 args_arg = args[-1][1:] 111 del args[-1] 112 if is_constructor: 113 args.insert(0, definition_info.args_with_defaults[0][0]) 114 return CallInfo(info.get_function_name(), args, keywords, args_arg, 115 keywords_arg, is_method_call or is_classmethod, 116 is_constructor) 117 118 @staticmethod 119 def _is_method_call(primary, pyname): 120 return primary is not None and \ 121 isinstance(primary.get_object().get_type(), 122 rope.base.pyobjects.PyClass) and \ 123 CallInfo._is_method(pyname) 124 125 @staticmethod 126 def _is_class(pyname): 127 return pyname is not None and \ 128 isinstance(pyname.get_object(), 129 rope.base.pyobjects.PyClass) 130 131 @staticmethod 132 def _is_method(pyname): 133 if pyname is not None and \ 134 isinstance(pyname.get_object(), rope.base.pyobjects.PyFunction): 135 return pyname.get_object().get_kind() == 'method' 136 return False 137 138 @staticmethod 139 def _is_classmethod(pyname): 140 if pyname is not None and \ 141 isinstance(pyname.get_object(), rope.base.pyobjects.PyFunction): 142 return pyname.get_object().get_kind() == 'classmethod' 143 return False 144 145 146class ArgumentMapping(object): 147 148 def __init__(self, definition_info, call_info): 149 self.call_info = call_info 150 self.param_dict = {} 151 self.keyword_args = [] 152 self.args_arg = [] 153 for index, value in enumerate(call_info.args): 154 if index < len(definition_info.args_with_defaults): 155 name = definition_info.args_with_defaults[index][0] 156 self.param_dict[name] = value 157 else: 158 self.args_arg.append(value) 159 for name, value in call_info.keywords: 160 index = -1 161 for pair in definition_info.args_with_defaults: 162 if pair[0] == name: 163 self.param_dict[name] = value 164 break 165 else: 166 self.keyword_args.append((name, value)) 167 168 def to_call_info(self, definition_info): 169 args = [] 170 keywords = [] 171 for index in range(len(definition_info.args_with_defaults)): 172 name = definition_info.args_with_defaults[index][0] 173 if name in self.param_dict: 174 args.append(self.param_dict[name]) 175 else: 176 for i in range(index, len(definition_info.args_with_defaults)): 177 name = definition_info.args_with_defaults[i][0] 178 if name in self.param_dict: 179 keywords.append((name, self.param_dict[name])) 180 break 181 args.extend(self.args_arg) 182 keywords.extend(self.keyword_args) 183 return CallInfo(self.call_info.function_name, args, keywords, 184 self.call_info.args_arg, self.call_info.keywords_arg, 185 self.call_info.implicit_arg, 186 self.call_info.constructor) 187 188 189class _FunctionParser(object): 190 191 def __init__(self, call, implicit_arg, is_lambda=False): 192 self.call = call 193 self.implicit_arg = implicit_arg 194 self.word_finder = worder.Worder(self.call) 195 if is_lambda: 196 self.last_parens = self.call.rindex(':') 197 else: 198 self.last_parens = self.call.rindex(')') 199 self.first_parens = self.word_finder._find_parens_start( 200 self.last_parens) 201 202 def get_parameters(self): 203 args, keywords = self.word_finder.get_parameters(self.first_parens, 204 self.last_parens) 205 if self.is_called_as_a_method(): 206 instance = self.call[:self.call.rindex('.', 0, self.first_parens)] 207 args.insert(0, instance.strip()) 208 return args, keywords 209 210 def get_instance(self): 211 if self.is_called_as_a_method(): 212 return self.word_finder.get_primary_at( 213 self.call.rindex('.', 0, self.first_parens) - 1) 214 215 def get_function_name(self): 216 if self.is_called_as_a_method(): 217 return self.word_finder.get_word_at(self.first_parens - 1) 218 else: 219 return self.word_finder.get_primary_at(self.first_parens - 1) 220 221 def is_called_as_a_method(self): 222 return self.implicit_arg and '.' in self.call[:self.first_parens] 223