diff --git a/jedi/inference/compiled/value.py b/jedi/inference/compiled/value.py index a8f4c5f4..b485e06f 100644 --- a/jedi/inference/compiled/value.py +++ b/jedi/inference/compiled/value.py @@ -311,6 +311,13 @@ class CompiledName(AbstractNameDefinition): return None return parent_qualified_names + (self.string_name,) + def get_defining_qualified_value(self): + context = self.parent_context + if context.is_module() or context.is_class(): + return self.parent_context.get_value() # Might be None + + return None + def __repr__(self): try: name = self.parent_context.name # __name__ is not defined all the time diff --git a/jedi/inference/context.py b/jedi/inference/context.py index bf43bd4b..8ecf4b77 100644 --- a/jedi/inference/context.py +++ b/jedi/inference/context.py @@ -133,6 +133,9 @@ class AbstractContext(object): def py__name__(self): raise NotImplementedError + def get_value(self): + raise NotImplementedError + @property def name(self): return None @@ -200,6 +203,9 @@ class ValueContext(AbstractContext): def py__doc__(self): return self._value.py__doc__() + def get_value(self): + return self._value + def __repr__(self): return '%s(%s)' % (self.__class__.__name__, self._value) @@ -226,6 +232,7 @@ class TreeContextMixin(object): self.inference_state, parent_context.parent_context, class_value) func = value.BoundMethod( instance=instance, + class_context=class_value.as_context(), function=func ) return func @@ -365,6 +372,9 @@ class CompForContext(TreeContextMixin, AbstractContext): def get_filters(self, until_position=None, origin_scope=None): yield ParserTreeFilter(self) + def get_value(self): + return None + def py__name__(self): return '' diff --git a/jedi/inference/names.py b/jedi/inference/names.py index 842a45eb..7e4da7db 100644 --- a/jedi/inference/names.py +++ b/jedi/inference/names.py @@ -135,9 +135,7 @@ class AbstractTreeName(AbstractNameDefinition): if self.is_import(): raise 1 elif self.parent_context: - values = self.parent_context.name.infer() - if len(values) == 1: - return next(iter(values)) + return self.parent_context.get_value() # Might be None return None def goto(self): @@ -240,6 +238,12 @@ class ValueNameMixin(object): return self._value.as_context() return super(ValueNameMixin, self).get_root_context() + def get_defining_qualified_value(self): + context = self.parent_context + if context.is_module() or context.is_class(): + return self.parent_context.get_value() # Might be None + return None + @property def api_type(self): return self._value.api_type diff --git a/jedi/inference/value/function.py b/jedi/inference/value/function.py index 18963f64..77ddaae2 100644 --- a/jedi/inference/value/function.py +++ b/jedi/inference/value/function.py @@ -11,7 +11,7 @@ from jedi.inference.signature import TreeSignature from jedi.inference.filters import ParserTreeFilter, FunctionExecutionFilter, \ AnonymousFunctionExecutionFilter from jedi.inference.names import ValueName, AbstractNameDefinition, \ - AnonymousParamName, ParamName + AnonymousParamName, ParamName, NameWrapper from jedi.inference.base_value import ContextualizedNode, NO_VALUES, \ ValueSet, TreeValue, ValueWrapper from jedi.inference.lazy_value import LazyKnownValues, LazyKnownValue, \ @@ -68,7 +68,7 @@ class FunctionMixin(object): if instance is None: # Calling the Foo.bar results in the original bar function. return ValueSet([self]) - return ValueSet([BoundMethod(instance, self)]) + return ValueSet([BoundMethod(instance, class_value.as_context(), self)]) def get_param_names(self): return [AnonymousParamName(self, param.name) @@ -141,6 +141,15 @@ class FunctionValue(use_metaclass(CachedMetaClass, FunctionMixin, FunctionAndCla return [self] +class FunctionNameInClass(NameWrapper): + def __init__(self, class_context, name): + super(FunctionNameInClass, self).__init__(name) + self._class_context = class_context + + def get_defining_qualified_value(self): + return self._class_context.get_value() # Might be None. + + class MethodValue(FunctionValue): def __init__(self, inference_state, class_context, *args, **kwargs): super(MethodValue, self).__init__(inference_state, *args, **kwargs) @@ -157,6 +166,10 @@ class MethodValue(FunctionValue): return None return names + (self.py__name__(),) + @property + def name(self): + return FunctionNameInClass(self.class_context, super(MethodValue, self).name) + class BaseFunctionExecutionContext(ValueContext, TreeContextMixin): def is_function_execution(self): diff --git a/jedi/inference/value/instance.py b/jedi/inference/value/instance.py index 8fbbebec..8ce6ee69 100644 --- a/jedi/inference/value/instance.py +++ b/jedi/inference/value/instance.py @@ -17,7 +17,7 @@ from jedi.inference.cache import inference_state_method_cache from jedi.inference.arguments import ValuesArguments, TreeArgumentsWrapper from jedi.inference.value.function import \ FunctionValue, FunctionMixin, OverloadedFunctionValue, \ - BaseFunctionExecutionContext, FunctionExecutionContext + BaseFunctionExecutionContext, FunctionExecutionContext, FunctionNameInClass from jedi.inference.value.klass import ClassFilter from jedi.inference.value.dynamic_arrays import get_dynamic_array_instance from jedi.parser_utils import function_is_staticmethod, function_is_classmethod @@ -211,7 +211,7 @@ class _BaseTreeInstance(AbstractInstanceValue): new = search_ancestor(new, 'funcdef', 'classdef') if class_context.tree_node is new: func = FunctionValue.from_context(class_context, func_node) - bound_method = BoundMethod(self, func) + bound_method = BoundMethod(self, class_context, func) if func_node.name.value == '__init__': context = bound_method.as_context(self._arguments) else: @@ -343,7 +343,7 @@ class TreeInstance(_BaseTreeInstance): # First check if the signature even matches, if not we don't # need to infer anything. continue - bound_method = BoundMethod(self, signature.value) + bound_method = BoundMethod(self, self.class_value.as_context(), signature.value) all_annotations = py__annotations__(signature.value.tree_node) type_var_dict = infer_type_vars_for_execution(bound_method, args, all_annotations) if type_var_dict: @@ -446,13 +446,21 @@ class CompiledInstanceClassFilter(AbstractFilter): class BoundMethod(FunctionMixin, ValueWrapper): - def __init__(self, instance, function): + def __init__(self, instance, class_context, function): super(BoundMethod, self).__init__(function) self.instance = instance + self._class_context = class_context def is_bound_method(self): return True + @property + def name(self): + return FunctionNameInClass( + self._class_context, + super(BoundMethod, self).name + ) + def py__class__(self): c, = values_from_qualified_names(self.inference_state, u'types', u'MethodType') return c @@ -477,7 +485,7 @@ class BoundMethod(FunctionMixin, ValueWrapper): def get_signature_functions(self): return [ - BoundMethod(self.instance, f) + BoundMethod(self.instance, self._class_context, f) for f in self._wrapped_value.get_signature_functions() ] diff --git a/jedi/inference/value/klass.py b/jedi/inference/value/klass.py index 24c4236e..5fe04a75 100644 --- a/jedi/inference/value/klass.py +++ b/jedi/inference/value/klass.py @@ -74,8 +74,7 @@ class ClassName(TreeNameDefinition): else: yield result_value - @property - def defining_qualified_value(self): + def get_defining_qualified_value(self): return self._class_value diff --git a/test/test_inference/test_gradual/test_typeshed.py b/test/test_inference/test_gradual/test_typeshed.py index adf7a114..dd0e24dc 100644 --- a/test/test_inference/test_gradual/test_typeshed.py +++ b/test/test_inference/test_gradual/test_typeshed.py @@ -192,6 +192,7 @@ def _assert_is_same(d1, d2): 'from collections import Counter; Counter', 'from collections import Counter; Counter()', 'from collections import Counter; Counter.most_common', + 'from collections import Counter; Counter().most_common', ]) def test_goto_stubs_on_itself(Script, code, type_): """