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.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()

View File

@@ -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

View File

@@ -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=)