diff --git a/jedi/evaluate/signature.py b/jedi/evaluate/signature.py index b85d9e06..20aa4c65 100644 --- a/jedi/evaluate/signature.py +++ b/jedi/evaluate/signature.py @@ -30,7 +30,7 @@ class _SignatureMixin(object): s += ' -> ' + annotation return s - def get_param_names(self): + def get_param_names(self, resolve_stars=True): param_names = self._function_context.get_param_names() if self.is_bound: return param_names[1:] @@ -77,9 +77,12 @@ class TreeSignature(AbstractSignature): return '' return a.get_code(include_prefix=False) - def get_param_names(self): - from jedi.evaluate.star_args import process_params - return process_params(super(TreeSignature, self).get_param_names()) + def get_param_names(self, resolve_stars=True): + params = super(TreeSignature, self).get_param_names() + if resolve_stars: + from jedi.evaluate.star_args import process_params + params = process_params(params) + return params class BuiltinSignature(AbstractSignature): diff --git a/jedi/evaluate/star_args.py b/jedi/evaluate/star_args.py index 4f688885..05709d83 100644 --- a/jedi/evaluate/star_args.py +++ b/jedi/evaluate/star_args.py @@ -94,8 +94,8 @@ def _remove_given_params(arguments, param_names): def process_params(param_names, star_count=3): # default means both * and ** used_names = set() - arg_funcs = [] - kwarg_funcs = [] + arg_callables = [] + kwarg_callables = [] kw_only_names = [] kwarg_names = [] @@ -104,13 +104,13 @@ def process_params(param_names, star_count=3): # default means both * and ** kind = p.get_kind() if kind == Parameter.VAR_POSITIONAL: if star_count & 1: - arg_funcs = list(_iter_nodes_for_param(p)) - if not arg_funcs: + arg_callables = list(_iter_nodes_for_param(p)) + if not arg_callables: arg_names.append(p) elif p.get_kind() == Parameter.VAR_KEYWORD: if star_count & 2: - kwarg_funcs = list(_iter_nodes_for_param(p)) - if not kwarg_funcs: + kwarg_callables = list(_iter_nodes_for_param(p)) + if not kwarg_callables: kwarg_names.append(p) elif kind == Parameter.KEYWORD_ONLY: if star_count & 2: @@ -128,30 +128,31 @@ def process_params(param_names, star_count=3): # default means both * and ** yield p longest_param_names = () - for func_and_argument in arg_funcs: + for func_and_argument in arg_callables: func, arguments = func_and_argument new_star_count = star_count - if func_and_argument in kwarg_funcs: - kwarg_funcs.remove(func_and_argument) + if func_and_argument in kwarg_callables: + kwarg_callables.remove(func_and_argument) else: new_star_count = 1 - args_for_this_func = [] - for p in process_params( - list(_remove_given_params( - arguments, - func.get_param_names() - )), 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 signature in func.get_signatures(): + 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: @@ -170,14 +171,15 @@ def process_params(param_names, star_count=3): # default means both * and ** yield p used_names.add(p.string_name) - for func, arguments in kwarg_funcs: - for p in process_params( - list(_remove_given_params( - arguments, - func.get_param_names() - )), star_count=2): - if p.get_kind() != Parameter.KEYWORD_ONLY or not kwarg_names: - yield p + for func, arguments in kwarg_callables: + for signature in func.get_signatures(): + 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.KEYWORD_ONLY or not kwarg_names: + yield p if kwarg_names: yield kwarg_names[0] diff --git a/test/test_evaluate/test_signature.py b/test/test_evaluate/test_signature.py index eec334d2..101bb6d3 100644 --- a/test/test_evaluate/test_signature.py +++ b/test/test_evaluate/test_signature.py @@ -94,8 +94,21 @@ def test_tree_signature(Script, environment, code, expected): @pytest.mark.parametrize( 'combination, expected', [ + # Functions ('full_redirect(simple)', 'b, *, c'), + ('full_redirect(simple4)', 'b, x: int'), + ('full_redirect(a)', 'b, *args'), + ('full_redirect(kw)', 'b, *, c, **kwargs'), + ('full_redirect(akw)', 'c, *args, **kwargs'), + # Non functions + ('full_redirect(lambda x, y: ...)', 'y'), + ('full_redirect(C)', 'z, *c'), + ('full_redirect(C())', 'y'), + ('full_redirect()', '*args, **kwargs'), + ('full_redirect(1)', '*args, **kwargs'), + + # Merging ('two_redirects(simple, simple)', 'a, b, *, c'), ('two_redirects(simple2, simple2)', 'x'), ('two_redirects(akw, kw)', 'a, c, *args, **kwargs'), @@ -145,6 +158,10 @@ def test_nested_signatures(Script, environment, combination, expected): return lambda *args, **kwargs: func1(*args) + func2(**kwargs) def combined_lot_of_args(func1, func2): return lambda *args, **kwargs: func1(1, 2, 3, 4, *args) + func2(a=3, x=1, y=1, **kwargs) + + class C: + def __init__(self, a, z, *c): ... + def __call__(self, x, y): ... ''') code += 'z = ' + combination + '\nz(' sig, = Script(code).call_signatures()