diff --git a/jedi/api/classes.py b/jedi/api/classes.py index f0c5078d..88e38462 100644 --- a/jedi/api/classes.py +++ b/jedi/api/classes.py @@ -14,6 +14,7 @@ from jedi.parser import tree from jedi.parser.utils import load_parser from jedi.cache import memoize_method from jedi.evaluate import representation as er +from jedi.evaluate import instance from jedi.evaluate import iterable from jedi.evaluate import imports from jedi.evaluate import compiled @@ -305,14 +306,14 @@ class BaseDefinition(object): """ Follow both statements and imports, as far as possible. """ - if self._definition.isinstance(tree.ExprStmt): + if self._name.api_type == 'expr_stmt': return self._evaluator.eval_statement(self._definition) - elif self._definition.isinstance(tree.Import): + elif self._name.api_type == 'import': raise DeprecationWarning # TODO self._name.infer()? return imports.ImportWrapper(self._evaluator, self._name).follow() else: - return set([self._definition]) + return set([self._name.parent_context]) @property @memoize_method @@ -321,33 +322,33 @@ class BaseDefinition(object): Raises an ``AttributeError``if the definition is not callable. Otherwise returns a list of `Definition` that represents the params. """ + def get_param_names(context): + param_names = [] + if context.api_type == 'function': + param_names = context.get_param_names() + if isinstance(context, instance.BoundMethod): + param_names = param_names[1:] + elif isinstance(context, (instance.AbstractInstanceContext, er.ClassContext)): + if isinstance(context, er.ClassContext): + search = '__init__' + else: + search = '__call__' + names = context.get_function_slot_names(search) + if not names: + return [] + + # Just take the first one here, not optimal, but currently + # there's no better solution. + inferred = names[0].infer() + return get_param_names(next(iter(inferred))) + return param_names + followed = list(self._follow_statements_imports()) if not followed or not hasattr(followed[0], 'py__call__'): raise AttributeError() - followed = followed[0] # only check the first one. + context = followed[0] # only check the first one. - if followed.type in ('funcdef', 'lambda'): - if isinstance(followed, er.InstanceElement): - params = followed.params[1:] - else: - params = followed.params - elif followed.isinstance(er.compiled.CompiledObject): - params = followed.params - elif isinstance(followed, er.Class): - try: - sub = followed.get_subscope_by_name('__init__') - params = sub.params[1:] # ignore self - except KeyError: - return [] - elif isinstance(followed, er.Instance): - try: - sub = followed.get_subscope_by_name('__call__') - params = sub.params[1:] # ignore self - except KeyError: - return [] - else: - return [] - return [_Param(self._evaluator, p.name) for p in params] + return [_Param(self._evaluator, n) for n in get_param_names(context)] def parent(self): scope = self._definition.get_parent_scope() diff --git a/jedi/api/completion.py b/jedi/api/completion.py index b1f487cd..fe80d5ba 100644 --- a/jedi/api/completion.py +++ b/jedi/api/completion.py @@ -22,7 +22,8 @@ def get_call_signature_param_names(call_signatures): # Allow access on _definition here, because it's a # public API and we don't want to make the internal # Name object public. - if p._definition.stars == 0: # no *args/**kwargs + tree_param = tree.search_ancestor(p._name.tree_name, 'param') + if tree_param.stars == 0: # no *args/**kwargs yield p._name diff --git a/test/completion/named_param.py b/test/completion/named_param.py index b420df16..66ba59e9 100644 --- a/test/completion/named_param.py +++ b/test/completion/named_param.py @@ -34,7 +34,7 @@ my_lambda = lambda lambda_param: lambda_param + 1 #? 22 ['lambda_param'] my_lambda(lambda_param) -# __call__ +# __call__ / __init__ class Test(object): def __init__(self, hello_other): pass @@ -42,7 +42,21 @@ class Test(object): def __call__(self, hello): pass + def test(self, blub): + pass + +#? 10 ['hello_other'] +Test(hello=) #? 12 ['hello'] Test()(hello=) #? 11 [] Test()(self=) +#? 16 [] +Test().test(self=) +#? 16 ['blub'] +Test().test(blub=) + +# builtins + +#? 12 [] +any(iterable=)