diff --git a/jedi/api/__init__.py b/jedi/api/__init__.py index f33cfb20..5677d0b4 100644 --- a/jedi/api/__init__.py +++ b/jedi/api/__init__.py @@ -345,12 +345,12 @@ class Script(object): ) debug.speed('func_call followed') - return [classes.CallSignature(self._evaluator, signature, + return [classes.CallSignature(definition, signature, call_signature_details.bracket_leaf.start_pos, call_signature_details.call_index, call_signature_details.keyword_name_str) - for d in definitions - for signature in d.get_signatures()] + for definition in definitions + for signature in definition.get_signatures()] def _analysis(self): self._evaluator.is_analysis = True diff --git a/jedi/api/classes.py b/jedi/api/classes.py index 2bc0f60d..39f94c2e 100644 --- a/jedi/api/classes.py +++ b/jedi/api/classes.py @@ -320,12 +320,13 @@ 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): + def get_param_names(signature): param_names = [] if context.api_type == 'function': param_names = list(context.get_param_names()) if isinstance(context, instance.BoundMethod): param_names = param_names[1:] + return param_names elif context.is_class() or context.is_instance(): if context.is_class(): search = u'__init__' @@ -346,12 +347,13 @@ class BaseDefinition(object): return list(context.get_param_names()) return param_names - followed = list(self._name.infer()) - if not followed or not hasattr(followed[0], 'py__call__'): - raise AttributeError('There are no params defined on this.') - context = followed[0] # only check the first one. + # Only return the first one. There might be multiple one, especially + # with overloading. + for context in self._name.infer(): + for signature in context.get_signatures(): + return [Definition(self._evaluator, n) for n in signature.get_param_names()] - return [Definition(self._evaluator, n) for n in get_param_names(context)] + raise AttributeError('There are no params defined on this.') def parent(self): context = self._name.parent_context @@ -600,8 +602,8 @@ class CallSignature(Definition): It knows what functions you are currently in. e.g. `isinstance(` would return the `isinstance` function. without `(` it would return nothing. """ - def __init__(self, evaluator, signature, bracket_start_pos, index, key_name_str): - super(CallSignature, self).__init__(evaluator, signature.name) + def __init__(self, definition, signature, bracket_start_pos, index, key_name_str): + super(CallSignature, self).__init__(definition.evaluator, definition.name) self._index = index self._key_name_str = key_name_str self._bracket_start_pos = bracket_start_pos @@ -634,6 +636,10 @@ class CallSignature(Definition): return None return self._index + @property + def params(self): + return [Definition(self._evaluator, n) for n in self._signature.get_param_names()] + @property def bracket_start(self): """ diff --git a/jedi/evaluate/__init__.py b/jedi/evaluate/__init__.py index f85126d3..43f66400 100644 --- a/jedi/evaluate/__init__.py +++ b/jedi/evaluate/__init__.py @@ -323,12 +323,8 @@ class Evaluator(object): context_set = eval_trailer(context, context_set, trailer) param_names = [] for context in context_set: - try: - get_param_names = context.get_param_names - except AttributeError: - pass - else: - for param_name in get_param_names(): + for signature in context.get_signatures(): + for param_name in signature.get_param_names(): if param_name.string_name == name.value: param_names.append(param_name) return param_names diff --git a/jedi/evaluate/base_context.py b/jedi/evaluate/base_context.py index 277c5692..f13fadee 100644 --- a/jedi/evaluate/base_context.py +++ b/jedi/evaluate/base_context.py @@ -356,6 +356,9 @@ class ContextSet(BaseContextSet): context_set |= method() return context_set + def get_signatures(self): + return [sig for c in self._set for sig in c.get_signatures()] + NO_CONTEXTS = ContextSet([]) diff --git a/jedi/evaluate/context/instance.py b/jedi/evaluate/context/instance.py index 2869e1e7..5254e2e6 100644 --- a/jedi/evaluate/context/instance.py +++ b/jedi/evaluate/context/instance.py @@ -218,6 +218,10 @@ class AbstractInstanceContext(Context): raise NotImplementedError return class_context + def get_signatures(self): + init_funcs = self.py__getattribute__('__call__') + return [sig.bind() for sig in init_funcs.get_signatures()] + def __repr__(self): return "<%s of %s(%s)>" % (self.__class__.__name__, self.class_context, self.var_args) @@ -296,12 +300,14 @@ class TreeInstance(AbstractInstanceContext): return self._get_annotated_class_object() or self.class_context def _get_annotation_init_functions(self): - for init in self.class_context.py__getattribute__('__init__'): - if isinstance(init, OverloadedFunctionContext): - for func in init.overloaded_functions: - yield func - elif isinstance(init, FunctionContext): - yield init + filter = next(self.class_context.get_filters()) + for init_name in filter.get('__init__'): + for init in init_name.infer(): + if isinstance(init, OverloadedFunctionContext): + for func in init.overloaded_functions: + yield func + elif isinstance(init, FunctionContext): + yield init class AnonymousInstance(TreeInstance): diff --git a/jedi/evaluate/context/klass.py b/jedi/evaluate/context/klass.py index 13fdca33..3a95b2ff 100644 --- a/jedi/evaluate/context/klass.py +++ b/jedi/evaluate/context/klass.py @@ -292,6 +292,4 @@ class ClassContext(use_metaclass(CachedMetaClass, ClassMixin, TreeContext)): def get_signatures(self): init_funcs = self.py__getattribute__('__init__') - return [ - s for f in init_funcs for s in f.get_signatures() - ] + return [sig.bind() for sig in init_funcs.get_signatures()] diff --git a/test/test_api/test_call_signatures.py b/test/test_api/test_call_signatures.py index 18668be2..f3f4fdff 100644 --- a/test/test_api/test_call_signatures.py +++ b/test/test_api/test_call_signatures.py @@ -424,20 +424,28 @@ def test_lambda_params(Script): assert [p.name for p in sig.params] == ['x'] +CLASS_CODE = dedent('''\ +class X(): + def __init__(self, foo, bar): + self.foo = foo +''') + + def test_class_creation(Script): - code = dedent('''\ - class X(): - def __init__(self, foo, bar): - self.foo = foo - ''') - sig, = Script(code + 'X(').call_signatures() + + sig, = Script(CLASS_CODE + 'X(').call_signatures() assert sig.index == 0 assert sig.name == 'X' assert [p.name for p in sig.params] == ['foo', 'bar'] - sig, = Script(code + 'X.__init__(').call_signatures() + +def test_call_init_on_class(Script): + sig, = Script(CLASS_CODE + 'X.__init__(').call_signatures() assert [p.name for p in sig.params] == ['self', 'foo', 'bar'] - sig, = Script(code + 'X().__init__(').call_signatures() + + +def test_call_init_on_instance(Script): + sig, = Script(CLASS_CODE + 'X().__init__(').call_signatures() assert [p.name for p in sig.params] == ['foo', 'bar']