1 from collections import defaultdict 2 from inspect import Parameter 3 4 from jedi import debug 5 from jedi.inference.utils import PushBackIterator 6 from jedi.inference import analysis 7 from jedi.inference.lazy_value import LazyKnownValue, \ 8 LazyTreeValue, LazyUnknownValue 9 from jedi.inference.value import iterable 10 from jedi.inference.names import ParamName 11 12 13 def _add_argument_issue(error_name, lazy_value, message): 14 if isinstance(lazy_value, LazyTreeValue): 15 node = lazy_value.data 16 if node.parent.type == 'argument': 17 node = node.parent 18 return analysis.add(lazy_value.context, error_name, node, message) 19 20 21 class ExecutedParamName(ParamName): 22 def __init__(self, function_value, arguments, param_node, lazy_value, is_default=False): 23 super().__init__(function_value, param_node.name, arguments=arguments) 24 self._lazy_value = lazy_value 25 self._is_default = is_default 26 27 def infer(self): 28 return self._lazy_value.infer() 29 30 def matches_signature(self): 31 if self._is_default: 32 return True 33 argument_values = self.infer().py__class__() 34 if self.get_kind() in (Parameter.VAR_POSITIONAL, Parameter.VAR_KEYWORD): 35 return True 36 annotations = self.infer_annotation(execute_annotation=False) 37 if not annotations: 38 # If we cannot infer annotations - or there aren't any - pretend 39 # that the signature matches. 40 return True 41 matches = any(c1.is_sub_class_of(c2) 42 for c1 in argument_values 43 for c2 in annotations.gather_annotation_classes()) 44 debug.dbg("param compare %s: %s <=> %s", 45 matches, argument_values, annotations, color='BLUE') 46 return matches 47 48 def __repr__(self): 49 return '<%s: %s>' % (self.__class__.__name__, self.string_name) 50 51 52 def get_executed_param_names_and_issues(function_value, arguments): 53 """ 54 Return a tuple of: 55 - a list of `ExecutedParamName`s corresponding to the arguments of the 56 function execution `function_value`, containing the inferred value of 57 those arguments (whether explicit or default) 58 - a list of the issues encountered while building that list 59 60 For example, given: 61 ``` 62 def foo(a, b, c=None, d='d'): ... 63 64 foo(42, c='c') 65 ``` 66 67 Then for the execution of `foo`, this will return a tuple containing: 68 - a list with entries for each parameter a, b, c & d; the entries for a, 69 c, & d will have their values (42, 'c' and 'd' respectively) included. 70 - a list with a single entry about the lack of a value for `b` 71 """ 72 def too_many_args(argument): 73 m = _error_argument_count(funcdef, len(unpacked_va)) 74 # Just report an error for the first param that is not needed (like 75 # cPython). 76 if arguments.get_calling_nodes(): 77 # There might not be a valid calling node so check for that first. 78 issues.append( 79 _add_argument_issue( 80 'type-error-too-many-arguments', 81 argument, 82 message=m 83 ) 84 ) 85 else: 86 issues.append(None) 87 debug.warning('non-public warning: %s', m) 88 89 issues = [] # List[Optional[analysis issue]] 90 result_params = [] 91 param_dict = {} 92 funcdef = function_value.tree_node 93 # Default params are part of the value where the function was defined. 94 # This means that they might have access on class variables that the 95 # function itself doesn't have. 96 default_param_context = function_value.get_default_param_context() 97 98 for param in funcdef.get_params(): 99 param_dict[param.name.value] = param 100 unpacked_va = list(arguments.unpack(funcdef)) 101 var_arg_iterator = PushBackIterator(iter(unpacked_va)) 102 103 non_matching_keys = defaultdict(lambda: []) 104 keys_used = {} 105 keys_only = False 106 had_multiple_value_error = False 107 for param in funcdef.get_params(): 108 # The value and key can both be null. There, the defaults apply. 109 # args / kwargs will just be empty arrays / dicts, respectively. 110 # Wrong value count is just ignored. If you try to test cases that are 111 # not allowed in Python, Jedi will maybe not show any completions. 112 is_default = False 113 key, argument = next(var_arg_iterator, (None, None)) 114 while key is not None: 115 keys_only = True 116 try: 117 key_param = param_dict[key] 118 except KeyError: 119 non_matching_keys[key] = argument 120 else: 121 if key in keys_used: 122 had_multiple_value_error = True 123 m = ("TypeError: %s() got multiple values for keyword argument '%s'." 124 % (funcdef.name, key)) 125 for contextualized_node in arguments.get_calling_nodes(): 126 issues.append( 127 analysis.add(contextualized_node.context, 128 'type-error-multiple-values', 129 contextualized_node.node, message=m) 130 ) 131 else: 132 keys_used[key] = ExecutedParamName( 133 function_value, arguments, key_param, argument) 134 key, argument = next(var_arg_iterator, (None, None)) 135 136 try: 137 result_params.append(keys_used[param.name.value]) 138 continue 139 except KeyError: 140 pass 141 142 if param.star_count == 1: 143 # *args param 144 lazy_value_list = [] 145 if argument is not None: 146 lazy_value_list.append(argument) 147 for key, argument in var_arg_iterator: 148 # Iterate until a key argument is found. 149 if key: 150 var_arg_iterator.push_back((key, argument)) 151 break 152 lazy_value_list.append(argument) 153 seq = iterable.FakeTuple(function_value.inference_state, lazy_value_list) 154 result_arg = LazyKnownValue(seq) 155 elif param.star_count == 2: 156 if argument is not None: 157 too_many_args(argument) 158 # **kwargs param 159 dct = iterable.FakeDict(function_value.inference_state, dict(non_matching_keys)) 160 result_arg = LazyKnownValue(dct) 161 non_matching_keys = {} 162 else: 163 # normal param 164 if argument is None: 165 # No value: Return an empty container 166 if param.default is None: 167 result_arg = LazyUnknownValue() 168 if not keys_only: 169 for contextualized_node in arguments.get_calling_nodes(): 170 m = _error_argument_count(funcdef, len(unpacked_va)) 171 issues.append( 172 analysis.add( 173 contextualized_node.context, 174 'type-error-too-few-arguments', 175 contextualized_node.node, 176 message=m, 177 ) 178 ) 179 else: 180 result_arg = LazyTreeValue(default_param_context, param.default) 181 is_default = True 182 else: 183 result_arg = argument 184 185 result_params.append(ExecutedParamName( 186 function_value, arguments, param, result_arg, is_default=is_default 187 )) 188 if not isinstance(result_arg, LazyUnknownValue): 189 keys_used[param.name.value] = result_params[-1] 190 191 if keys_only: 192 # All arguments should be handed over to the next function. It's not 193 # about the values inside, it's about the names. Jedi needs to now that 194 # there's nothing to find for certain names. 195 for k in set(param_dict) - set(keys_used): 196 param = param_dict[k] 197 198 if not (non_matching_keys or had_multiple_value_error 199 or param.star_count or param.default): 200 # add a warning only if there's not another one. 201 for contextualized_node in arguments.get_calling_nodes(): 202 m = _error_argument_count(funcdef, len(unpacked_va)) 203 issues.append( 204 analysis.add(contextualized_node.context, 205 'type-error-too-few-arguments', 206 contextualized_node.node, message=m) 207 ) 208 209 for key, lazy_value in non_matching_keys.items(): 210 m = "TypeError: %s() got an unexpected keyword argument '%s'." \ 211 % (funcdef.name, key) 212 issues.append( 213 _add_argument_issue( 214 'type-error-keyword-argument', 215 lazy_value, 216 message=m 217 ) 218 ) 219 220 remaining_arguments = list(var_arg_iterator) 221 if remaining_arguments: 222 first_key, lazy_value = remaining_arguments[0] 223 too_many_args(lazy_value) 224 return result_params, issues 225 226 227 def get_executed_param_names(function_value, arguments): 228 """ 229 Return a list of `ExecutedParamName`s corresponding to the arguments of the 230 function execution `function_value`, containing the inferred value of those 231 arguments (whether explicit or default). Any issues building this list (for 232 example required arguments which are missing in the invocation) are ignored. 233 234 For example, given: 235 ``` 236 def foo(a, b, c=None, d='d'): ... 237 238 foo(42, c='c') 239 ``` 240 241 Then for the execution of `foo`, this will return a list containing entries 242 for each parameter a, b, c & d; the entries for a, c, & d will have their 243 values (42, 'c' and 'd' respectively) included. 244 """ 245 return get_executed_param_names_and_issues(function_value, arguments)[0] 246 247 248 def _error_argument_count(funcdef, actual_count): 249 params = funcdef.get_params() 250 default_arguments = sum(1 for p in params if p.default or p.star_count) 251 252 if default_arguments == 0: 253 before = 'exactly ' 254 else: 255 before = 'from %s to ' % (len(params) - default_arguments) 256 return ('TypeError: %s() takes %s%s arguments (%s given).' 257 % (funcdef.name, before, len(params), actual_count)) 258