Trying to start implementing instance filters.

This commit is contained in:
Dave Halter
2016-10-06 16:35:53 +02:00
parent 2f1e9d634f
commit 37ba971787
6 changed files with 149 additions and 87 deletions

View File

@@ -137,10 +137,10 @@ class Evaluator(object):
:return: List of Names. Their parents are the types.
"""
f = finder.NameFinder(self, scope, name_str, position)
scopes = f.scopes(search_global)
filters = f.get_filters(search_global)
if is_goto:
return f.filter_name(scopes)
return f.find(scopes, attribute_lookup=not search_global)
return f.filter_name(filters)
return f.find(filters, attribute_lookup=not search_global)
#@memoize_default(default=[], evaluator_is_first_arg=True)
#@recursion.recursion_decorator

View File

@@ -157,7 +157,7 @@ class CompiledObject(Base):
def names_dicts(self, search_global, is_instance=False):
return self._names_dict_ensure_one_dict(is_instance)
def get_filters(self, search_global, is_instance=False):
def get_filters(self, search_global, is_instance=False, until_position=None):
yield self._ensure_one_filter(is_instance)
@memoize_method
@@ -237,7 +237,7 @@ class CompiledObject(Base):
for name in dir(self.obj):
try:
faked_subscopes.append(
fake.get_faked(module.obj, self.obj, parent=self, name=name)
fake.get_faked(module, self.obj, parent=self, name=name)
)
except fake.FakeDoesNotExist:
pass
@@ -332,13 +332,14 @@ class CompiledObjectFilter(AbstractFilter):
"""
name_class = CompiledName
def __init__(self, evaluator, compiled_obj, is_instance=False):
def __init__(self, evaluator, compiled_obj, origin_scope, is_instance=False):
super(CompiledObjectFilter, self).__init__(origin_scope)
self._evaluator = evaluator
self._compiled_obj = compiled_obj
self._is_instance = is_instance
@memoize_method
def get(self, name, until_position=None):
def get(self, name):
name = str(name)
try:
getattr(self._compiled_obj.obj, name)
@@ -351,7 +352,7 @@ class CompiledObjectFilter(AbstractFilter):
return [FakeName(name, create(self._evaluator, None), is_definition=True)]
return [self.name_class(self._evaluator, self._compiled_obj, name)]
def values(self, until_position=None):
def values(self):
raise NotImplementedError
obj = self._compiled_obj.obj
@@ -511,7 +512,7 @@ def _parse_function_doc(doc):
def _create_from_name(evaluator, module, parent, name):
try:
return fake.get_faked(module.obj, parent.obj, parent=parent, name=name)
return fake.get_faked(module, parent.obj, parent=parent, name=name)
except fake.FakeDoesNotExist:
pass
@@ -593,7 +594,7 @@ def create(evaluator, obj, parent=None, module=None):
return create(evaluator, obj, create(evaluator, _builtins))
try:
return fake.get_faked(module and module.obj, obj, parent=parent)
return fake.get_faked(module, obj, parent=parent)
except fake.FakeDoesNotExist:
pass

View File

@@ -114,36 +114,39 @@ def _faked(module, obj, name):
faked_mod = _load_faked_module(module)
if faked_mod is None:
return None
return None, None
module.used_names = faked_mod.used_names
# Having the module as a `parser.representation.module`, we need to scan
# for methods.
if name is None:
if inspect.isbuiltin(obj):
return search_scope(faked_mod, obj.__name__)
return search_scope(faked_mod, obj.__name__), faked_mod
elif not inspect.isclass(obj):
# object is a method or descriptor
try:
objclass = obj.__objclass__
except AttributeError:
return None
return None, None
else:
cls = search_scope(faked_mod, objclass.__name__)
if cls is None:
return None
return search_scope(cls, obj.__name__)
return None, None
return search_scope(cls, obj.__name__), faked_mod
else:
if obj == module:
return search_scope(faked_mod, name)
return search_scope(faked_mod, name), faked_mod
else:
try:
cls_name = obj.__name__
except AttributeError:
return None
return None, None
cls = search_scope(faked_mod, cls_name)
if cls is None:
return None
return search_scope(cls, name)
return None, None
return search_scope(cls, name), faked_mod
return None, None
def memoize_faked(obj):
@@ -171,7 +174,7 @@ def memoize_faked(obj):
@memoize_faked
def _get_faked(module, obj, name=None):
obj = type(obj) if is_class_instance(obj) else obj
result = _faked(module, obj, name)
result, fake_module = _faked(module, obj, name)
if result is None or isinstance(result, pt.Class):
# We're not interested in classes. What we want is functions.
raise FakeDoesNotExist
@@ -184,12 +187,13 @@ def _get_faked(module, obj, name=None):
new_line = pt.Newline('\n', (0, 0))
docstr_node = pt.Node('simple_stmt', [string, new_line])
suite.children.insert(1, docstr_node)
return result
return result, fake_module
def get_faked(module, obj, name=None, parent=None):
faked = _get_faked(module, obj, name)
faked, fake_module = _get_faked(module and module.obj, obj, name)
faked.parent = parent
module.used_names = fake_module.used_names
return faked

View File

@@ -5,60 +5,103 @@ are needed for name resolution.
from abc import abstractmethod
from jedi.parser.tree import search_ancestor
def filter_scope_names(names, scope, until_position=None, name=None):
return names
from jedi.evaluate import flow_analysis
class AbstractFilter(object):
def _filter(self, names, until_position):
if until_position is not None:
return [n for n in names if n.start_pos < until_position]
def __init__(self, origin_scope=None):
self._origin_scope = origin_scope
_until_position = None
def _filter(self, names):
if self._until_position is not None:
return [n for n in names if n.start_pos < self._until_position]
return names
@abstractmethod
def get(self, name, until_position=None):
def get(self, name):
pass
@abstractmethod
def values(self, until_position=None):
def values(self):
pass
class ParserTreeFilter(AbstractFilter):
def __init__(self, parser_scope):
def __init__(self, parser_scope, until_position=None, origin_scope=None):
super(ParserTreeFilter, self).__init__(origin_scope)
self._parser_scope = parser_scope
self._used_names = self._parser_scope.get_root_node().used_names
self._until_position = until_position
self._origin_scope = origin_scope
def _filter(self, names, until_position):
names = super(ParserTreeFilter, self)._filter(names, until_position)
def _filter(self, names):
names = super(ParserTreeFilter, self)._filter(names)
names = [n for n in names if n.is_definition()]
names = [n for n in names if n.parent.get_parent_scope() == self._parser_scope]
return names
def get(self, name, until_position=None):
return list(self._check_flows(sorted(names, key=lambda name: name.start_pos)))
def _check_flows(self, names):
for name in names:
yield name
continue
stmt = name.get_definition()
name_scope = self._evaluator.wrap(stmt.get_parent_scope())
check = flow_analysis.break_check(self._evaluator, name_scope,
stmt, self._origin_scope)
if check is not flow_analysis.UNREACHABLE:
yield name
if check is flow_analysis.REACHABLE:
break
def get(self, name):
try:
names = self._used_names[str(name)]
except KeyError:
return []
return self._filter(names, until_position)
return self._filter(names)
def values(self, name, until_position=None):
return self._filter(self._used_names.values(), until_position)
def values(self):
return self._filter(self._used_names.values())
class FunctionExecutionFilter(ParserTreeFilter):
def __init__(self, parser_scope, executed_function, param_by_name):
super(FunctionExecutionFilter, self).__init__(parser_scope)
def __init__(self, parser_scope, executed_function, param_by_name,
until_position=None, origin_scope=None):
super(FunctionExecutionFilter, self).__init__(
parser_scope,
until_position,
origin_scope
)
self._executed_function = executed_function
self._param_by_name = param_by_name
def _filter(self, names, until_position):
names = super(FunctionExecutionFilter, self)._filter(names, until_position)
def _filter(self, names):
names = super(FunctionExecutionFilter, self)._filter(names)
names = [self._executed_function.name_for_position(name.start_pos) for name in names]
names = [self._param_by_name(str(name)) if search_ancestor(name, 'param') else name
for name in names]
return names
def get_global_filters(evaluator, context, until_position):
"""
Returns all filters in order of priority for name resolution.
"""
while context is not None:
for filter in context.get_filters(search_global=True, until_position=until_position):
yield filter
if context.type == 'funcdef':
until_position = None
node = context.get_parent_scope()
context = evaluator.wrap(node)
# Add builtins to the global scope.
for filter in evaluator.BUILTINS.get_filters(search_global=True):
yield filter

View File

@@ -34,7 +34,7 @@ from jedi.evaluate import flow_analysis
from jedi.evaluate import param
from jedi.evaluate import helpers
from jedi.evaluate.cache import memoize_default
from jedi.evaluate.filters import ParserTreeFilter
from jedi.evaluate.filters import get_global_filters
def filter_after_position(names, position, origin=None):
@@ -104,14 +104,14 @@ class NameFinder(object):
self._found_predefined_if_name = None
@debug.increase_indent
def find(self, scopes, attribute_lookup):
def find(self, filters, attribute_lookup):
"""
:params bool attribute_lookup: Tell to logic if we're accessing the
attribute or the contents of e.g. a function.
"""
# TODO rename scopes to names_dicts
names = self.filter_name(scopes)
names = self.filter_name(filters)
if self._found_predefined_if_name is not None:
return self._found_predefined_if_name
@@ -133,11 +133,11 @@ class NameFinder(object):
debug.dbg('finder._names_to_types: %s -> %s', names, types)
return types
def scopes(self, search_global=False):
def get_filters(self, search_global=False):
if search_global:
return global_names_dict_generator(self._evaluator, self.scope, self.position)
return get_global_filters(self._evaluator, self.scope, self.position)
else:
return ((n, None) for n in self.scope.names_dicts(search_global))
return self.scope.get_filters(search_global, self.position)
def names_dict_lookup(self, names_dict, position):
def get_param(scope, el):
@@ -233,25 +233,16 @@ class NameFinder(object):
return [get_param(name_scope, n) for n in last_names]
return last_names
def filter_name(self, names_dicts):
def filter_name(self, filters):
"""
Searches names that are defined in a scope (the different
`names_dicts`), until a name fits.
"""
names = []
for filter in get_global_filters(self._evaluator, self.scope):
names = filter.get(self.name_str, self.position)
for filter in filters:
names = filter.get(self.name_str)
if names:
break
debug.dbg('finder.filter_name "%s" in (%s): %s@%s', self.name_str,
self.scope, names, self.position)
return names
for names_dict, position in names_dicts:
names = self.names_dict_lookup(names_dict, position)
if names:
break
debug.dbg('finder.filter_name "%s" in (%s): %s@%s', self.name_str,
self.scope, names, self.position)
return list(self._clean_names(names))
@@ -621,20 +612,6 @@ def global_names_dict_generator(evaluator, scope, position):
yield names_dict, None
def get_global_filters(evaluator, context):
"""
Returns all filters in order of priority for name resolution.
"""
while context is not None:
for filter in context.get_filters(search_global=True):
yield filter
context = evaluator.wrap(context.get_parent_scope())
# Add builtins to the global scope.
for filter in evaluator.BUILTINS.get_filters(search_global=True):
yield filter
def check_tuple_assignments(evaluator, types, name):
"""
Checks if tuples are assigned.

View File

@@ -207,10 +207,14 @@ class Instance(use_metaclass(CachedMetaClass, Executed)):
for names_dict in self.base.names_dicts(search_global=False, is_instance=True):
yield LazyInstanceDict(self._evaluator, self, names_dict)
def get_filters(self, search_global):
raise NotImplementedError
yield self._self_names_dict()
yield ParserTreeFilter(self.base)
def get_filters(self, search_global, until_position=None, origin_scope=None):
#for s in self.base.py__mro__():
#yield self._self_names_dict()
for cls in self.base.py__mro__():
if isinstance(cls, compiled.CompiledObject):
yield CompiledInstanceClassFilter(self._evaluator, self, cls, origin_scope)
else:
yield InstanceClassFilter(self._evaluator, self, cls.base, origin_scope)
def py__getitem__(self, index):
try:
@@ -263,6 +267,37 @@ class Instance(use_metaclass(CachedMetaClass, Executed)):
self.var_args, dec)
class CompiledInstanceClassFilter(compiled.CompiledObjectFilter):
def __init__(self, evaluator, instance, compiled_object, origin_scope):
super(CompiledInstanceClassFilter, self).__init__(
evaluator,
compiled_object,
is_instance=True,
origin_scope=origin_scope
)
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, instance, parser_scope, origin_scope):
super(InstanceClassFilter, self).__init__(
parser_scope,
origin_scope=origin_scope
)
self._evaluator = evaluator
self._instance = instance
def _filter(self, names):
names = super(InstanceClassFilter, self)._filter(names)
return [get_instance_el(self._evaluator, self._instance, name, True)
for name in names]
class LazyInstanceDict(object):
def __init__(self, evaluator, instance, dct):
self._evaluator = evaluator
@@ -492,9 +527,9 @@ class Class(use_metaclass(CachedMetaClass, Wrapper)):
else:
yield scope.names_dict
def get_filters(self, search_global):
def get_filters(self, search_global, until_position=None, is_instance=False):
if search_global:
yield ParserTreeFilter(self.base)
yield ParserTreeFilter(self.base, until_position)
else:
for scope in self.py__mro__():
if isinstance(scope, compiled.CompiledObject):
@@ -598,9 +633,9 @@ class Function(use_metaclass(CachedMetaClass, Wrapper)):
for names_dict in scope.names_dicts(False):
yield names_dict
def get_filters(self, search_global):
def get_filters(self, search_global, until_position=None):
if search_global:
yield ParserTreeFilter(self.base)
yield ParserTreeFilter(self.base, until_position)
else:
scope = self.py__class__()
for filter in scope.get_filters(search_global=False):
@@ -777,9 +812,11 @@ class FunctionExecution(Executed):
yield result
del evaluator.predefined_if_name_dict_dict[for_stmt]
def get_filters(self, search_global):
def get_filters(self, search_global, until_position=None):
yield FunctionExecutionFilter(self._original_function,
self._copied_funcdef, self.param_by_name)
self._copied_funcdef,
self.param_by_name,
until_position)
@memoize_default(default=NO_DEFAULT)
def _get_params(self):
@@ -852,8 +889,8 @@ class ModuleWrapper(use_metaclass(CachedMetaClass, tree.Module, Wrapper)):
yield dict((str(n), [GlobalName(n)]) for n in self.base.global_names)
yield self._sub_modules_dict()
def get_filters(self, search_global):
yield ParserTreeFilter(self._module)
def get_filters(self, search_global, until_position=None):
yield ParserTreeFilter(self._module, until_position)
# TODO
'''
yield self._module_attributes_dict()