1 """ 2 This module is responsible for inferring *args and **kwargs for signatures. 3 4 This means for example in this case:: 5 6 def foo(a, b, c): ... 7 8 def bar(*args): 9 return foo(1, *args) 10 11 The signature here for bar should be `bar(b, c)` instead of bar(*args). 12 """ 13 from inspect import Parameter 14 15 from parso import tree 16 17 from jedi.inference.utils import to_list 18 from jedi.inference.names import ParamNameWrapper 19 from jedi.inference.helpers import is_big_annoying_library 20 21 22 def _iter_nodes_for_param(param_name): 23 from parso.python.tree import search_ancestor 24 from jedi.inference.arguments import TreeArguments 25 26 execution_context = param_name.parent_context 27 # Walk up the parso tree to get the FunctionNode we want. We use the parso 28 # tree rather than going via the execution context so that we're agnostic of 29 # the specific scope we're evaluating within (i.e: module or function, 30 # etc.). 31 function_node = tree.search_ancestor(param_name.tree_name, 'funcdef', 'lambdef') 32 module_node = function_node.get_root_node() 33 start = function_node.children[-1].start_pos 34 end = function_node.children[-1].end_pos 35 for name in module_node.get_used_names().get(param_name.string_name): 36 if start <= name.start_pos < end: 37 # Is used in the function 38 argument = name.parent 39 if argument.type == 'argument' \ 40 and argument.children[0] == '*' * param_name.star_count: 41 trailer = search_ancestor(argument, 'trailer') 42 if trailer is not None: # Make sure we're in a function 43 context = execution_context.create_context(trailer) 44 if _goes_to_param_name(param_name, context, name): 45 values = _to_callables(context, trailer) 46 47 args = TreeArguments.create_cached( 48 execution_context.inference_state, 49 context=context, 50 argument_node=trailer.children[1], 51 trailer=trailer, 52 ) 53 for c in values: 54 yield c, args 55 56 57 def _goes_to_param_name(param_name, context, potential_name): 58 if potential_name.type != 'name': 59 return False 60 from jedi.inference.names import TreeNameDefinition 61 found = TreeNameDefinition(context, potential_name).goto() 62 return any(param_name.parent_context == p.parent_context 63 and param_name.start_pos == p.start_pos 64 for p in found) 65 66 67 def _to_callables(context, trailer): 68 from jedi.inference.syntax_tree import infer_trailer 69 70 atom_expr = trailer.parent 71 index = atom_expr.children[0] == 'await' 72 # Infer atom first 73 values = context.infer_node(atom_expr.children[index]) 74 for trailer2 in atom_expr.children[index + 1:]: 75 if trailer == trailer2: 76 break 77 values = infer_trailer(context, values, trailer2) 78 return values 79 80 81 def _remove_given_params(arguments, param_names): 82 count = 0 83 used_keys = set() 84 for key, _ in arguments.unpack(): 85 if key is None: 86 count += 1 87 else: 88 used_keys.add(key) 89 90 for p in param_names: 91 if count and p.maybe_positional_argument(): 92 count -= 1 93 continue 94 if p.string_name in used_keys and p.maybe_keyword_argument(): 95 continue 96 yield p 97 98 99 @to_list 100 def process_params(param_names, star_count=3): # default means both * and ** 101 if param_names: 102 if is_big_annoying_library(param_names[0].parent_context): 103 # At first this feature can look innocent, but it does a lot of 104 # type inference in some cases, so we just ditch it. 105 yield from param_names 106 return 107 108 used_names = set() 109 arg_callables = [] 110 kwarg_callables = [] 111 112 kw_only_names = [] 113 kwarg_names = [] 114 arg_names = [] 115 original_arg_name = None 116 original_kwarg_name = None 117 for p in param_names: 118 kind = p.get_kind() 119 if kind == Parameter.VAR_POSITIONAL: 120 if star_count & 1: 121 arg_callables = _iter_nodes_for_param(p) 122 original_arg_name = p 123 elif p.get_kind() == Parameter.VAR_KEYWORD: 124 if star_count & 2: 125 kwarg_callables = list(_iter_nodes_for_param(p)) 126 original_kwarg_name = p 127 elif kind == Parameter.KEYWORD_ONLY: 128 if star_count & 2: 129 kw_only_names.append(p) 130 elif kind == Parameter.POSITIONAL_ONLY: 131 if star_count & 1: 132 yield p 133 else: 134 if star_count == 1: 135 yield ParamNameFixedKind(p, Parameter.POSITIONAL_ONLY) 136 elif star_count == 2: 137 kw_only_names.append(ParamNameFixedKind(p, Parameter.KEYWORD_ONLY)) 138 else: 139 used_names.add(p.string_name) 140 yield p 141 142 # First process *args 143 longest_param_names = () 144 found_arg_signature = False 145 found_kwarg_signature = False 146 for func_and_argument in arg_callables: 147 func, arguments = func_and_argument 148 new_star_count = star_count 149 if func_and_argument in kwarg_callables: 150 kwarg_callables.remove(func_and_argument) 151 else: 152 new_star_count = 1 153 154 for signature in func.get_signatures(): 155 found_arg_signature = True 156 if new_star_count == 3: 157 found_kwarg_signature = True 158 args_for_this_func = [] 159 for p in process_params( 160 list(_remove_given_params( 161 arguments, 162 signature.get_param_names(resolve_stars=False) 163 )), new_star_count): 164 if p.get_kind() == Parameter.VAR_KEYWORD: 165 kwarg_names.append(p) 166 elif p.get_kind() == Parameter.VAR_POSITIONAL: 167 arg_names.append(p) 168 elif p.get_kind() == Parameter.KEYWORD_ONLY: 169 kw_only_names.append(p) 170 else: 171 args_for_this_func.append(p) 172 if len(args_for_this_func) > len(longest_param_names): 173 longest_param_names = args_for_this_func 174 175 for p in longest_param_names: 176 if star_count == 1 and p.get_kind() != Parameter.VAR_POSITIONAL: 177 yield ParamNameFixedKind(p, Parameter.POSITIONAL_ONLY) 178 else: 179 if p.get_kind() == Parameter.POSITIONAL_OR_KEYWORD: 180 used_names.add(p.string_name) 181 yield p 182 183 if not found_arg_signature and original_arg_name is not None: 184 yield original_arg_name 185 elif arg_names: 186 yield arg_names[0] 187 188 # Then process **kwargs 189 for func, arguments in kwarg_callables: 190 for signature in func.get_signatures(): 191 found_kwarg_signature = True 192 for p in process_params( 193 list(_remove_given_params( 194 arguments, 195 signature.get_param_names(resolve_stars=False) 196 )), star_count=2): 197 if p.get_kind() == Parameter.VAR_KEYWORD: 198 kwarg_names.append(p) 199 elif p.get_kind() == Parameter.KEYWORD_ONLY: 200 kw_only_names.append(p) 201 202 for p in kw_only_names: 203 if p.string_name in used_names: 204 continue 205 yield p 206 used_names.add(p.string_name) 207 208 if not found_kwarg_signature and original_kwarg_name is not None: 209 yield original_kwarg_name 210 elif kwarg_names: 211 yield kwarg_names[0] 212 213 214 class ParamNameFixedKind(ParamNameWrapper): 215 def __init__(self, param_name, new_kind): 216 super().__init__(param_name) 217 self._new_kind = new_kind 218 219 def get_kind(self): 220 return self._new_kind 221