forked from VimPlug/jedi
425 lines
15 KiB
Python
425 lines
15 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 memoize_default
|
|
from jedi.cache import memoize_method
|
|
from jedi.evaluate import representation as er
|
|
from jedi.evaluate.dynamic import search_params
|
|
|
|
|
|
class AbstractInstanceContext(Context):
|
|
"""
|
|
This class is used to evaluate instances.
|
|
"""
|
|
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
|
|
|
|
#####
|
|
""""
|
|
if class_context.name.string_name in ['list', 'set'] \
|
|
and evaluator.BUILTINS == parent_context.get_root_context():
|
|
# compare the module path with the builtin name.
|
|
self.var_args = iterable.check_array_instances(evaluator, self)
|
|
elif not is_generated:
|
|
# Need to execute the __init__ function, because the dynamic param
|
|
# searching needs it.
|
|
try:
|
|
method = self.get_subscope_by_name('__init__')
|
|
except KeyError:
|
|
pass
|
|
else:
|
|
self._init_execution = evaluator.execute(method, self.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 get_descriptor_returns(self, obj):
|
|
""" Throws a KeyError if there's no method. """
|
|
# 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):
|
|
yield CompiledSelfNameFilter(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 unite(name.execute_evaluated(index_obj) for name in names)
|
|
|
|
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
|
|
|
|
@memoize_default()
|
|
def create_instance_context(self, class_context, node):
|
|
if node.parent.type in ('funcdef', 'classdef'):
|
|
node = node.parent
|
|
scope = node.get_parent_scope()
|
|
if scope == class_context.classdef:
|
|
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 InstanceFunctionExecution(
|
|
self,
|
|
class_context.parent_context,
|
|
scope,
|
|
self.var_args
|
|
)
|
|
else:
|
|
return AnonymousInstanceFunctionExecution(
|
|
self,
|
|
class_context.parent_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):
|
|
@property
|
|
def name(self):
|
|
return compiled.CompiledContextName(self, self.class_context.name.string_name)
|
|
|
|
def create_instance_context(self, class_context, node):
|
|
if node.get_parent_scope().type == 'classdef':
|
|
return class_context
|
|
else:
|
|
return super(CompiledInstance, self).create_instance_context(class_context, node)
|
|
|
|
|
|
class TreeInstance(AbstractInstanceContext):
|
|
@property
|
|
def name(self):
|
|
return filters.ContextName(self, self.class_context.name.tree_name)
|
|
|
|
|
|
class AnonymousInstance(TreeInstance):
|
|
def __init__(self, evaluator, parent_context, class_context):
|
|
super(AnonymousInstance, self).__init__(
|
|
evaluator,
|
|
parent_context,
|
|
class_context,
|
|
var_args=None
|
|
)
|
|
|
|
|
|
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 v in super(CompiledInstanceName, self).infer():
|
|
if isinstance(v, er.FunctionContext):
|
|
yield BoundMethod(self._instance, self.parent_context, v)
|
|
else:
|
|
yield v
|
|
|
|
|
|
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(self, name):
|
|
return self.name_class(self._evaluator, self._instance, self._compiled_obj, name)
|
|
|
|
|
|
class BoundMethod(Context):
|
|
def __init__(self, instance, class_context, function):
|
|
self._instance = instance
|
|
self._class_context = class_context
|
|
self._function = function
|
|
|
|
def __getattr__(self, name):
|
|
return getattr(self._function, name)
|
|
|
|
def py__call__(self, var_args):
|
|
function_execution = InstanceFunctionExecution(
|
|
self._instance,
|
|
self.parent_context,
|
|
self._function.funcdef,
|
|
var_args
|
|
)
|
|
return self._function.infer_function_execution(function_execution)
|
|
|
|
def __repr__(self):
|
|
return '<%s: %s>' % (self.__class__.__name__, self._function)
|
|
|
|
|
|
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 v in super(LazyInstanceClassName, self).infer():
|
|
if isinstance(v, er.FunctionContext):
|
|
yield BoundMethod(self._instance, self._class_context, v)
|
|
else:
|
|
yield v
|
|
|
|
|
|
class InstanceClassFilter(filters.ParserTreeFilter):
|
|
name_class = LazyInstanceClassName
|
|
|
|
def __init__(self, evaluator, context, class_context, origin_scope):
|
|
super(InstanceClassFilter, self).__init__(
|
|
evaluator=evaluator,
|
|
context=context,
|
|
parser_scope=class_context.classdef,
|
|
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 = node.get_parent_scope()
|
|
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):
|
|
# 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
|
|
continue
|
|
init_execution = self.context.get_init_function()
|
|
# Hopefully we can somehow change this.
|
|
if init_execution is not None and \
|
|
init_execution.start_pos < name.start_pos < init_execution.end_pos:
|
|
name = init_execution.name_for_position(name.start_pos)
|
|
yield name
|
|
|
|
def _check_flows(self, names):
|
|
return names
|
|
|
|
|
|
class CompiledSelfNameFilter(SelfNameFilter):
|
|
"""
|
|
This filter is a bit special and exists only because of `compiled/fake/*`.
|
|
"""
|
|
def _access_possible(self, name):
|
|
return True
|
|
|
|
|
|
class ParamArguments(object):
|
|
"""
|
|
TODO This seems like a strange class, clean up?
|
|
"""
|
|
class LazyParamContext(object):
|
|
def __init__(self, fucking_param):
|
|
self._param = fucking_param
|
|
|
|
def infer(self):
|
|
return self._param.infer()
|
|
|
|
def __init__(self, class_context, funcdef):
|
|
self._class_context = class_context
|
|
self._funcdef = funcdef
|
|
|
|
def unpack(self, func=None):
|
|
params = search_params(
|
|
self._class_context.evaluator,
|
|
self._class_context,
|
|
self._funcdef
|
|
)
|
|
is_first = True
|
|
for p in params:
|
|
# TODO Yeah, here at last, the class seems to be really wrong.
|
|
if is_first:
|
|
is_first = False
|
|
continue
|
|
yield None, self.LazyParamContext(p)
|
|
|
|
|
|
class InstanceVarArgs(object):
|
|
def __init__(self, instance, funcdef, var_args):
|
|
self._instance = instance
|
|
self._funcdef = funcdef
|
|
self._var_args = var_args
|
|
|
|
@memoize_method
|
|
def _get_var_args(self):
|
|
if self._var_args is None:
|
|
# TODO this parent_context might be wrong. test?!
|
|
return ParamArguments(self._instance.class_context, self._funcdef)
|
|
|
|
return self._var_args
|
|
|
|
def unpack(self, func=None):
|
|
yield None, LazyKnownContext(self._instance)
|
|
for values in self._get_var_args().unpack(func):
|
|
yield values
|
|
|
|
def get_calling_var_args(self):
|
|
return self._get_var_args().get_calling_var_args()
|
|
|
|
|
|
class InstanceFunctionExecution(er.FunctionExecutionContext):
|
|
def __init__(self, instance, parent_context, funcdef, var_args):
|
|
self.instance = instance
|
|
var_args = InstanceVarArgs(instance, funcdef, var_args)
|
|
|
|
super(InstanceFunctionExecution, self).__init__(
|
|
instance.evaluator, parent_context, funcdef, var_args)
|
|
|
|
|
|
class AnonymousInstanceFunctionExecution(InstanceFunctionExecution):
|
|
function_execution_filter = filters.AnonymousInstanceFunctionExecutionFilter
|
|
|
|
def __init__(self, instance, parent_context, funcdef):
|
|
super(AnonymousInstanceFunctionExecution, self).__init__(
|
|
instance, parent_context, funcdef, None)
|