You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
484 lines
18 KiB
484 lines
18 KiB
6 years ago
|
from abc import abstractproperty
|
||
|
|
||
|
from jedi import debug
|
||
|
from jedi import settings
|
||
|
from jedi.evaluate import compiled
|
||
|
from jedi.evaluate import filters
|
||
|
from jedi.evaluate.base_context import Context, NO_CONTEXTS, ContextSet, \
|
||
|
iterator_to_context_set
|
||
|
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.context.function import FunctionExecutionContext, \
|
||
|
FunctionContext, AbstractFunction
|
||
|
from jedi.evaluate.context.klass import ClassContext, apply_py__get__, ClassFilter
|
||
|
from jedi.evaluate.context import iterable
|
||
|
from jedi.parser_utils import get_parent_scope
|
||
|
|
||
|
|
||
|
class InstanceExecutedParam(object):
|
||
|
def __init__(self, instance):
|
||
|
self._instance = instance
|
||
|
|
||
|
def infer(self):
|
||
|
return ContextSet(self._instance)
|
||
|
|
||
|
|
||
|
class AnonymousInstanceArguments(AnonymousArguments):
|
||
|
def __init__(self, instance):
|
||
|
self._instance = instance
|
||
|
|
||
|
def get_executed_params(self, execution_context):
|
||
|
from jedi.evaluate.dynamic import search_params
|
||
|
self_param = InstanceExecutedParam(self._instance)
|
||
|
tree_params = execution_context.tree_node.get_params()
|
||
|
if len(tree_params) == 1:
|
||
|
# If the only param is self, we don't need to try to find
|
||
|
# executions of this function, we have all the params already.
|
||
|
return [self_param]
|
||
|
executed_params = list(search_params(
|
||
|
execution_context.evaluator,
|
||
|
execution_context,
|
||
|
execution_context.tree_node
|
||
|
))
|
||
|
executed_params[0] = self_param
|
||
|
return executed_params
|
||
|
|
||
|
|
||
|
class AbstractInstanceContext(Context):
|
||
|
"""
|
||
|
This class is used to evaluate instances.
|
||
|
"""
|
||
|
api_type = u'instance'
|
||
|
|
||
|
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(u'__call__')
|
||
|
if not names:
|
||
|
# Means the Instance is not callable.
|
||
|
raise AttributeError
|
||
|
|
||
|
def execute(arguments):
|
||
|
return ContextSet.from_sets(name.infer().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 ContextSet.from_sets(
|
||
|
name.infer().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(u'__get__')
|
||
|
if names:
|
||
|
if isinstance(obj, AbstractInstanceContext):
|
||
|
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)
|
||
|
else:
|
||
|
return ContextSet(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 not isinstance(cls, compiled.CompiledObject) \
|
||
|
or cls.tree_node is not None:
|
||
|
# In this case we're excluding compiled objects that are
|
||
|
# not fake objects. It doesn't make sense for normal
|
||
|
# compiled objects to search for self variables.
|
||
|
yield SelfAttributeFilter(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(u'__getitem__')
|
||
|
except KeyError:
|
||
|
debug.warning('No __getitem__, cannot access the array.')
|
||
|
return NO_CONTEXTS
|
||
|
else:
|
||
|
index_obj = compiled.create_simple_object(self.evaluator, index)
|
||
|
return self.execute_function_slots(names, index_obj)
|
||
|
|
||
|
def py__iter__(self):
|
||
|
iter_slot_names = self.get_function_slot_names(u'__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.
|
||
|
if self.evaluator.environment.version_info.major == 2:
|
||
|
name = u'next'
|
||
|
else:
|
||
|
name = u'__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, bound_method):
|
||
|
return bound_method.get_function_execution(self.var_args)
|
||
|
|
||
|
def create_init_executions(self):
|
||
|
for name in self.get_function_slot_names(u'__init__'):
|
||
|
if isinstance(name, LazyInstanceClassName):
|
||
|
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):
|
||
|
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':
|
||
|
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, bound_method)
|
||
|
else:
|
||
|
return bound_method.get_function_execution()
|
||
|
elif scope.type == 'classdef':
|
||
|
class_context = ClassContext(self.evaluator, parent_context, scope)
|
||
|
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, evaluator, parent_context, class_context, var_args):
|
||
|
self._original_var_args = var_args
|
||
|
|
||
|
# I don't think that dynamic append lookups should happen here. That
|
||
|
# sounds more like something that should go to py__iter__.
|
||
|
if class_context.py__name__() in ['list', 'set'] \
|
||
|
and parent_context.get_root_context() == evaluator.builtins_module:
|
||
|
# compare the module path with the builtin name.
|
||
|
if settings.dynamic_array_additions:
|
||
|
var_args = iterable.get_dynamic_array_instance(self, var_args)
|
||
|
|
||
|
super(CompiledInstance, self).__init__(evaluator, parent_context, class_context, var_args)
|
||
|
|
||
|
@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)
|
||
|
|
||
|
def get_first_non_keyword_argument_contexts(self):
|
||
|
key, lazy_context = next(self._original_var_args.unpack(), ('', None))
|
||
|
if key is not None:
|
||
|
return NO_CONTEXTS
|
||
|
|
||
|
return lazy_context.infer()
|
||
|
|
||
|
|
||
|
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):
|
||
|
def __init__(self, evaluator, parent_context, class_context):
|
||
|
super(AnonymousInstance, self).__init__(
|
||
|
evaluator,
|
||
|
parent_context,
|
||
|
class_context,
|
||
|
var_args=AnonymousInstanceArguments(self),
|
||
|
)
|
||
|
|
||
|
|
||
|
class CompiledInstanceName(compiled.CompiledName):
|
||
|
|
||
|
def __init__(self, evaluator, instance, klass, name):
|
||
|
super(CompiledInstanceName, self).__init__(
|
||
|
evaluator,
|
||
|
klass.parent_context,
|
||
|
name.string_name
|
||
|
)
|
||
|
self._instance = instance
|
||
|
self._class = klass
|
||
|
self._class_member_name = name
|
||
|
|
||
|
@iterator_to_context_set
|
||
|
def infer(self):
|
||
|
for result_context in self._class_member_name.infer():
|
||
|
is_function = result_context.api_type == 'function'
|
||
|
if result_context.tree_node is not None and is_function:
|
||
|
yield BoundMethod(self._instance, self._class, result_context)
|
||
|
else:
|
||
|
if is_function:
|
||
|
yield CompiledBoundMethod(result_context)
|
||
|
else:
|
||
|
yield result_context
|
||
|
|
||
|
|
||
|
class CompiledInstanceClassFilter(filters.AbstractFilter):
|
||
|
name_class = CompiledInstanceName
|
||
|
|
||
|
def __init__(self, evaluator, instance, klass):
|
||
|
self._evaluator = evaluator
|
||
|
self._instance = instance
|
||
|
self._class = klass
|
||
|
self._class_filter = next(klass.get_filters(is_instance=True))
|
||
|
|
||
|
def get(self, name):
|
||
|
return self._convert(self._class_filter.get(name))
|
||
|
|
||
|
def values(self):
|
||
|
return self._convert(self._class_filter.values())
|
||
|
|
||
|
def _convert(self, names):
|
||
|
return [
|
||
|
CompiledInstanceName(self._evaluator, self._instance, self._class, n)
|
||
|
for n in names
|
||
|
]
|
||
|
|
||
|
|
||
|
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 = 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:
|
||
|
arguments = AnonymousInstanceArguments(self._instance)
|
||
|
|
||
|
arguments = InstanceArguments(self._instance, arguments)
|
||
|
|
||
|
if isinstance(self._function, compiled.CompiledObject):
|
||
|
# This is kind of weird, because it's coming from a compiled object
|
||
|
# and we're not sure if we want that in the future.
|
||
|
return FunctionExecutionContext(
|
||
|
self.evaluator, self.parent_context, self, arguments
|
||
|
)
|
||
|
|
||
|
return self._function.get_function_execution(arguments)
|
||
|
|
||
|
def __repr__(self):
|
||
|
return '<%s: %s>' % (self.__class__.__name__, self._function)
|
||
|
|
||
|
|
||
|
class CompiledBoundMethod(compiled.CompiledObject):
|
||
|
def __init__(self, func):
|
||
|
super(CompiledBoundMethod, self).__init__(
|
||
|
func.evaluator, func.access_handle, func.parent_context, func.tree_node)
|
||
|
|
||
|
def get_param_names(self):
|
||
|
return list(super(CompiledBoundMethod, self).get_param_names())[1:]
|
||
|
|
||
|
|
||
|
class SelfName(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(object):
|
||
|
def __init__(self, instance, class_context, class_member_name):
|
||
|
self._instance = instance
|
||
|
self.class_context = class_context
|
||
|
self._class_member_name = class_member_name
|
||
|
|
||
|
@iterator_to_context_set
|
||
|
def infer(self):
|
||
|
for result_context in self._class_member_name.infer():
|
||
|
if isinstance(result_context, FunctionContext):
|
||
|
# 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
|
||
|
|
||
|
def __getattr__(self, name):
|
||
|
return getattr(self._class_member_name, name)
|
||
|
|
||
|
|
||
|
class InstanceClassFilter(filters.AbstractFilter):
|
||
|
"""
|
||
|
This filter is special in that it uses the class filter and wraps the
|
||
|
resulting names in LazyINstanceClassName. The idea is that the class name
|
||
|
filtering can be very flexible and always be reflected in instances.
|
||
|
"""
|
||
|
def __init__(self, evaluator, context, class_context, origin_scope):
|
||
|
self._instance = context
|
||
|
self._class_context = class_context
|
||
|
self._class_filter = next(class_context.get_filters(
|
||
|
search_global=False,
|
||
|
origin_scope=origin_scope,
|
||
|
is_instance=True,
|
||
|
))
|
||
|
|
||
|
def get(self, name):
|
||
|
return self._convert(self._class_filter.get(name))
|
||
|
|
||
|
def values(self):
|
||
|
return self._convert(self._class_filter.values())
|
||
|
|
||
|
def _convert(self, names):
|
||
|
return [LazyInstanceClassName(self._instance, self._class_context, n) for n in names]
|
||
|
|
||
|
|
||
|
class SelfAttributeFilter(ClassFilter):
|
||
|
"""
|
||
|
This class basically filters all the use cases where `self.*` was assigned.
|
||
|
"""
|
||
|
name_class = SelfName
|
||
|
|
||
|
def __init__(self, evaluator, context, class_context, origin_scope):
|
||
|
super(SelfAttributeFilter, self).__init__(
|
||
|
evaluator=evaluator,
|
||
|
context=context,
|
||
|
node_context=class_context,
|
||
|
origin_scope=origin_scope,
|
||
|
is_instance=True,
|
||
|
)
|
||
|
self._class_context = class_context
|
||
|
|
||
|
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 _convert_names(self, names):
|
||
|
return [self.name_class(self.context, self._class_context, name) for name in names]
|
||
|
|
||
|
def _check_flows(self, names):
|
||
|
return names
|
||
|
|
||
|
|
||
|
class InstanceArguments(AbstractArguments):
|
||
|
def __init__(self, instance, var_args):
|
||
|
self.instance = instance
|
||
|
self._var_args = 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.instance)
|
||
|
for values in self._var_args.unpack(func):
|
||
|
yield values
|
||
|
|
||
|
def get_calling_nodes(self):
|
||
|
return self._var_args.get_calling_nodes()
|
||
|
|
||
|
def get_executed_params(self, execution_context):
|
||
|
if isinstance(self._var_args, AnonymousInstanceArguments):
|
||
|
return self._var_args.get_executed_params(execution_context)
|
||
|
|
||
|
return super(InstanceArguments, self).get_executed_params(execution_context)
|