forked from VimPlug/jedi
434 lines
16 KiB
Python
434 lines
16 KiB
Python
from abc import abstractproperty
|
|
|
|
from jedi._compatibility import is_py3
|
|
from jedi.common import unite
|
|
from jedi import debug
|
|
from jedi.evaluate import compiled
|
|
from jedi.evaluate import filters
|
|
from jedi.evaluate.context import Context, LazyKnownContext, LazyKnownContexts
|
|
from jedi.evaluate.cache import evaluator_method_cache
|
|
from jedi.evaluate.param import AbstractArguments, AnonymousArguments
|
|
from jedi.cache import memoize_method
|
|
from jedi.evaluate import representation as er
|
|
from jedi.evaluate import iterable
|
|
from jedi.parser_utils import get_parent_scope
|
|
|
|
|
|
|
|
class InstanceFunctionExecution(er.FunctionExecutionContext):
|
|
def __init__(self, instance, parent_context, function_context, var_args):
|
|
self.instance = instance
|
|
var_args = InstanceVarArgs(self, var_args)
|
|
|
|
super(InstanceFunctionExecution, self).__init__(
|
|
instance.evaluator, parent_context, function_context, var_args)
|
|
|
|
|
|
class AnonymousInstanceFunctionExecution(er.FunctionExecutionContext):
|
|
function_execution_filter = filters.AnonymousInstanceFunctionExecutionFilter
|
|
|
|
def __init__(self, instance, parent_context, function_context, var_args):
|
|
self.instance = instance
|
|
super(AnonymousInstanceFunctionExecution, self).__init__(
|
|
instance.evaluator, parent_context, function_context, var_args)
|
|
|
|
|
|
class AbstractInstanceContext(Context):
|
|
"""
|
|
This class is used to evaluate instances.
|
|
"""
|
|
api_type = 'instance'
|
|
function_execution_cls = InstanceFunctionExecution
|
|
|
|
def __init__(self, evaluator, parent_context, class_context, var_args):
|
|
super(AbstractInstanceContext, self).__init__(evaluator, parent_context)
|
|
# Generated instances are classes that are just generated by self
|
|
# (No var_args) used.
|
|
self.class_context = class_context
|
|
self.var_args = var_args
|
|
|
|
def is_class(self):
|
|
return False
|
|
|
|
@property
|
|
def py__call__(self):
|
|
names = self.get_function_slot_names('__call__')
|
|
if not names:
|
|
# Means the Instance is not callable.
|
|
raise AttributeError
|
|
|
|
def execute(arguments):
|
|
return unite(name.execute(arguments) for name in names)
|
|
|
|
return execute
|
|
|
|
def py__class__(self):
|
|
return self.class_context
|
|
|
|
def py__bool__(self):
|
|
# Signalize that we don't know about the bool type.
|
|
return None
|
|
|
|
def get_function_slot_names(self, name):
|
|
# Python classes don't look at the dictionary of the instance when
|
|
# looking up `__call__`. This is something that has to do with Python's
|
|
# internal slot system (note: not __slots__, but C slots).
|
|
for filter in self.get_filters(include_self_names=False):
|
|
names = filter.get(name)
|
|
if names:
|
|
return names
|
|
return []
|
|
|
|
def execute_function_slots(self, names, *evaluated_args):
|
|
return unite(
|
|
name.execute_evaluated(*evaluated_args)
|
|
for name in names
|
|
)
|
|
|
|
def py__get__(self, obj):
|
|
# 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('__get__')
|
|
if names:
|
|
if isinstance(obj, AbstractInstanceContext):
|
|
return self.execute_function_slots(names, obj, obj.class_context)
|
|
else:
|
|
none_obj = compiled.create(self.evaluator, None)
|
|
return self.execute_function_slots(names, none_obj, obj)
|
|
else:
|
|
return set([self])
|
|
|
|
def get_filters(self, search_global=None, until_position=None,
|
|
origin_scope=None, include_self_names=True):
|
|
if include_self_names:
|
|
for cls in self.class_context.py__mro__():
|
|
if isinstance(cls, compiled.CompiledObject):
|
|
if cls.tree_node is not None:
|
|
# In this case we're talking about a fake object, it
|
|
# doesn't make sense for normal compiled objects to
|
|
# search for self variables.
|
|
yield SelfNameFilter(self.evaluator, self, cls, origin_scope)
|
|
else:
|
|
yield SelfNameFilter(self.evaluator, self, cls, origin_scope)
|
|
|
|
for cls in self.class_context.py__mro__():
|
|
if isinstance(cls, compiled.CompiledObject):
|
|
yield CompiledInstanceClassFilter(self.evaluator, self, cls)
|
|
else:
|
|
yield InstanceClassFilter(self.evaluator, self, cls, origin_scope)
|
|
|
|
def py__getitem__(self, index):
|
|
try:
|
|
names = self.get_function_slot_names('__getitem__')
|
|
except KeyError:
|
|
debug.warning('No __getitem__, cannot access the array.')
|
|
return set()
|
|
else:
|
|
index_obj = compiled.create(self.evaluator, index)
|
|
return self.execute_function_slots(names, index_obj)
|
|
|
|
def py__iter__(self):
|
|
iter_slot_names = self.get_function_slot_names('__iter__')
|
|
if not iter_slot_names:
|
|
debug.warning('No __iter__ on %s.' % self)
|
|
return
|
|
|
|
for generator in self.execute_function_slots(iter_slot_names):
|
|
if isinstance(generator, AbstractInstanceContext):
|
|
# `__next__` logic.
|
|
name = '__next__' if is_py3 else 'next'
|
|
iter_slot_names = generator.get_function_slot_names(name)
|
|
if iter_slot_names:
|
|
yield LazyKnownContexts(
|
|
generator.execute_function_slots(iter_slot_names)
|
|
)
|
|
else:
|
|
debug.warning('Instance has no __next__ function in %s.', generator)
|
|
else:
|
|
for lazy_context in generator.py__iter__():
|
|
yield lazy_context
|
|
|
|
@abstractproperty
|
|
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
|
|
)
|
|
return self.function_execution_cls(
|
|
self,
|
|
class_context.parent_context,
|
|
bound_method,
|
|
self.var_args
|
|
)
|
|
|
|
def create_init_executions(self):
|
|
for name in self.get_function_slot_names('__init__'):
|
|
if isinstance(name, LazyInstanceName):
|
|
yield self._create_init_execution(name.class_context, name.tree_name.parent)
|
|
|
|
@evaluator_method_cache()
|
|
def create_instance_context(self, class_context, node):
|
|
if node.parent.type in ('funcdef', 'classdef'):
|
|
node = node.parent
|
|
scope = get_parent_scope(node)
|
|
if scope == class_context.tree_node:
|
|
return class_context
|
|
else:
|
|
parent_context = self.create_instance_context(class_context, scope)
|
|
if scope.type == 'funcdef':
|
|
if scope.name.value == '__init__' and parent_context == class_context:
|
|
return self._create_init_execution(class_context, scope)
|
|
else:
|
|
bound_method = BoundMethod(
|
|
self.evaluator, self, class_context,
|
|
parent_context, scope
|
|
)
|
|
return bound_method.get_function_execution()
|
|
elif scope.type == 'classdef':
|
|
class_context = er.ClassContext(self.evaluator, scope, parent_context)
|
|
return class_context
|
|
elif scope.type == 'comp_for':
|
|
# Comprehensions currently don't have a special scope in Jedi.
|
|
return self.create_instance_context(class_context, scope)
|
|
else:
|
|
raise NotImplementedError
|
|
return class_context
|
|
|
|
def __repr__(self):
|
|
return "<%s of %s(%s)>" % (self.__class__.__name__, self.class_context,
|
|
self.var_args)
|
|
|
|
|
|
class CompiledInstance(AbstractInstanceContext):
|
|
def __init__(self, *args, **kwargs):
|
|
super(CompiledInstance, self).__init__(*args, **kwargs)
|
|
# I don't think that dynamic append lookups should happen here. That
|
|
# sounds more like something that should go to py__iter__.
|
|
if self.class_context.name.string_name in ['list', 'set'] \
|
|
and self.parent_context.get_root_context() == self.evaluator.BUILTINS:
|
|
# compare the module path with the builtin name.
|
|
self.var_args = iterable.get_dynamic_array_instance(self)
|
|
|
|
@property
|
|
def name(self):
|
|
return compiled.CompiledContextName(self, self.class_context.name.string_name)
|
|
|
|
def create_instance_context(self, class_context, node):
|
|
if get_parent_scope(node).type == 'classdef':
|
|
return class_context
|
|
else:
|
|
return super(CompiledInstance, self).create_instance_context(class_context, node)
|
|
|
|
|
|
class TreeInstance(AbstractInstanceContext):
|
|
def __init__(self, evaluator, parent_context, class_context, var_args):
|
|
super(TreeInstance, self).__init__(evaluator, parent_context,
|
|
class_context, var_args)
|
|
self.tree_node = class_context.tree_node
|
|
|
|
@property
|
|
def name(self):
|
|
return filters.ContextName(self, self.class_context.name.tree_name)
|
|
|
|
|
|
class AnonymousInstance(TreeInstance):
|
|
function_execution_cls = AnonymousInstanceFunctionExecution
|
|
|
|
def __init__(self, evaluator, parent_context, class_context):
|
|
super(AnonymousInstance, self).__init__(
|
|
evaluator,
|
|
parent_context,
|
|
class_context,
|
|
var_args=AnonymousArguments(),
|
|
)
|
|
|
|
|
|
class CompiledInstanceName(compiled.CompiledName):
|
|
def __init__(self, evaluator, instance, parent_context, name):
|
|
super(CompiledInstanceName, self).__init__(evaluator, parent_context, name)
|
|
self._instance = instance
|
|
|
|
def infer(self):
|
|
for result_context in super(CompiledInstanceName, self).infer():
|
|
if isinstance(result_context, er.FunctionContext):
|
|
parent_context = result_context.parent_context
|
|
while parent_context.is_class():
|
|
parent_context = parent_context.parent_context
|
|
|
|
yield BoundMethod(
|
|
result_context.evaluator, self._instance, self.parent_context,
|
|
parent_context, result_context.tree_node
|
|
)
|
|
else:
|
|
if result_context.api_type == 'function':
|
|
yield CompiledBoundMethod(result_context)
|
|
else:
|
|
yield result_context
|
|
|
|
|
|
class CompiledInstanceClassFilter(compiled.CompiledObjectFilter):
|
|
name_class = CompiledInstanceName
|
|
|
|
def __init__(self, evaluator, instance, compiled_object):
|
|
super(CompiledInstanceClassFilter, self).__init__(
|
|
evaluator,
|
|
compiled_object,
|
|
is_instance=True,
|
|
)
|
|
self._instance = instance
|
|
|
|
def _create_name(self, name):
|
|
return self.name_class(
|
|
self._evaluator, self._instance, self._compiled_object, name)
|
|
|
|
|
|
class BoundMethod(er.FunctionContext):
|
|
def __init__(self, evaluator, instance, class_context, *args, **kwargs):
|
|
super(BoundMethod, self).__init__(evaluator, *args, **kwargs)
|
|
self._instance = instance
|
|
self._class_context = class_context
|
|
|
|
def get_function_execution(self, arguments=None):
|
|
if arguments is None:
|
|
arguments = AnonymousArguments()
|
|
return AnonymousInstanceFunctionExecution(
|
|
self._instance, self.parent_context, self, arguments)
|
|
else:
|
|
return InstanceFunctionExecution(
|
|
self._instance, self.parent_context, self, arguments)
|
|
|
|
|
|
class CompiledBoundMethod(compiled.CompiledObject):
|
|
def __init__(self, func):
|
|
super(CompiledBoundMethod, self).__init__(
|
|
func.evaluator, func.obj, func.parent_context, func.tree_node)
|
|
|
|
def get_param_names(self):
|
|
return list(super(CompiledBoundMethod, self).get_param_names())[1:]
|
|
|
|
|
|
class InstanceNameDefinition(filters.TreeNameDefinition):
|
|
def infer(self):
|
|
contexts = super(InstanceNameDefinition, self).infer()
|
|
for context in contexts:
|
|
yield context
|
|
|
|
|
|
class LazyInstanceName(filters.TreeNameDefinition):
|
|
"""
|
|
This name calculates the parent_context lazily.
|
|
"""
|
|
def __init__(self, instance, class_context, tree_name):
|
|
self._instance = instance
|
|
self.class_context = class_context
|
|
self.tree_name = tree_name
|
|
|
|
@property
|
|
def parent_context(self):
|
|
return self._instance.create_instance_context(self.class_context, self.tree_name)
|
|
|
|
|
|
class LazyInstanceClassName(LazyInstanceName):
|
|
def infer(self):
|
|
for result_context in super(LazyInstanceClassName, self).infer():
|
|
if isinstance(result_context, er.FunctionContext):
|
|
# 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
|
|
while parent_context.is_class():
|
|
parent_context = parent_context.parent_context
|
|
|
|
yield BoundMethod(
|
|
result_context.evaluator, self._instance, self.class_context,
|
|
parent_context, result_context.tree_node
|
|
)
|
|
else:
|
|
for c in er.apply_py__get__(result_context, self._instance):
|
|
yield c
|
|
|
|
|
|
class InstanceClassFilter(filters.ParserTreeFilter):
|
|
name_class = LazyInstanceClassName
|
|
|
|
def __init__(self, evaluator, context, class_context, origin_scope):
|
|
super(InstanceClassFilter, self).__init__(
|
|
evaluator=evaluator,
|
|
context=context,
|
|
node_context=class_context,
|
|
origin_scope=origin_scope
|
|
)
|
|
self._class_context = class_context
|
|
|
|
def _equals_origin_scope(self):
|
|
node = self._origin_scope
|
|
while node is not None:
|
|
if node == self._parser_scope or node == self.context:
|
|
return True
|
|
node = get_parent_scope(node)
|
|
return False
|
|
|
|
def _access_possible(self, name):
|
|
return not name.value.startswith('__') or name.value.endswith('__') \
|
|
or self._equals_origin_scope()
|
|
|
|
def _filter(self, names):
|
|
names = super(InstanceClassFilter, self)._filter(names)
|
|
return [name for name in names if self._access_possible(name)]
|
|
|
|
def _convert_names(self, names):
|
|
return [self.name_class(self.context, self._class_context, name) for name in names]
|
|
|
|
|
|
class SelfNameFilter(InstanceClassFilter):
|
|
name_class = LazyInstanceName
|
|
|
|
def _filter(self, names):
|
|
names = self._filter_self_names(names)
|
|
if isinstance(self._parser_scope, compiled.CompiledObject) and False:
|
|
# This would be for builtin skeletons, which are not yet supported.
|
|
return list(names)
|
|
else:
|
|
start, end = self._parser_scope.start_pos, self._parser_scope.end_pos
|
|
return [n for n in names if start < n.start_pos < end]
|
|
|
|
def _filter_self_names(self, names):
|
|
for name in names:
|
|
trailer = name.parent
|
|
if trailer.type == 'trailer' \
|
|
and len(trailer.children) == 2 \
|
|
and trailer.children[0] == '.':
|
|
if name.is_definition() and self._access_possible(name):
|
|
yield name
|
|
|
|
def _check_flows(self, names):
|
|
return names
|
|
|
|
|
|
class InstanceVarArgs(AbstractArguments):
|
|
def __init__(self, execution_context, var_args):
|
|
self._execution_context = execution_context
|
|
self._var_args = var_args
|
|
|
|
@memoize_method
|
|
def _get_var_args(self):
|
|
return self._var_args
|
|
|
|
@property
|
|
def argument_node(self):
|
|
return self._var_args.argument_node
|
|
|
|
@property
|
|
def trailer(self):
|
|
return self._var_args.trailer
|
|
|
|
def unpack(self, func=None):
|
|
yield None, LazyKnownContext(self._execution_context.instance)
|
|
for values in self._get_var_args().unpack(func):
|
|
yield values
|
|
|
|
def get_calling_nodes(self):
|
|
return self._get_var_args().get_calling_nodes()
|