| """ |
| This module is responsible for inferring *args and **kwargs for signatures. |
| |
| This means for example in this case:: |
| |
| def foo(a, b, c): ... |
| |
| def bar(*args): |
| return foo(1, *args) |
| |
| The signature here for bar should be `bar(b, c)` instead of bar(*args). |
| """ |
| from inspect import Parameter |
|
|
| from parso import tree |
|
|
| from jedi.inference.utils import to_list |
| from jedi.inference.names import ParamNameWrapper |
| from jedi.inference.helpers import is_big_annoying_library |
|
|
|
|
| def _iter_nodes_for_param(param_name): |
| from parso.python.tree import search_ancestor |
| from jedi.inference.arguments import TreeArguments |
|
|
| execution_context = param_name.parent_context |
| |
| |
| |
| |
| function_node = tree.search_ancestor(param_name.tree_name, 'funcdef', 'lambdef') |
| module_node = function_node.get_root_node() |
| start = function_node.children[-1].start_pos |
| end = function_node.children[-1].end_pos |
| for name in module_node.get_used_names().get(param_name.string_name): |
| if start <= name.start_pos < end: |
| |
| argument = name.parent |
| if argument.type == 'argument' \ |
| and argument.children[0] == '*' * param_name.star_count: |
| trailer = search_ancestor(argument, 'trailer') |
| if trailer is not None: |
| context = execution_context.create_context(trailer) |
| if _goes_to_param_name(param_name, context, name): |
| values = _to_callables(context, trailer) |
|
|
| args = TreeArguments.create_cached( |
| execution_context.inference_state, |
| context=context, |
| argument_node=trailer.children[1], |
| trailer=trailer, |
| ) |
| for c in values: |
| yield c, args |
|
|
|
|
| def _goes_to_param_name(param_name, context, potential_name): |
| if potential_name.type != 'name': |
| return False |
| from jedi.inference.names import TreeNameDefinition |
| found = TreeNameDefinition(context, potential_name).goto() |
| return any(param_name.parent_context == p.parent_context |
| and param_name.start_pos == p.start_pos |
| for p in found) |
|
|
|
|
| def _to_callables(context, trailer): |
| from jedi.inference.syntax_tree import infer_trailer |
|
|
| atom_expr = trailer.parent |
| index = atom_expr.children[0] == 'await' |
| |
| values = context.infer_node(atom_expr.children[index]) |
| for trailer2 in atom_expr.children[index + 1:]: |
| if trailer == trailer2: |
| break |
| values = infer_trailer(context, values, trailer2) |
| return values |
|
|
|
|
| def _remove_given_params(arguments, param_names): |
| count = 0 |
| used_keys = set() |
| for key, _ in arguments.unpack(): |
| if key is None: |
| count += 1 |
| else: |
| used_keys.add(key) |
|
|
| for p in param_names: |
| if count and p.maybe_positional_argument(): |
| count -= 1 |
| continue |
| if p.string_name in used_keys and p.maybe_keyword_argument(): |
| continue |
| yield p |
|
|
|
|
| @to_list |
| def process_params(param_names, star_count=3): |
| if param_names: |
| if is_big_annoying_library(param_names[0].parent_context): |
| |
| |
| yield from param_names |
| return |
|
|
| used_names = set() |
| arg_callables = [] |
| kwarg_callables = [] |
|
|
| kw_only_names = [] |
| kwarg_names = [] |
| arg_names = [] |
| original_arg_name = None |
| original_kwarg_name = None |
| for p in param_names: |
| kind = p.get_kind() |
| if kind == Parameter.VAR_POSITIONAL: |
| if star_count & 1: |
| arg_callables = _iter_nodes_for_param(p) |
| original_arg_name = p |
| elif p.get_kind() == Parameter.VAR_KEYWORD: |
| if star_count & 2: |
| kwarg_callables = list(_iter_nodes_for_param(p)) |
| original_kwarg_name = p |
| elif kind == Parameter.KEYWORD_ONLY: |
| if star_count & 2: |
| kw_only_names.append(p) |
| elif kind == Parameter.POSITIONAL_ONLY: |
| if star_count & 1: |
| yield p |
| else: |
| if star_count == 1: |
| yield ParamNameFixedKind(p, Parameter.POSITIONAL_ONLY) |
| elif star_count == 2: |
| kw_only_names.append(ParamNameFixedKind(p, Parameter.KEYWORD_ONLY)) |
| else: |
| used_names.add(p.string_name) |
| yield p |
|
|
| |
| longest_param_names = () |
| found_arg_signature = False |
| found_kwarg_signature = False |
| for func_and_argument in arg_callables: |
| func, arguments = func_and_argument |
| new_star_count = star_count |
| if func_and_argument in kwarg_callables: |
| kwarg_callables.remove(func_and_argument) |
| else: |
| new_star_count = 1 |
|
|
| for signature in func.get_signatures(): |
| found_arg_signature = True |
| if new_star_count == 3: |
| found_kwarg_signature = True |
| args_for_this_func = [] |
| for p in process_params( |
| list(_remove_given_params( |
| arguments, |
| signature.get_param_names(resolve_stars=False) |
| )), new_star_count): |
| if p.get_kind() == Parameter.VAR_KEYWORD: |
| kwarg_names.append(p) |
| elif p.get_kind() == Parameter.VAR_POSITIONAL: |
| arg_names.append(p) |
| elif p.get_kind() == Parameter.KEYWORD_ONLY: |
| kw_only_names.append(p) |
| else: |
| args_for_this_func.append(p) |
| if len(args_for_this_func) > len(longest_param_names): |
| longest_param_names = args_for_this_func |
|
|
| for p in longest_param_names: |
| if star_count == 1 and p.get_kind() != Parameter.VAR_POSITIONAL: |
| yield ParamNameFixedKind(p, Parameter.POSITIONAL_ONLY) |
| else: |
| if p.get_kind() == Parameter.POSITIONAL_OR_KEYWORD: |
| used_names.add(p.string_name) |
| yield p |
|
|
| if not found_arg_signature and original_arg_name is not None: |
| yield original_arg_name |
| elif arg_names: |
| yield arg_names[0] |
|
|
| |
| for func, arguments in kwarg_callables: |
| for signature in func.get_signatures(): |
| found_kwarg_signature = True |
| for p in process_params( |
| list(_remove_given_params( |
| arguments, |
| signature.get_param_names(resolve_stars=False) |
| )), star_count=2): |
| if p.get_kind() == Parameter.VAR_KEYWORD: |
| kwarg_names.append(p) |
| elif p.get_kind() == Parameter.KEYWORD_ONLY: |
| kw_only_names.append(p) |
|
|
| for p in kw_only_names: |
| if p.string_name in used_names: |
| continue |
| yield p |
| used_names.add(p.string_name) |
|
|
| if not found_kwarg_signature and original_kwarg_name is not None: |
| yield original_kwarg_name |
| elif kwarg_names: |
| yield kwarg_names[0] |
|
|
|
|
| class ParamNameFixedKind(ParamNameWrapper): |
| def __init__(self, param_name, new_kind): |
| super().__init__(param_name) |
| self._new_kind = new_kind |
|
|
| def get_kind(self): |
| return self._new_kind |
|
|