From f25310e0b90ddd1cbb6bbbc98c00925af4795188 Mon Sep 17 00:00:00 2001 From: Dave Halter Date: Fri, 3 Aug 2018 00:15:11 +0200 Subject: [PATCH] BoundMethods now have access to the function that they are using --- jedi/evaluate/__init__.py | 14 +++---- jedi/evaluate/compiled/access.py | 2 +- jedi/evaluate/context/function.py | 67 ++++++++++++++++--------------- jedi/evaluate/context/instance.py | 54 +++++++++++++------------ jedi/evaluate/lazy_context.py | 1 + 5 files changed, 72 insertions(+), 66 deletions(-) diff --git a/jedi/evaluate/__init__.py b/jedi/evaluate/__init__.py index 084ec987..d316785f 100644 --- a/jedi/evaluate/__init__.py +++ b/jedi/evaluate/__init__.py @@ -334,15 +334,15 @@ class Evaluator(object): parent_context = from_scope_node(parent_scope, child_is_funcdef=is_funcdef) if is_funcdef: + func = FunctionContext.from_context( + parent_context, + scope_node + ) if isinstance(parent_context, AnonymousInstance): func = BoundMethod( - self, parent_context, parent_context.class_context, - parent_context.parent_context, scope_node - ) - else: - func = FunctionContext.from_context( - parent_context, - scope_node + instance=parent_context, + klass=parent_context.class_context, + function=func ) if is_nested and not node_is_object: return func.get_function_execution() diff --git a/jedi/evaluate/compiled/access.py b/jedi/evaluate/compiled/access.py index 0ef7e44d..610cec92 100644 --- a/jedi/evaluate/compiled/access.py +++ b/jedi/evaluate/compiled/access.py @@ -470,7 +470,7 @@ else: class _SPECIAL_OBJECTS(object): FUNCTION_CLASS = types.FunctionType - METHOD_CLASS = type(DirectObjectAccess.py__bool__) + BOUND_METHOD_CLASS = type(DirectObjectAccess(None, None).py__bool__) MODULE_CLASS = types.ModuleType GENERATOR_OBJECT = _a_generator(1.0) BUILTINS = builtins diff --git a/jedi/evaluate/context/function.py b/jedi/evaluate/context/function.py index 70b39859..992cecdf 100644 --- a/jedi/evaluate/context/function.py +++ b/jedi/evaluate/context/function.py @@ -38,19 +38,9 @@ class LambdaName(AbstractNameDefinition): return ContextSet(self._lambda_context) -class FunctionContext(use_metaclass(CachedMetaClass, TreeContext)): - """ - Needed because of decorators. Decorators are evaluated here. - """ +class AbstractFunction(TreeContext): api_type = u'function' - @classmethod - def from_context(cls, context, tree_node): - while context.is_class(): - context = context.parent_context - - return cls(context.evaluator, parent_context=context, tree_node=tree_node) - def get_filters(self, search_global, until_position=None, origin_scope=None): if search_global: yield ParserTreeFilter( @@ -64,6 +54,24 @@ class FunctionContext(use_metaclass(CachedMetaClass, TreeContext)): for filter in scope.get_filters(search_global=False, origin_scope=origin_scope): yield filter + def get_param_names(self): + function_execution = self.get_function_execution() + return [ParamName(function_execution, param.name) + for param in self.tree_node.get_params()] + + @property + def name(self): + if self.tree_node.type == 'lambdef': + return LambdaName(self) + return ContextName(self, self.tree_node.name) + + def get_function_execution(self, arguments=None): + raise NotImplementedError + + def py__call__(self, arguments): + function_execution = self.get_function_execution(arguments) + return self.infer_function_execution(function_execution) + def infer_function_execution(self, function_execution): """ Created to be used by inheritance. @@ -86,35 +94,28 @@ class FunctionContext(use_metaclass(CachedMetaClass, TreeContext)): else: return function_execution.get_return_values() + +class FunctionContext(use_metaclass(CachedMetaClass, AbstractFunction)): + """ + Needed because of decorators. Decorators are evaluated here. + """ + @classmethod + def from_context(cls, context, tree_node): + from jedi.evaluate.context import AbstractInstanceContext + + while context.is_class() or isinstance(context, AbstractInstanceContext): + context = context.parent_context + + return cls(context.evaluator, parent_context=context, tree_node=tree_node) + def get_function_execution(self, arguments=None): if arguments is None: arguments = AnonymousArguments() return FunctionExecutionContext(self.evaluator, self.parent_context, self, arguments) - def py__call__(self, arguments): - function_execution = self.get_function_execution(arguments) - return self.infer_function_execution(function_execution) - def py__class__(self): - # This differentiation is only necessary for Python2. Python3 does not - # use a different method class. - if isinstance(parser_utils.get_parent_scope(self.tree_node), tree.Class): - name = u'METHOD_CLASS' - else: - name = u'FUNCTION_CLASS' - return compiled.get_special_object(self.evaluator, name) - - @property - def name(self): - if self.tree_node.type == 'lambdef': - return LambdaName(self) - return ContextName(self, self.tree_node.name) - - def get_param_names(self): - function_execution = self.get_function_execution() - return [ParamName(function_execution, param.name) - for param in self.tree_node.get_params()] + return compiled.get_special_object(self.evaluator, u'FUNCTION_CLASS') class FunctionExecutionContext(TreeContext): diff --git a/jedi/evaluate/context/instance.py b/jedi/evaluate/context/instance.py index 73d022a3..41981962 100644 --- a/jedi/evaluate/context/instance.py +++ b/jedi/evaluate/context/instance.py @@ -9,7 +9,8 @@ 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.cache import memoize_method -from jedi.evaluate.context.function import FunctionExecutionContext, FunctionContext +from jedi.evaluate.context.function import FunctionExecutionContext, \ + FunctionContext, AbstractFunction from jedi.evaluate.context.klass import ClassContext, apply_py__get__ from jedi.evaluate.context import iterable from jedi.parser_utils import get_parent_scope @@ -158,10 +159,7 @@ class AbstractInstanceContext(Context): def name(self): pass - def _create_init_execution(self, class_context, func_node): - bound_method = BoundMethod( - self.evaluator, self, class_context, self.parent_context, func_node - ) + def _create_init_execution(self, class_context, bound_method): return self.function_execution_cls( self, class_context.parent_context, @@ -172,7 +170,12 @@ class AbstractInstanceContext(Context): def create_init_executions(self): for name in self.get_function_slot_names(u'__init__'): if isinstance(name, SelfName): - yield self._create_init_execution(name.class_context, name.tree_name.parent) + function = FunctionContext.from_context( + self.parent_context, + name.tree_name.parent + ) + bound_method = BoundMethod(self, name.class_context, function) + yield self._create_init_execution(name.class_context, bound_method) @evaluator_method_cache() def create_instance_context(self, class_context, node): @@ -184,13 +187,14 @@ class AbstractInstanceContext(Context): else: parent_context = self.create_instance_context(class_context, scope) if scope.type == 'funcdef': + func = FunctionContext.from_context( + parent_context, + scope, + ) + bound_method = BoundMethod(self, class_context, func) if scope.name.value == '__init__' and parent_context == class_context: - return self._create_init_execution(class_context, scope) + return self._create_init_execution(class_context, bound_method) else: - bound_method = BoundMethod( - self.evaluator, self, class_context, - parent_context, scope - ) return bound_method.get_function_execution() elif scope.type == 'classdef': class_context = ClassContext(self.evaluator, parent_context, scope) @@ -270,12 +274,8 @@ class CompiledInstanceName(compiled.CompiledName): for result_context in super(CompiledInstanceName, self).infer(): is_function = result_context.api_type == 'function' if result_context.tree_node is not None and is_function: - parent_context = result_context.parent_context - yield BoundMethod( - result_context.evaluator, self._instance, self.parent_context, - parent_context, result_context.tree_node - ) + yield BoundMethod(self._instance, self.parent_context, result_context) else: if is_function: yield CompiledBoundMethod(result_context) @@ -299,11 +299,19 @@ class CompiledInstanceClassFilter(compiled.CompiledObjectFilter): self._evaluator, self._instance, self._compiled_object, name) -class BoundMethod(FunctionContext): - def __init__(self, evaluator, instance, class_context, *args, **kwargs): - super(BoundMethod, self).__init__(evaluator, *args, **kwargs) +class BoundMethod(AbstractFunction): + def __init__(self, instance, klass, function): + super(BoundMethod, self).__init__( + function.evaluator, + function.parent_context, + function.tree_node, + ) self._instance = instance - self._class_context = class_context + self._class = klass + self._function = function + + def py__class__(self): + return compiled.get_special_object(self.evaluator, u'BOUND_METHOD_CLASS') def get_function_execution(self, arguments=None): if arguments is None: @@ -351,11 +359,7 @@ class LazyInstanceClassName(SelfName): # Classes are never used to resolve anything within the # functions. Only other functions and modules will resolve # those things. - parent_context = result_context.parent_context - yield BoundMethod( - result_context.evaluator, self._instance, self.class_context, - parent_context, result_context.tree_node - ) + yield BoundMethod(self._instance, self.class_context, result_context) else: for c in apply_py__get__(result_context, self._instance): yield c diff --git a/jedi/evaluate/lazy_context.py b/jedi/evaluate/lazy_context.py index 97e4b068..84b375c1 100644 --- a/jedi/evaluate/lazy_context.py +++ b/jedi/evaluate/lazy_context.py @@ -1,6 +1,7 @@ from jedi.evaluate.base_context import ContextSet, NO_CONTEXTS from jedi.common.utils import monkeypatch + class AbstractLazyContext(object): def __init__(self, data): self.data = data