1
0
forked from VimPlug/jedi

Fix __call__.

This commit is contained in:
Dave Halter
2016-10-24 00:39:59 +02:00
parent 0475bb5fd0
commit e34246eb00
12 changed files with 298 additions and 112 deletions

View File

@@ -416,6 +416,7 @@ class Evaluator(object):
@debug.increase_indent
def execute(self, obj, arguments=None):
if not isinstance(arguments, param.Arguments):
raise NotImplementedError
arguments = param.Arguments(self, arguments)
if self.is_analysis:

View File

@@ -55,9 +55,8 @@ class CompiledObject(Context):
@CheckAttribute
def py__call__(self, params):
if inspect.isclass(self.obj):
from jedi.evaluate.representation import Instance
return set([self])
return set([Instance(self._evaluator, self.parent_context, self, params)])
from jedi.evaluate.instance import CompiledInstance
return set([CompiledInstance(self._evaluator, self.parent_context, self, params)])
else:
return set(self._execute_function(params))
@@ -261,22 +260,22 @@ class CompiledObject(Context):
class CompiledName(AbstractNameDefinition):
def __init__(self, evaluator, compiled_obj, name):
def __init__(self, evaluator, parent_context, name):
self._evaluator = evaluator
self._compiled_obj = compiled_obj
self.parent_context = parent_context
self.string_name = name
def __repr__(self):
try:
name = self._compiled_obj.name # __name__ is not defined all the time
name = self.parent_context.name # __name__ is not defined all the time
except AttributeError:
name = None
return '<%s: (%s).%s>' % (type(self).__name__, name, self.string_name)
@underscore_memoization
def infer(self):
module = self._compiled_obj.get_root_context()
return [_create_from_name(self._evaluator, module, self._compiled_obj, self.string_name)]
module = self.parent_context.get_root_context()
return [_create_from_name(self._evaluator, module, self.parent_context, self.string_name)]
class CompiledContextName(AbstractNameDefinition):

30
jedi/evaluate/context.py Normal file
View File

@@ -0,0 +1,30 @@
class Context(object):
type = None # TODO remove
def __init__(self, evaluator, parent_context=None):
self._evaluator = evaluator
self.parent_context = parent_context
def get_parent_flow_context(self):
return self.parent_context
def get_root_context(self):
context = self
while True:
if context.parent_context is None:
return context
context = context.parent_context
def execute(self, arguments=None):
return self._evaluator.execute(self, arguments)
class TreeContext(Context):
def eval_node(self, node):
return self._evaluator.eval_element(self, node)
class FlowContext(TreeContext):
def get_parent_flow_context(self):
if 1:
return self.parent_context

View File

@@ -9,7 +9,7 @@ from jedi.evaluate import flow_analysis
from jedi.common import to_list
class AbstractNameDefinition(object):
class AbstractNameDefinition():
start_pos = None
string_name = None
parent_context = None
@@ -108,8 +108,8 @@ class AbstractUsedNamesFilter(AbstractFilter):
return [TreeNameDefinition(self._context, name) for name in names]
def values(self):
return self._convert_to_names(name for name_list in self._used_names.values()
for name in self._filter(name_list))
return self._convert_names(name for name_list in self._used_names.values()
for name in self._filter(name_list))
class ParserTreeFilter(AbstractUsedNamesFilter):
@@ -150,6 +150,7 @@ class FunctionExecutionFilter(ParserTreeFilter):
origin_scope
)
@to_list
def _convert_names(self, names):
for name in names:
param = search_ancestor(name, 'param')

View File

@@ -442,7 +442,7 @@ def _eval_param(evaluator, context, param, scope):
res_new.add(scope.instance)
else:
inst = er.Instance(evaluator, context.parent_context.parent_context, context.parent_context,
Arguments(evaluator, context, ()),
Arguments(evaluator, context),
is_generated=True)
res_new.add(inst)
return res_new

221
jedi/evaluate/instance.py Normal file
View 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)

View File

@@ -680,7 +680,7 @@ def _check_array_additions(evaluator, compare_array, module, is_list):
return set()
def check_additions(arglist, add_name):
params = list(param.Arguments(evaluator, arglist).unpack())
params = list(param.Arguments(evaluator, context, arglist).unpack())
result = set()
if add_name in ['insert']:
params = params[1:]

View File

@@ -28,7 +28,7 @@ def try_iter_content(types, depth=0):
class Arguments(tree.Base):
def __init__(self, evaluator, context, argument_node, trailer):
def __init__(self, evaluator, context, argument_node, trailer=None):
"""
The argument_node is either a parser node or a list of evaluated
objects. Those evaluated objects may be lists of evaluated objects

View File

@@ -58,6 +58,7 @@ from jedi.evaluate import imports
from jedi.evaluate.filters import ParserTreeFilter, FunctionExecutionFilter, \
GlobalNameFilter, DictFilter, ContextName
from jedi.evaluate.context import TreeContext
from jedi.evaluate.instance import TreeInstance
class Executed(TreeContext):
@@ -77,15 +78,15 @@ class Instance(use_metaclass(CachedMetaClass, Executed)):
"""
This class is used to evaluate instances.
"""
def __init__(self, evaluator, parent_context, base, var_args, is_generated=False):
super(Instance, self).__init__(evaluator, parent_context, base, var_args)
self.decorates = None
def __init__(self, evaluator, parent_context, class_context, var_args, is_generated=False):
super(Instance, self).__init__(evaluator, parent_context, var_args)
# Generated instances are classes that are just generated by self
# (No var_args) used.
self.is_generated = is_generated
self._class_context = class_context
self._init_execution = None
if base.name.string_name in ['list', 'set'] \
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)
@@ -116,7 +117,7 @@ class Instance(use_metaclass(CachedMetaClass, Executed)):
return actual
def py__class__(self):
return self.base
return self.class_context
def py__bool__(self):
# Signalize that we don't know about the bool type.
@@ -174,7 +175,7 @@ class Instance(use_metaclass(CachedMetaClass, Executed)):
return names
def get_subscope_by_name(self, name):
sub = self.base.get_subscope_by_name(name)
sub = self._class_context.get_subscope_by_name(name)
return get_instance_el(self._evaluator, self, sub, True)
def execute_subscope_by_name(self, name, *args):
@@ -196,7 +197,7 @@ class Instance(use_metaclass(CachedMetaClass, Executed)):
def names_dicts(self, search_global):
yield self._self_names_dict()
for s in self.base.py__mro__()[1:]:
for s in self._class_context.py__mro__()[1:]:
if not isinstance(s, compiled.CompiledObject):
# Compiled objects don't have `self.` names.
for inst in self._evaluator.execute(s):
@@ -206,13 +207,13 @@ class Instance(use_metaclass(CachedMetaClass, Executed)):
yield LazyInstanceDict(self._evaluator, self, names_dict)
def get_filters(self, search_global, until_position=None, origin_scope=None):
for cls in self.base.py__mro__():
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.base.py__mro__():
for cls in self._class_context.py__mro__():
if isinstance(cls, compiled.CompiledObject):
yield CompiledInstanceClassFilter(self._evaluator, self, cls)
else:
@@ -251,83 +252,11 @@ class Instance(use_metaclass(CachedMetaClass, Executed)):
@property
@underscore_memoization
def name(self):
return ContextName(self, self.base.name)
return ContextName(self, self._class_context.name.string_name)
def __repr__(self):
dec = ''
if self.decorates is not None:
dec = " decorates " + repr(self.decorates)
return "<%s of %s(%s)%s>" % (type(self).__name__, self.base,
self.var_args, dec)
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)
return "<%s of %s(%s)>" % (type(self).__name__, self._class_context,
self.var_args)
class LazyInstanceDict(object):
@@ -535,7 +464,7 @@ class ClassContext(use_metaclass(CachedMetaClass, TreeContext, Wrapper)):
return [compiled.create(self._evaluator, object)]
def py__call__(self, params):
return set([Instance(self._evaluator, self.parent_context, self, params)])
return set([TreeInstance(self._evaluator, self.parent_context, self, params)])
def py__class__(self):
return compiled.create(self._evaluator, type)
@@ -864,6 +793,7 @@ class ModuleContext(use_metaclass(CachedMetaClass, TreeContext, Wrapper)):
self._evaluator = evaluator
self.base = self._module = module
self._parent_module = parent_module
self.path = None
def names_dicts(self, search_global):
yield self.base.names_dict