forked from VimPlug/jedi
Fix __call__.
This commit is contained in:
221
jedi/evaluate/instance.py
Normal file
221
jedi/evaluate/instance.py
Normal file
@@ -0,0 +1,221 @@
|
||||
from abc import abstractproperty
|
||||
|
||||
from jedi import debug
|
||||
from jedi.evaluate import compiled
|
||||
from jedi.evaluate.filters import ParserTreeFilter, ContextName
|
||||
from jedi.evaluate.context import Context
|
||||
|
||||
|
||||
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):
|
||||
contexts = set()
|
||||
for name in names:
|
||||
for context in name.infer():
|
||||
context.execute(arguments)
|
||||
return contexts
|
||||
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_slot(self, name, *args):
|
||||
raise NotImplementedError
|
||||
method = self.get_subscope_by_name(name)
|
||||
return self._evaluator.execute_evaluated(method, *args)
|
||||
|
||||
def get_descriptor_returns(self, obj):
|
||||
""" Throws a KeyError if there's no method. """
|
||||
raise NotImplementedError
|
||||
# Arguments in __get__ descriptors are obj, class.
|
||||
# `method` is the new parent of the array, don't know if that's good.
|
||||
none_obj = compiled.create(self._evaluator, None)
|
||||
args = [obj, obj.base] if isinstance(obj, Instance) else [none_obj, obj]
|
||||
try:
|
||||
return self.execute_subscope_by_name('__get__', *args)
|
||||
except KeyError:
|
||||
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 SelfNameFilter(self._evaluator, self, cls, origin_scope)
|
||||
else:
|
||||
yield SelfNameFilter(self._evaluator, self, cls.base, 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.base, origin_scope)
|
||||
|
||||
def py__getitem__(self, index):
|
||||
try:
|
||||
method = self.get_subscope_by_name('__getitem__')
|
||||
except KeyError:
|
||||
debug.warning('No __getitem__, cannot access the array.')
|
||||
return set()
|
||||
else:
|
||||
index_obj = compiled.create(self._evaluator, index)
|
||||
return self._evaluator.execute_evaluated(method, index_obj)
|
||||
|
||||
def py__iter__(self):
|
||||
try:
|
||||
method = self.get_subscope_by_name('__iter__')
|
||||
except KeyError:
|
||||
debug.warning('No __iter__ on %s.' % self)
|
||||
return
|
||||
else:
|
||||
iters = self._evaluator.execute(method)
|
||||
for generator in iters:
|
||||
if isinstance(generator, Instance):
|
||||
# `__next__` logic.
|
||||
name = '__next__' if is_py3 else 'next'
|
||||
try:
|
||||
yield generator.execute_subscope_by_name(name)
|
||||
except KeyError:
|
||||
debug.warning('Instance has no __next__ function in %s.', generator)
|
||||
else:
|
||||
for typ in generator.py__iter__():
|
||||
yield typ
|
||||
|
||||
@abstractproperty
|
||||
def name(self):
|
||||
pass
|
||||
|
||||
def __repr__(self):
|
||||
return "<%s of %s(%s)>" % (type(self).__name__, self._class_context,
|
||||
self.var_args)
|
||||
|
||||
|
||||
class CompiledInstance(AbstractInstanceContext):
|
||||
@property
|
||||
def name(self):
|
||||
return compiled.CompiledContextName(self, self._class_context.name.string_name)
|
||||
|
||||
|
||||
class TreeInstance(AbstractInstanceContext):
|
||||
@property
|
||||
def name(self):
|
||||
return ContextName(self, self._class_context.name)
|
||||
|
||||
|
||||
class CompiledInstanceClassFilter(compiled.CompiledObjectFilter):
|
||||
def __init__(self, evaluator, instance, compiled_object):
|
||||
super(CompiledInstanceClassFilter, self).__init__(
|
||||
evaluator,
|
||||
compiled_object,
|
||||
is_instance=True,
|
||||
)
|
||||
self._instance = instance
|
||||
|
||||
def _filter(self, names):
|
||||
names = super(CompiledInstanceClassFilter, self)._filter(names)
|
||||
return [get_instance_el(self._evaluator, self._instance, name, True)
|
||||
for name in names]
|
||||
|
||||
|
||||
class InstanceClassFilter(ParserTreeFilter):
|
||||
def __init__(self, evaluator, context, parser_scope, origin_scope):
|
||||
super(InstanceClassFilter, self).__init__(
|
||||
evaluator=evaluator,
|
||||
context=context,
|
||||
parser_scope=parser_scope,
|
||||
origin_scope=origin_scope
|
||||
)
|
||||
|
||||
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 [get_instance_el(self._evaluator, self._context, name, True)
|
||||
for name in names if self._access_possible(name)]
|
||||
|
||||
def _check_flows(self, names):
|
||||
return names
|
||||
|
||||
|
||||
class SelfNameFilter(InstanceClassFilter):
|
||||
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 []
|
||||
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 tree.is_node(trailer, 'trailer') \
|
||||
and len(trailer.children) == 2 \
|
||||
and trailer.children[0] == '.':
|
||||
if name.is_definition() and self._access_possible(name):
|
||||
init_execution = self._context._get_init_execution()
|
||||
# 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 get_instance_el(self._evaluator, self._context, name)
|
||||
Reference in New Issue
Block a user