1
0
forked from VimPlug/jedi

Refactor the mixed objects a bit to make at least some interpreter tests pass.

This commit is contained in:
Dave Halter
2016-12-16 17:17:03 +01:00
parent 575352d4b6
commit 9ac301d0c3
7 changed files with 93 additions and 114 deletions

View File

@@ -6,6 +6,7 @@ import copy
from jedi.cache import underscore_memoization from jedi.cache import underscore_memoization
from jedi.evaluate import helpers from jedi.evaluate import helpers
from jedi.evaluate.representation import ModuleContext from jedi.evaluate.representation import ModuleContext
from jedi.evaluate import compiled
from jedi.evaluate.compiled import mixed from jedi.evaluate.compiled import mixed
from jedi.evaluate.context import Context from jedi.evaluate.context import Context
@@ -25,22 +26,19 @@ class MixedModuleContext(Context):
def get_node(self): def get_node(self):
return self.tree_node return self.tree_node
def names_dicts(self, search_global):
for names_dict in self._module_context.names_dicts(search_global):
yield names_dict
for namespace_obj in self._namespace_objects:
m = mixed.MixedObject(self.evaluator, namespace_obj, self.tree_node.name)
for names_dict in m.names_dicts(False):
yield names_dict
def get_filters(self, *args, **kwargs): def get_filters(self, *args, **kwargs):
for filter in self._module_context.get_filters(*args, **kwargs): for filter in self._module_context.get_filters(*args, **kwargs):
yield filter yield filter
for namespace_obj in self._namespace_objects: for namespace_obj in self._namespace_objects:
m = mixed.MixedObject(self.evaluator, namespace_obj, self.tree_node.name) compiled_object = compiled.create(self.evaluator, namespace_obj)
for filter in m.get_filters(*args, **kwargs): mixed_object = mixed.MixedObject(
self.evaluator,
parent_context=self,
compiled_object=compiled_object,
tree_name=self.tree_node.name
)
for filter in mixed_object.get_filters(*args, **kwargs):
yield filter yield filter
def __getattr__(self, name): def __getattr__(self, name):

View File

@@ -565,7 +565,7 @@ class Evaluator(object):
if node.children[1].type == 'comp_for': if node.children[1].type == 'comp_for':
return node return node
def from_scope_node(scope_node, child_is_funcdef=None): def from_scope_node(scope_node, child_is_funcdef=None, is_nested=True):
if scope_node == base_node: if scope_node == base_node:
return base_context return base_context
@@ -585,7 +585,9 @@ class Evaluator(object):
parent_context, parent_context,
scope_node scope_node
) )
if is_nested:
return func.get_function_execution() return func.get_function_execution()
return func
elif scope_node.type == 'classdef': elif scope_node.type == 'classdef':
class_context = er.ClassContext(self, scope_node, parent_context) class_context = er.ClassContext(self, scope_node, parent_context)
if child_is_funcdef: if child_is_funcdef:
@@ -607,4 +609,4 @@ class Evaluator(object):
# object itself and not its contents. # object itself and not its contents.
node = node.parent node = node.parent
scope_node = parent_scope(node) scope_node = parent_scope(node)
return from_scope_node(scope_node) return from_scope_node(scope_node, is_nested=not node_is_context)

View File

@@ -231,6 +231,12 @@ class CompiledObject(Context):
for part in self.obj: for part in self.obj:
yield LazyKnownContext(create(self.evaluator, part)) yield LazyKnownContext(create(self.evaluator, part))
def py__name__(self):
try:
return self._get_class().__name__
except AttributeError:
return None
@property @property
def name(self): def name(self):
try: try:
@@ -308,67 +314,22 @@ class CompiledContextName(ContextNameMixin, AbstractNameDefinition):
self.parent_context = context.parent_context self.parent_context = context.parent_context
class LazyNamesDict(object):
"""
A names_dict instance for compiled objects, resembles the parser.tree.
"""
name_class = CompiledName
def __init__(self, evaluator, compiled_obj, is_instance=False):
self._evaluator = evaluator
self._compiled_obj = compiled_obj
self._is_instance = is_instance
def __iter__(self):
return (v[0].value for v in self.values())
@memoize_method
def __getitem__(self, name):
try:
getattr(self._compiled_obj.obj, name)
except AttributeError:
raise KeyError('%s in %s not found.' % (name, self._compiled_obj))
except Exception:
# This is a bit ugly. We're basically returning this to make
# lookups possible without having the actual attribute. However
# this makes proper completion possible.
return [FakeName(name, create(self._evaluator, None), is_definition=True)]
return [self.name_class(self._evaluator, self._compiled_obj, name)]
def values(self):
obj = self._compiled_obj.obj
values = []
for name in dir(obj):
try:
values.append(self[name])
except KeyError:
# The dir function can be wrong.
pass
is_instance = self._is_instance or fake.is_class_instance(obj)
# ``dir`` doesn't include the type names.
if not inspect.ismodule(obj) and obj != type and not is_instance:
values += create(self._evaluator, type).names_dict.values()
return values
class CompiledObjectFilter(AbstractFilter): class CompiledObjectFilter(AbstractFilter):
""" """
A names_dict instance for compiled objects, resembles the parser.tree. A names_dict instance for compiled objects, resembles the parser.tree.
""" """
name_class = CompiledName name_class = CompiledName
def __init__(self, evaluator, compiled_obj, is_instance=False): def __init__(self, evaluator, compiled_object, is_instance=False):
super(CompiledObjectFilter, self).__init__(None) super(CompiledObjectFilter, self).__init__(None)
self._evaluator = evaluator self._evaluator = evaluator
self._compiled_obj = compiled_obj self._compiled_object = compiled_object
self._is_instance = is_instance self._is_instance = is_instance
@memoize_method @memoize_method
def get(self, name): def get(self, name):
name = str(name) name = str(name)
obj = self._compiled_obj.obj obj = self._compiled_object.obj
try: try:
getattr(obj, name) getattr(obj, name)
if self._is_instance and name not in dir(obj): if self._is_instance and name not in dir(obj):
@@ -380,10 +341,10 @@ class CompiledObjectFilter(AbstractFilter):
# lookups possible without having the actual attribute. However # lookups possible without having the actual attribute. However
# this makes proper completion possible. # this makes proper completion possible.
return [FakeName(name, create(self._evaluator, None), is_definition=True)] return [FakeName(name, create(self._evaluator, None), is_definition=True)]
return [self._create(name)] return [self._create_name(name)]
def values(self): def values(self):
obj = self._compiled_obj.obj obj = self._compiled_object.obj
names = [] names = []
for name in dir(obj): for name in dir(obj):
@@ -396,8 +357,8 @@ class CompiledObjectFilter(AbstractFilter):
names += filter.values() names += filter.values()
return names return names
def _create(self, name): def _create_name(self, name):
return self.name_class(self._evaluator, self._compiled_obj, name) return self.name_class(self._evaluator, self._compiled_object, name)
def dotted_from_fs_path(fs_path, sys_path): def dotted_from_fs_path(fs_path, sys_path):

View File

@@ -9,6 +9,7 @@ from jedi import common
from jedi.parser.fast import FastParser from jedi.parser.fast import FastParser
from jedi.evaluate import compiled from jedi.evaluate import compiled
from jedi.cache import underscore_memoization from jedi.cache import underscore_memoization
from jedi.evaluate import imports
class MixedObject(object): class MixedObject(object):
@@ -28,67 +29,80 @@ class MixedObject(object):
fewer special cases, because we in Python you don't have the same freedoms fewer special cases, because we in Python you don't have the same freedoms
to modify the runtime. to modify the runtime.
""" """
def __init__(self, evaluator, obj, node_name): def __init__(self, evaluator, parent_context, compiled_object, tree_name):
self._evaluator = evaluator self.evaluator = evaluator
self.obj = obj self.compiled_object = compiled_object
self.node_name = node_name self.obj = compiled_object.obj
self.definition = node_name.get_definition() self._tree_name = tree_name
name_module = tree_name.get_root_node()
if parent_context.get_node().get_root_node() != name_module:
from jedi.evaluate.representation import ModuleContext
module_context = ModuleContext(evaluator, name_module)
name = compiled_object.get_root_context().py__name__()
imports.add_module(evaluator, name, module_context)
else:
module_context = parent_context.get_root_context()
@property self._context = module_context.create_context(
def names_dict(self): tree_name.parent,
return LazyMixedNamesDict(self._evaluator, self) node_is_context=True
)
def names_dicts(self, search_global): def get_filters(self, *args, **kwargs):
# TODO is this needed? yield MixedObjectFilter(self.evaluator, self)
assert search_global is False
return [self.names_dict]
def api_type(self):
mappings = {
'expr_stmt': 'statement',
'classdef': 'class',
'funcdef': 'function',
'file_input': 'module',
}
return mappings[self.definition.type]
def __repr__(self): def __repr__(self):
return '<%s: %s>' % (type(self).__name__, repr(self.obj)) return '<%s: %s>' % (type(self).__name__, repr(self.obj))
def __getattr__(self, name): def __getattr__(self, name):
return getattr(self.definition, name) return getattr(self._context, name)
class MixedName(compiled.CompiledName): class MixedName(compiled.CompiledName):
""" """
The ``CompiledName._compiled_object`` is our MixedObject. The ``CompiledName._compiled_object`` is our MixedObject.
""" """
@property
@underscore_memoization
def parent(self):
return create(self._evaluator, getattr(self._compiled_obj.obj, self.name))
@parent.setter
def parent(self, value):
pass # Just ignore this, Name tries to overwrite the parent attribute.
@property @property
def start_pos(self): def start_pos(self):
if isinstance(self.parent, MixedObject): contexts = list(self.infer())
return self.parent.node_name.start_pos if not contexts:
# This means a start_pos that doesn't exist (compiled objects). # This means a start_pos that doesn't exist (compiled objects).
return (0, 0) return (0, 0)
return contexts[0].name.start_pos
@start_pos.setter @start_pos.setter
def start_pos(self, value): def start_pos(self, value):
# Ignore the __init__'s start_pos setter call. # Ignore the __init__'s start_pos setter call.
pass pass
@underscore_memoization
def infer(self):
obj = self.parent_context.obj
try:
obj = getattr(obj, self.string_name)
except AttributeError:
# Happens e.g. in properties of
# PyQt4.QtGui.QStyleOptionComboBox.currentText
# -> just set it to None
obj = None
return [create(self._evaluator, obj, parent_context=self.parent_context)]
class LazyMixedNamesDict(compiled.LazyNamesDict): @property
def api_type(self):
return next(iter(self.infer())).api_type
class MixedObjectFilter(compiled.CompiledObjectFilter):
name_class = MixedName name_class = MixedName
def __init__(self, evaluator, mixed_object, is_instance=False):
super(MixedObjectFilter, self).__init__(
evaluator, mixed_object, is_instance)
self._mixed_object = mixed_object
#def _create(self, name):
#return MixedName(self._evaluator, self._compiled_object, name)
def parse(grammar, path): def parse(grammar, path):
with open(path) as f: with open(path) as f:
@@ -121,7 +135,7 @@ def find_syntax_node_name(evaluator, python_object):
# We don't need to check names for modules, because there's not really # We don't need to check names for modules, because there's not really
# a way to write a module in a module in Python (and also __name__ can # a way to write a module in a module in Python (and also __name__ can
# be something like ``email.utils``). # be something like ``email.utils``).
return module return module.name
name_str = python_object.__name__ name_str = python_object.__name__
if name_str == '<lambda>': if name_str == '<lambda>':
@@ -159,9 +173,11 @@ def find_syntax_node_name(evaluator, python_object):
@compiled.compiled_objects_cache('mixed_cache') @compiled.compiled_objects_cache('mixed_cache')
def create(evaluator, obj): def create(evaluator, obj, parent_context=None, *args):
name = find_syntax_node_name(evaluator, obj) tree_name = find_syntax_node_name(evaluator, obj)
if name is None:
return compiled.create(evaluator, obj) compiled_object = compiled.create(
else: evaluator, obj, parent_context=parent_context.compiled_object)
return MixedObject(evaluator, obj, name) if tree_name is None:
return compiled_object
return MixedObject(evaluator, parent_context, compiled_object, tree_name)

View File

@@ -2,10 +2,9 @@ from jedi.common import unite
class Context(object): class Context(object):
api_type = 'instance' api_type = None
""" """
Most contexts are just instances of something, therefore make this the To be defined by subclasses.
default to make subclassing a lot easier.
""" """
predefined_names = {} predefined_names = {}

View File

@@ -17,6 +17,8 @@ class AbstractInstanceContext(Context):
""" """
This class is used to evaluate instances. This class is used to evaluate instances.
""" """
api_type = 'instance'
def __init__(self, evaluator, parent_context, class_context, var_args): def __init__(self, evaluator, parent_context, class_context, var_args):
super(AbstractInstanceContext, self).__init__(evaluator, parent_context) super(AbstractInstanceContext, self).__init__(evaluator, parent_context)
# Generated instances are classes that are just generated by self # Generated instances are classes that are just generated by self

View File

@@ -60,7 +60,8 @@ def test_side_effect_completion():
side_effect = get_completion('SideEffectContainer', _GlobalNameSpace.__dict__) side_effect = get_completion('SideEffectContainer', _GlobalNameSpace.__dict__)
# It's a class that contains MixedObject. # It's a class that contains MixedObject.
assert isinstance(side_effect._definition.base, mixed.MixedObject) context, = side_effect._name.infer()
assert isinstance(context, mixed.MixedObject)
foo = get_completion('SideEffectContainer.foo', _GlobalNameSpace.__dict__) foo = get_completion('SideEffectContainer.foo', _GlobalNameSpace.__dict__)
assert foo.name == 'foo' assert foo.name == 'foo'