Fix named param completion and add a few tests.

This commit is contained in:
Dave Halter
2016-11-27 11:27:30 +01:00
parent 0b5f8ccc21
commit 4ad0179b72
3 changed files with 44 additions and 28 deletions

View File

@@ -14,6 +14,7 @@ from jedi.parser import tree
from jedi.parser.utils import load_parser from jedi.parser.utils import load_parser
from jedi.cache import memoize_method from jedi.cache import memoize_method
from jedi.evaluate import representation as er from jedi.evaluate import representation as er
from jedi.evaluate import instance
from jedi.evaluate import iterable from jedi.evaluate import iterable
from jedi.evaluate import imports from jedi.evaluate import imports
from jedi.evaluate import compiled from jedi.evaluate import compiled
@@ -305,14 +306,14 @@ class BaseDefinition(object):
""" """
Follow both statements and imports, as far as possible. 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) return self._evaluator.eval_statement(self._definition)
elif self._definition.isinstance(tree.Import): elif self._name.api_type == 'import':
raise DeprecationWarning raise DeprecationWarning
# TODO self._name.infer()? # TODO self._name.infer()?
return imports.ImportWrapper(self._evaluator, self._name).follow() return imports.ImportWrapper(self._evaluator, self._name).follow()
else: else:
return set([self._definition]) return set([self._name.parent_context])
@property @property
@memoize_method @memoize_method
@@ -321,33 +322,33 @@ class BaseDefinition(object):
Raises an ``AttributeError``if the definition is not callable. Raises an ``AttributeError``if the definition is not callable.
Otherwise returns a list of `Definition` that represents the params. 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()) followed = list(self._follow_statements_imports())
if not followed or not hasattr(followed[0], 'py__call__'): if not followed or not hasattr(followed[0], 'py__call__'):
raise AttributeError() raise AttributeError()
followed = followed[0] # only check the first one. context = followed[0] # only check the first one.
if followed.type in ('funcdef', 'lambda'): return [_Param(self._evaluator, n) for n in get_param_names(context)]
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]
def parent(self): def parent(self):
scope = self._definition.get_parent_scope() scope = self._definition.get_parent_scope()

View File

@@ -22,7 +22,8 @@ def get_call_signature_param_names(call_signatures):
# Allow access on _definition here, because it's a # Allow access on _definition here, because it's a
# public API and we don't want to make the internal # public API and we don't want to make the internal
# Name object public. # 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 yield p._name

View File

@@ -34,7 +34,7 @@ my_lambda = lambda lambda_param: lambda_param + 1
#? 22 ['lambda_param'] #? 22 ['lambda_param']
my_lambda(lambda_param) my_lambda(lambda_param)
# __call__ # __call__ / __init__
class Test(object): class Test(object):
def __init__(self, hello_other): def __init__(self, hello_other):
pass pass
@@ -42,7 +42,21 @@ class Test(object):
def __call__(self, hello): def __call__(self, hello):
pass pass
def test(self, blub):
pass
#? 10 ['hello_other']
Test(hello=)
#? 12 ['hello'] #? 12 ['hello']
Test()(hello=) Test()(hello=)
#? 11 [] #? 11 []
Test()(self=) Test()(self=)
#? 16 []
Test().test(self=)
#? 16 ['blub']
Test().test(blub=)
# builtins
#? 12 []
any(iterable=)