Function -> FunctionContext and fakes use the FunctionContext, too.

This commit is contained in:
Dave Halter
2016-10-25 09:59:42 +02:00
parent 64b6396d19
commit 90af0c36e0
9 changed files with 72 additions and 54 deletions

View File

@@ -581,7 +581,7 @@ class Definition(use_metaclass(CachedMetaClass, BaseDefinition)):
d = 'class ' + d.type d = 'class ' + d.type
elif isinstance(d, (tree.Class, er.ClassContext, er.Instance)): elif isinstance(d, (tree.Class, er.ClassContext, er.Instance)):
d = 'class ' + unicode(d.name) d = 'class ' + unicode(d.name)
elif isinstance(d, (er.Function, tree.Function)): elif isinstance(d, (er.FunctionContext, tree.Function)):
d = 'def ' + unicode(d.name) d = 'def ' + unicode(d.name)
elif isinstance(d, tree.Module): elif isinstance(d, tree.Module):
# only show module name # only show module name

View File

@@ -118,7 +118,7 @@ class Evaluator(object):
if element.type == 'classdef': if element.type == 'classdef':
return er.ClassContext(self, element, parent_context) return er.ClassContext(self, element, parent_context)
elif element.type == 'funcdef': elif element.type == 'funcdef':
return er.Function(self, parent_context, element) return er.FunctionContext(self, parent_context, element)
elif element.type == 'lambda': elif element.type == 'lambda':
return er.LambdaWrapper(self, element) return er.LambdaWrapper(self, element)
elif element.type == 'file_input': elif element.type == 'file_input':
@@ -306,7 +306,7 @@ class Evaluator(object):
types = self.eval_trailer(context, types, trailer) types = self.eval_trailer(context, types, trailer)
elif element.type in ('testlist_star_expr', 'testlist',): elif element.type in ('testlist_star_expr', 'testlist',):
# The implicit tuple in statements. # The implicit tuple in statements.
types = set([iterable.ImplicitTuple(self, element)]) types = set([iterable.ArrayLiteralContext(self, context, element)])
elif element.type in ('not_test', 'factor'): elif element.type in ('not_test', 'factor'):
types = self.eval_element(context, element.children[-1]) types = self.eval_element(context, element.children[-1])
for operator in element.children[:-1]: for operator in element.children[:-1]:
@@ -406,13 +406,6 @@ class Evaluator(object):
new_types |= self.execute(typ, arguments) new_types |= self.execute(typ, arguments)
return new_types return new_types
def execute_evaluated(self, obj, *args):
"""
Execute a function with already executed arguments.
"""
args = [iterable.AlreadyEvaluated([arg]) for arg in args]
return self.execute(obj, args)
@debug.increase_indent @debug.increase_indent
def execute(self, obj, arguments=None): def execute(self, obj, arguments=None):
if not isinstance(arguments, param.Arguments): if not isinstance(arguments, param.Arguments):
@@ -422,7 +415,7 @@ class Evaluator(object):
if self.is_analysis: if self.is_analysis:
arguments.eval_all() arguments.eval_all()
if isinstance(obj, er.Function): if isinstance(obj, er.FunctionContext):
obj = obj.get_decorated_func() obj = obj.get_decorated_func()
debug.dbg('execute: %s %s', obj, arguments) debug.dbg('execute: %s %s', obj, arguments)

View File

@@ -238,12 +238,13 @@ class CompiledObject(Context):
Returns only the faked scopes - the other ones are not important for Returns only the faked scopes - the other ones are not important for
internal analysis. internal analysis.
""" """
raise NotImplementedError
module = self.get_parent_until() module = self.get_parent_until()
faked_subscopes = [] faked_subscopes = []
for name in dir(self.obj): for name in dir(self.obj):
try: try:
faked_subscopes.append( faked_subscopes.append(
fake.get_faked(module, self.obj, parent=self, name=name) fake.get_faked(self._evaluator, module, self.obj, parent=self, name=name)
) )
except fake.FakeDoesNotExist: except fake.FakeDoesNotExist:
pass pass
@@ -512,20 +513,21 @@ def _parse_function_doc(doc):
return param_str, ret return param_str, ret
def _create_from_name(evaluator, module, parent, name): def _create_from_name(evaluator, module, compiled_object, name):
obj = compiled_object.obj
try: try:
return fake.get_faked(module, parent.obj, parent=parent, name=name) return fake.get_faked(evaluator, module, obj, parent_context=compiled_object, name=name)
except fake.FakeDoesNotExist: except fake.FakeDoesNotExist:
pass pass
try: try:
obj = getattr(parent.obj, name) obj = getattr(obj, name)
except AttributeError: except AttributeError:
# Happens e.g. in properties of # Happens e.g. in properties of
# PyQt4.QtGui.QStyleOptionComboBox.currentText # PyQt4.QtGui.QStyleOptionComboBox.currentText
# -> just set it to None # -> just set it to None
obj = None obj = None
return create(evaluator, obj, parent) return create(evaluator, obj, parent_context=compiled_object)
def builtin_from_name(evaluator, string): def builtin_from_name(evaluator, string):
@@ -550,7 +552,7 @@ _SPECIAL_OBJECTS = {
def get_special_object(evaluator, identifier): def get_special_object(evaluator, identifier):
obj = _SPECIAL_OBJECTS[identifier] obj = _SPECIAL_OBJECTS[identifier]
return create(evaluator, obj, parent=create(evaluator, _builtins)) return create(evaluator, obj, parent_context=create(evaluator, _builtins))
def compiled_objects_cache(attribute_name): def compiled_objects_cache(attribute_name):
@@ -560,21 +562,21 @@ def compiled_objects_cache(attribute_name):
Caching the id has the advantage that an object doesn't need to be Caching the id has the advantage that an object doesn't need to be
hashable. hashable.
""" """
def wrapper(evaluator, obj, parent=None, module=None): def wrapper(evaluator, obj, parent_context=None, module=None):
cache = getattr(evaluator, attribute_name) cache = getattr(evaluator, attribute_name)
# Do a very cheap form of caching here. # Do a very cheap form of caching here.
key = id(obj), id(parent) key = id(obj), id(parent_context)
try: try:
return cache[key][0] return cache[key][0]
except KeyError: except KeyError:
# TODO this whole decorator looks way too ugly and this if # TODO this whole decorator looks way too ugly and this if
# doesn't make it better. Find a more generic solution. # doesn't make it better. Find a more generic solution.
if parent or module: if parent_context or module:
result = func(evaluator, obj, parent, module) result = func(evaluator, obj, parent_context, module)
else: else:
result = func(evaluator, obj) result = func(evaluator, obj)
# Need to cache all of them, otherwise the id could be overwritten. # Need to cache all of them, otherwise the id could be overwritten.
cache[key] = result, obj, parent, module cache[key] = result, obj, parent_context, module
return result return result
return wrapper return wrapper
@@ -582,22 +584,22 @@ def compiled_objects_cache(attribute_name):
@compiled_objects_cache('compiled_cache') @compiled_objects_cache('compiled_cache')
def create(evaluator, obj, parent=None, module=None): def create(evaluator, obj, parent_context=None, module=None):
""" """
A very weird interface class to this module. The more options provided the A very weird interface class to this module. The more options provided the
more acurate loading compiled objects is. more acurate loading compiled objects is.
""" """
if inspect.ismodule(obj): if inspect.ismodule(obj):
if parent is not None: if parent_context is not None:
# Modules don't have parents, be careful with caching: recurse. # Modules don't have parents, be careful with caching: recurse.
return create(evaluator, obj) return create(evaluator, obj)
else: else:
if parent is None and obj != _builtins: if parent_context is None and obj != _builtins:
return create(evaluator, obj, create(evaluator, _builtins)) return create(evaluator, obj, create(evaluator, _builtins))
try: try:
return fake.get_faked(module, obj, parent=parent) return fake.get_faked(evaluator, module, obj, parent_context=parent_context)
except fake.FakeDoesNotExist: except fake.FakeDoesNotExist:
pass pass
return CompiledObject(evaluator, obj, parent) return CompiledObject(evaluator, obj, parent_context)

View File

@@ -172,12 +172,13 @@ def memoize_faked(obj):
def _get_faked(module, obj, name=None): def _get_faked(module, obj, name=None):
obj = type(obj) if is_class_instance(obj) else obj obj = type(obj) if is_class_instance(obj) else obj
result, fake_module = _faked(module, obj, name) result, fake_module = _faked(module, obj, name)
if result is None or isinstance(result, pt.Class): if result is None or result.type == 'classdef':
# We're not interested in classes. What we want is functions. # We're not interested in classes. What we want is functions.
raise FakeDoesNotExist raise FakeDoesNotExist
else: else:
# Set the docstr which was previously not set (faked modules don't # Set the docstr which was previously not set (faked modules don't
# contain it). # contain it).
assert result.type == 'funcdef'
doc = '"""%s"""' % obj.__doc__ # TODO need escapes. doc = '"""%s"""' % obj.__doc__ # TODO need escapes.
suite = result.children[-1] suite = result.children[-1]
string = pt.String(doc, (0, 0), '') string = pt.String(doc, (0, 0), '')
@@ -187,12 +188,12 @@ def _get_faked(module, obj, name=None):
return result, fake_module return result, fake_module
def get_faked(module, obj, name=None, parent=None): def get_faked(evaluator, module, obj, name=None, parent_context=None):
faked, fake_module = _get_faked(module and module.obj, obj, name) faked, fake_module = _get_faked(module and module.obj, obj, name)
faked.parent = parent
if module is not None: if module is not None:
module.used_names = fake_module.used_names module.used_names = fake_module.used_names
return faked from jedi.evaluate.representation import FunctionContext
return FunctionContext(evaluator, parent_context, faked)
def is_class_instance(obj): def is_class_instance(obj):

View File

@@ -18,6 +18,15 @@ class Context(object):
def execute(self, arguments=None): def execute(self, arguments=None):
return self._evaluator.execute(self, arguments) return self._evaluator.execute(self, arguments)
def execute_evaluated(self, *args):
"""
Execute a function with already executed arguments.
"""
from jedi.evaluate.iterable import AlreadyEvaluated
# TODO UGLY
args = [AlreadyEvaluated([arg]) for arg in args]
return self.execute(args)
class TreeContext(Context): class TreeContext(Context):
def eval_node(self, node): def eval_node(self, node):

View File

@@ -6,7 +6,7 @@ from abc import abstractmethod
from jedi.parser.tree import search_ancestor from jedi.parser.tree import search_ancestor
from jedi.evaluate import flow_analysis from jedi.evaluate import flow_analysis
from jedi.common import to_list from jedi.common import to_list, unite
class AbstractNameDefinition(): class AbstractNameDefinition():
@@ -28,6 +28,12 @@ class AbstractNameDefinition():
return '<%s: %s>' % (type(self).__name__, self.string_name) return '<%s: %s>' % (type(self).__name__, self.string_name)
return '<%s: %s@%s>' % (type(self).__name__, self.string_name, self.start_pos) return '<%s: %s@%s>' % (type(self).__name__, self.string_name, self.start_pos)
def execute(self, arguments):
return unite(context.execute(arguments) for context in self.infer())
def execute_evaluated(self, *args, **kwargs):
return unite(context.execute(*args, **kwargs) for context in self.infer())
class ContextName(AbstractNameDefinition): class ContextName(AbstractNameDefinition):
def __init__(self, parent_context, name): def __init__(self, parent_context, name):

View File

@@ -1,5 +1,6 @@
from abc import abstractproperty from abc import abstractproperty
from jedi.common import unite
from jedi import debug from jedi import debug
from jedi.evaluate import compiled from jedi.evaluate import compiled
from jedi.evaluate.filters import ParserTreeFilter, ContextName from jedi.evaluate.filters import ParserTreeFilter, ContextName
@@ -45,11 +46,8 @@ class AbstractInstanceContext(Context):
raise AttributeError raise AttributeError
def execute(arguments): def execute(arguments):
contexts = set() return unite(name.execute(arguments) for name in names)
for name in names:
for context in name.infer():
context.execute(arguments)
return contexts
return execute return execute
def py__class__(self): def py__class__(self):
@@ -103,13 +101,13 @@ class AbstractInstanceContext(Context):
def py__getitem__(self, index): def py__getitem__(self, index):
try: try:
method = self.get_subscope_by_name('__getitem__') names = self._get_function_slot_names('__getitem__')
except KeyError: except KeyError:
debug.warning('No __getitem__, cannot access the array.') debug.warning('No __getitem__, cannot access the array.')
return set() return set()
else: else:
index_obj = compiled.create(self._evaluator, index) index_obj = compiled.create(self._evaluator, index)
return self._evaluator.execute_evaluated(method, index_obj) return unite(name.execute_evaluated(index_obj) for name in names)
def py__iter__(self): def py__iter__(self):
try: try:

View File

@@ -39,6 +39,12 @@ class AbstractArrayContext(Context):
def get_filters(self, search_global, until_position=None, origin_scope=None): def get_filters(self, search_global, until_position=None, origin_scope=None):
raise NotImplementedError raise NotImplementedError
@property
def name(self):
raise NotImplementedError
#return compiled.CompiledContextName(
return helpers.FakeName(self.type, parent=self)
class IterableWrapper(tree.Base): class IterableWrapper(tree.Base):
def is_class(self): def is_class(self):
@@ -354,8 +360,12 @@ class ArrayLiteralContext(AbstractArrayContext, ArrayMixin):
def __init__(self, evaluator, parent_context, atom): def __init__(self, evaluator, parent_context, atom):
super(ArrayLiteralContext, self).__init__(evaluator, parent_context) super(ArrayLiteralContext, self).__init__(evaluator, parent_context)
self.atom = atom self.atom = atom
self._array_type = ArrayLiteralContext.mapping[atom.children[0]]
"""The builtin name of the array (list, set, tuple or dict).""" if self.atom.type in ('testlist_star_expr', 'testlist'):
self._array_type = 'tuple'
else:
self._array_type = ArrayLiteralContext.mapping[atom.children[0]]
"""The builtin name of the array (list, set, tuple or dict)."""
c = self.atom.children c = self.atom.children
array_node = c[1] array_node = c[1]
@@ -363,12 +373,6 @@ class ArrayLiteralContext(AbstractArrayContext, ArrayMixin):
and (not hasattr(array_node, 'children') or ':' not in array_node.children): and (not hasattr(array_node, 'children') or ':' not in array_node.children):
self._array_type = 'set' self._array_type = 'set'
@property
def name(self):
raise NotImplementedError
#return compiled.CompiledContextName(
return helpers.FakeName(self.type, parent=self)
def py__getitem__(self, index): def py__getitem__(self, index):
"""Here the index is an int/str. Raises IndexError/KeyError.""" """Here the index is an int/str. Raises IndexError/KeyError."""
if self.type == 'dict': if self.type == 'dict':
@@ -423,6 +427,10 @@ class ArrayLiteralContext(AbstractArrayContext, ArrayMixin):
def _items(self): def _items(self):
c = self.atom.children c = self.atom.children
if self.atom.type in ('testlist_star_expr', 'testlist'):
return c[::2]
array_node = c[1] array_node = c[1]
if array_node in (']', '}', ')'): if array_node in (']', '}', ')'):
return [] # Direct closing bracket, doesn't contain items. return [] # Direct closing bracket, doesn't contain items.
@@ -458,6 +466,7 @@ class _FakeArray(ArrayLiteralContext):
class ImplicitTuple(_FakeArray): class ImplicitTuple(_FakeArray):
def __init__(self, evaluator, testlist): def __init__(self, evaluator, testlist):
super(ImplicitTuple, self).__init__(evaluator, testlist, 'tuple') super(ImplicitTuple, self).__init__(evaluator, testlist, 'tuple')
raise NotImplementedError
self._testlist = testlist self._testlist = testlist
def _items(self): def _items(self):

View File

@@ -385,7 +385,7 @@ class InstanceElement(use_metaclass(CachedMetaClass, tree.Base)):
# more complicated than we would it actually like to be. # more complicated than we would it actually like to be.
return self.var.py__call__(params) return self.var.py__call__(params)
else: else:
return Function.py__call__(self, params) return FunctionContext.py__call__(self, params)
def __repr__(self): def __repr__(self):
return "<%s of %s>" % (type(self).__name__, self.var) return "<%s of %s>" % (type(self).__name__, self.var)
@@ -518,13 +518,13 @@ class ClassContext(use_metaclass(CachedMetaClass, TreeContext, Wrapper)):
return "<e%s of %s>" % (type(self).__name__, self.base) return "<e%s of %s>" % (type(self).__name__, self.base)
class Function(use_metaclass(CachedMetaClass, TreeContext, Wrapper)): class FunctionContext(use_metaclass(CachedMetaClass, TreeContext, Wrapper)):
""" """
Needed because of decorators. Decorators are evaluated here. Needed because of decorators. Decorators are evaluated here.
""" """
def __init__(self, evaluator, parent_context, func, is_decorated=False): def __init__(self, evaluator, parent_context, func, is_decorated=False):
""" This should not be called directly """ """ This should not be called directly """
super(Function, self).__init__(evaluator, parent_context) super(FunctionContext, self).__init__(evaluator, parent_context)
self.base = self.base_func = func self.base = self.base_func = func
self.is_decorated = is_decorated self.is_decorated = is_decorated
# A property that is set by the decorator resolution. # A property that is set by the decorator resolution.
@@ -563,10 +563,10 @@ class Function(use_metaclass(CachedMetaClass, TreeContext, Wrapper)):
self.base_func, dec_results) self.base_func, dec_results)
# Create param array. # Create param array.
if isinstance(f, Function): if isinstance(f, FunctionContext):
old_func = f # TODO this is just hacky. change. old_func = f # TODO this is just hacky. change.
elif f.type == 'funcdef': elif f.type == 'funcdef':
old_func = Function(self._evaluator, f, is_decorated=True) old_func = FunctionContext(self._evaluator, f, is_decorated=True)
else: else:
old_func = f old_func = f
@@ -579,7 +579,7 @@ class Function(use_metaclass(CachedMetaClass, TreeContext, Wrapper)):
debug.warning('multiple wrappers found %s %s', debug.warning('multiple wrappers found %s %s',
self.base_func, wrappers) self.base_func, wrappers)
f = list(wrappers)[0] f = list(wrappers)[0]
if isinstance(f, (Instance, Function)): if isinstance(f, (Instance, FunctionContext)):
f.decorates = self f.decorates = self
debug.dbg('decorator end %s', f) debug.dbg('decorator end %s', f)
@@ -629,7 +629,7 @@ class Function(use_metaclass(CachedMetaClass, TreeContext, Wrapper)):
return "<e%s of %s%s>" % (type(self).__name__, self.base_func, dec) return "<e%s of %s%s>" % (type(self).__name__, self.base_func, dec)
class LambdaWrapper(Function): class LambdaWrapper(FunctionContext):
def get_decorated_func(self): def get_decorated_func(self):
return self return self