diff --git a/jedi/evaluate/context/function.py b/jedi/evaluate/context/function.py index 39ab62ca..bee7899b 100644 --- a/jedi/evaluate/context/function.py +++ b/jedi/evaluate/context/function.py @@ -53,6 +53,13 @@ class FunctionMixin(object): for filter in scope.get_filters(search_global=False, origin_scope=origin_scope): yield filter + def py__get__(self, instance, class_context): + from jedi.evaluate.context.instance import BoundMethod + if instance is None: + # Calling the Foo.bar results in the original bar function. + return ContextSet([self]) + return ContextSet([BoundMethod(instance, class_context, self)]) + def get_param_names(self): function_execution = self.get_function_execution() return [ParamName(function_execution, param.name) @@ -335,7 +342,7 @@ class FunctionExecutionContext(TreeContext): return self.get_return_values() -class OverloadedFunctionContext(ContextWrapper): +class OverloadedFunctionContext(FunctionMixin, ContextWrapper): def __init__(self, function, overloaded_functions): super(OverloadedFunctionContext, self).__init__(function) self.overloaded_functions = overloaded_functions diff --git a/jedi/evaluate/context/instance.py b/jedi/evaluate/context/instance.py index e9e90c55..f75b7025 100644 --- a/jedi/evaluate/context/instance.py +++ b/jedi/evaluate/context/instance.py @@ -8,7 +8,7 @@ from jedi.evaluate.base_context import Context, NO_CONTEXTS, ContextSet, \ iterator_to_context_set, ContextWrapper from jedi.evaluate.lazy_context import LazyKnownContext, LazyKnownContexts from jedi.evaluate.cache import evaluator_method_cache -from jedi.evaluate.arguments import AbstractArguments, AnonymousArguments, \ +from jedi.evaluate.arguments import AnonymousArguments, \ ValuesArguments, TreeArgumentsWrapper from jedi.evaluate.context.function import FunctionExecutionContext, \ FunctionContext, FunctionMixin, OverloadedFunctionContext, MethodContext @@ -109,16 +109,17 @@ class AbstractInstanceContext(Context): for name in names ) - def py__get__(self, obj): + def py__get__(self, obj, class_context): + """ + obj may be None. + """ # Arguments in __get__ descriptors are obj, class. # `method` is the new parent of the array, don't know if that's good. names = self.get_function_slot_names(u'__get__') if names: - if obj.is_instance(): - return self.execute_function_slots(names, obj, obj.class_context) - else: - none_obj = compiled.builtin_from_name(self.evaluator, u'None') - return self.execute_function_slots(names, none_obj, obj) + if obj is None: + obj = compiled.builtin_from_name(self.evaluator, u'None') + return self.execute_function_slots(names, obj, class_context) else: return ContextSet([self]) @@ -273,8 +274,10 @@ class TreeInstance(AbstractInstanceContext): # Just take the first result, it should always be one, because we # control the typeshed code. c = self.class_context - if isinstance(func, MethodContext): + try: c = func.class_context + except AttributeError: + pass bound = BoundMethod(self, c, func) execution = bound.get_function_execution(self.var_args) if not execution.matches_signature(): @@ -442,14 +445,8 @@ class LazyInstanceClassName(object): @iterator_to_context_set def infer(self): for result_context in self._class_member_name.infer(): - if isinstance(result_context, (FunctionContext, OverloadedFunctionContext)): - # Classes are never used to resolve anything within the - # functions. Only other functions and modules will resolve - # those things. - yield BoundMethod(self._instance, self.class_context, result_context) - else: - for c in apply_py__get__(result_context, self._instance): - yield c + for c in apply_py__get__(result_context, self._instance, self.class_context): + yield c def __getattr__(self, name): return getattr(self._class_member_name, name) diff --git a/jedi/evaluate/context/klass.py b/jedi/evaluate/context/klass.py index a37da4a5..0475d67e 100644 --- a/jedi/evaluate/context/klass.py +++ b/jedi/evaluate/context/klass.py @@ -50,13 +50,13 @@ from jedi.evaluate.base_context import ContextSet, iterator_to_context_set, \ TreeContext, NO_CONTEXTS -def apply_py__get__(context, base_context): +def apply_py__get__(context, instance, class_context): try: method = context.py__get__ except AttributeError: yield context else: - for descriptor_context in method(base_context): + for descriptor_context in method(instance, class_context): yield descriptor_context @@ -122,7 +122,9 @@ class ClassName(TreeNameDefinition): for result_context in inferred: if self._apply_decorators: - for c in apply_py__get__(result_context, self.parent_context): + for c in apply_py__get__(result_context, + instance=None, + class_context=self.parent_context): yield c else: yield result_context diff --git a/jedi/evaluate/imports.py b/jedi/evaluate/imports.py index bf8e717f..cb778e12 100644 --- a/jedi/evaluate/imports.py +++ b/jedi/evaluate/imports.py @@ -404,7 +404,7 @@ def import_module(evaluator, import_names, parent_module_context, sys_path): import_names=import_names, sys_path=sys_path, ) - return ContextSet(module) + return ContextSet([module]) module_name = '.'.join(import_names) if parent_module_context is None: diff --git a/jedi/plugins/stdlib.py b/jedi/plugins/stdlib.py index 8d1a5a05..c49b687a 100644 --- a/jedi/plugins/stdlib.py +++ b/jedi/plugins/stdlib.py @@ -284,8 +284,7 @@ class StaticMethodObject(AbstractObjectOverwrite, ContextWrapper): def get_object(self): return self._wrapped_context - @publish_method('__get__') - def _py__get__(self): + def py__get__(self, instance, klass): return ContextSet([self._wrapped_context]) @@ -302,12 +301,11 @@ class ClassMethodObject(AbstractObjectOverwrite, ContextWrapper): def get_object(self): return self._wrapped_context - def py__get__(self, obj): - actual, = self._wrapped_context.py__getattribute__('__get__') - klass = obj - if not obj.is_class(): - klass = obj.py__class__() - return ContextSet([ClassMethodGet(actual, klass, self._function)]) + def py__get__(self, obj, class_context): + return ContextSet([ + ClassMethodGet(__get__, class_context, self._function) + for __get__ in self._wrapped_context.py__getattribute__('__get__') + ]) class ClassMethodGet(AbstractObjectOverwrite, ContextWrapper): diff --git a/test/test_plugin/test_stub.py b/test/test_plugin/test_stub.py index 5eeed3ef..a21f6064 100644 --- a/test/test_plugin/test_stub.py +++ b/test/test_plugin/test_stub.py @@ -1,8 +1,7 @@ import os from jedi.plugins import typeshed -from jedi.evaluate.context import TreeInstance, BoundMethod, \ - ClassContext +from jedi.evaluate.context import TreeInstance, BoundMethod from parso.utils import PythonVersionInfo from jedi.evaluate.filters import TreeNameDefinition @@ -48,7 +47,7 @@ def test_function(Script): def_, = Script(code + '()').goto_definitions() context = def_._name._context assert isinstance(context, TreeInstance) - assert isinstance(context.class_context, ClassContext), context + assert isinstance(context.class_context, typeshed.StubOnlyClass), context def_, = Script('import threading; threading.Thread').goto_definitions() assert isinstance(def_._name._context, typeshed.StubClassContext), def_ @@ -86,7 +85,7 @@ def test_method(Script): def_, = Script(code).goto_definitions() context = def_._name._context assert isinstance(context, BoundMethod), context - assert isinstance(context._function, typeshed.StubFunctionContext), context + assert isinstance(context._wrapped_context, typeshed.StubFunctionContext), context def_, = Script(code + '()').goto_definitions() context = def_._name._context diff --git a/test/test_settings.py b/test/test_settings.py index 2990061d..1a313b9d 100644 --- a/test/test_settings.py +++ b/test/test_settings.py @@ -3,8 +3,7 @@ import pytest from jedi import settings from jedi.evaluate.filters import ContextName from jedi.evaluate.compiled import CompiledContextName -from jedi.plugins.typeshed import StubContextWithCompiled -from jedi.evaluate.context import FunctionContext +from jedi.plugins.typeshed import StubOnlyModuleContext @pytest.fixture() @@ -16,8 +15,7 @@ def test_base_auto_import_modules(auto_import_json, Script): loads, = Script('import json; json.loads').goto_definitions() assert isinstance(loads._name, ContextName) context, = loads._name.infer() - assert isinstance(context, StubContextWithCompiled) - assert isinstance(context._wrapped_context, FunctionContext) + assert isinstance(context.parent_context, StubOnlyModuleContext) def test_auto_import_modules_imports(auto_import_json, Script):