1
0
forked from VimPlug/jedi

BoundMethods now have access to the function that they are using

This commit is contained in:
Dave Halter
2018-08-03 00:15:11 +02:00
parent e576457a43
commit f25310e0b9
5 changed files with 72 additions and 66 deletions

View File

@@ -334,15 +334,15 @@ class Evaluator(object):
parent_context = from_scope_node(parent_scope, child_is_funcdef=is_funcdef) parent_context = from_scope_node(parent_scope, child_is_funcdef=is_funcdef)
if is_funcdef: if is_funcdef:
func = FunctionContext.from_context(
parent_context,
scope_node
)
if isinstance(parent_context, AnonymousInstance): if isinstance(parent_context, AnonymousInstance):
func = BoundMethod( func = BoundMethod(
self, parent_context, parent_context.class_context, instance=parent_context,
parent_context.parent_context, scope_node klass=parent_context.class_context,
) function=func
else:
func = FunctionContext.from_context(
parent_context,
scope_node
) )
if is_nested and not node_is_object: if is_nested and not node_is_object:
return func.get_function_execution() return func.get_function_execution()

View File

@@ -470,7 +470,7 @@ else:
class _SPECIAL_OBJECTS(object): class _SPECIAL_OBJECTS(object):
FUNCTION_CLASS = types.FunctionType FUNCTION_CLASS = types.FunctionType
METHOD_CLASS = type(DirectObjectAccess.py__bool__) BOUND_METHOD_CLASS = type(DirectObjectAccess(None, None).py__bool__)
MODULE_CLASS = types.ModuleType MODULE_CLASS = types.ModuleType
GENERATOR_OBJECT = _a_generator(1.0) GENERATOR_OBJECT = _a_generator(1.0)
BUILTINS = builtins BUILTINS = builtins

View File

@@ -38,19 +38,9 @@ class LambdaName(AbstractNameDefinition):
return ContextSet(self._lambda_context) return ContextSet(self._lambda_context)
class FunctionContext(use_metaclass(CachedMetaClass, TreeContext)): class AbstractFunction(TreeContext):
"""
Needed because of decorators. Decorators are evaluated here.
"""
api_type = u'function' 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): def get_filters(self, search_global, until_position=None, origin_scope=None):
if search_global: if search_global:
yield ParserTreeFilter( 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): for filter in scope.get_filters(search_global=False, origin_scope=origin_scope):
yield filter 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): def infer_function_execution(self, function_execution):
""" """
Created to be used by inheritance. Created to be used by inheritance.
@@ -86,35 +94,28 @@ class FunctionContext(use_metaclass(CachedMetaClass, TreeContext)):
else: else:
return function_execution.get_return_values() 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): def get_function_execution(self, arguments=None):
if arguments is None: if arguments is None:
arguments = AnonymousArguments() arguments = AnonymousArguments()
return FunctionExecutionContext(self.evaluator, self.parent_context, self, arguments) 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): def py__class__(self):
# This differentiation is only necessary for Python2. Python3 does not return compiled.get_special_object(self.evaluator, u'FUNCTION_CLASS')
# 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()]
class FunctionExecutionContext(TreeContext): class FunctionExecutionContext(TreeContext):

View File

@@ -9,7 +9,8 @@ from jedi.evaluate.lazy_context import LazyKnownContext, LazyKnownContexts
from jedi.evaluate.cache import evaluator_method_cache from jedi.evaluate.cache import evaluator_method_cache
from jedi.evaluate.arguments import AbstractArguments, AnonymousArguments from jedi.evaluate.arguments import AbstractArguments, AnonymousArguments
from jedi.cache import memoize_method 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.klass import ClassContext, apply_py__get__
from jedi.evaluate.context import iterable from jedi.evaluate.context import iterable
from jedi.parser_utils import get_parent_scope from jedi.parser_utils import get_parent_scope
@@ -158,10 +159,7 @@ class AbstractInstanceContext(Context):
def name(self): def name(self):
pass pass
def _create_init_execution(self, class_context, func_node): def _create_init_execution(self, class_context, bound_method):
bound_method = BoundMethod(
self.evaluator, self, class_context, self.parent_context, func_node
)
return self.function_execution_cls( return self.function_execution_cls(
self, self,
class_context.parent_context, class_context.parent_context,
@@ -172,7 +170,12 @@ class AbstractInstanceContext(Context):
def create_init_executions(self): def create_init_executions(self):
for name in self.get_function_slot_names(u'__init__'): for name in self.get_function_slot_names(u'__init__'):
if isinstance(name, SelfName): 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() @evaluator_method_cache()
def create_instance_context(self, class_context, node): def create_instance_context(self, class_context, node):
@@ -184,13 +187,14 @@ class AbstractInstanceContext(Context):
else: else:
parent_context = self.create_instance_context(class_context, scope) parent_context = self.create_instance_context(class_context, scope)
if scope.type == 'funcdef': 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: 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: else:
bound_method = BoundMethod(
self.evaluator, self, class_context,
parent_context, scope
)
return bound_method.get_function_execution() return bound_method.get_function_execution()
elif scope.type == 'classdef': elif scope.type == 'classdef':
class_context = ClassContext(self.evaluator, parent_context, scope) class_context = ClassContext(self.evaluator, parent_context, scope)
@@ -270,12 +274,8 @@ class CompiledInstanceName(compiled.CompiledName):
for result_context in super(CompiledInstanceName, self).infer(): for result_context in super(CompiledInstanceName, self).infer():
is_function = result_context.api_type == 'function' is_function = result_context.api_type == 'function'
if result_context.tree_node is not None and is_function: if result_context.tree_node is not None and is_function:
parent_context = result_context.parent_context
yield BoundMethod( yield BoundMethod(self._instance, self.parent_context, result_context)
result_context.evaluator, self._instance, self.parent_context,
parent_context, result_context.tree_node
)
else: else:
if is_function: if is_function:
yield CompiledBoundMethod(result_context) yield CompiledBoundMethod(result_context)
@@ -299,11 +299,19 @@ class CompiledInstanceClassFilter(compiled.CompiledObjectFilter):
self._evaluator, self._instance, self._compiled_object, name) self._evaluator, self._instance, self._compiled_object, name)
class BoundMethod(FunctionContext): class BoundMethod(AbstractFunction):
def __init__(self, evaluator, instance, class_context, *args, **kwargs): def __init__(self, instance, klass, function):
super(BoundMethod, self).__init__(evaluator, *args, **kwargs) super(BoundMethod, self).__init__(
function.evaluator,
function.parent_context,
function.tree_node,
)
self._instance = instance 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): def get_function_execution(self, arguments=None):
if arguments is None: if arguments is None:
@@ -351,11 +359,7 @@ class LazyInstanceClassName(SelfName):
# Classes are never used to resolve anything within the # Classes are never used to resolve anything within the
# functions. Only other functions and modules will resolve # functions. Only other functions and modules will resolve
# those things. # those things.
parent_context = result_context.parent_context yield BoundMethod(self._instance, self.class_context, result_context)
yield BoundMethod(
result_context.evaluator, self._instance, self.class_context,
parent_context, result_context.tree_node
)
else: else:
for c in apply_py__get__(result_context, self._instance): for c in apply_py__get__(result_context, self._instance):
yield c yield c

View File

@@ -1,6 +1,7 @@
from jedi.evaluate.base_context import ContextSet, NO_CONTEXTS from jedi.evaluate.base_context import ContextSet, NO_CONTEXTS
from jedi.common.utils import monkeypatch from jedi.common.utils import monkeypatch
class AbstractLazyContext(object): class AbstractLazyContext(object):
def __init__(self, data): def __init__(self, data):
self.data = data self.data = data