diff --git a/jedi/evaluate/__init__.py b/jedi/evaluate/__init__.py index 37e0cd1c..7243bc66 100644 --- a/jedi/evaluate/__init__.py +++ b/jedi/evaluate/__init__.py @@ -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 diff --git a/jedi/evaluate/compiled/__init__.py b/jedi/evaluate/compiled/__init__.py index beef3f6a..46dabac6 100644 --- a/jedi/evaluate/compiled/__init__.py +++ b/jedi/evaluate/compiled/__init__.py @@ -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 diff --git a/jedi/evaluate/compiled/fake.py b/jedi/evaluate/compiled/fake.py index e94543ed..436f149f 100644 --- a/jedi/evaluate/compiled/fake.py +++ b/jedi/evaluate/compiled/fake.py @@ -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 diff --git a/jedi/evaluate/filters.py b/jedi/evaluate/filters.py index a18fc7ce..56b8e517 100644 --- a/jedi/evaluate/filters.py +++ b/jedi/evaluate/filters.py @@ -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 diff --git a/jedi/evaluate/finder.py b/jedi/evaluate/finder.py index ba243a08..77a34771 100644 --- a/jedi/evaluate/finder.py +++ b/jedi/evaluate/finder.py @@ -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. diff --git a/jedi/evaluate/representation.py b/jedi/evaluate/representation.py index a30ad526..093c975f 100644 --- a/jedi/evaluate/representation.py +++ b/jedi/evaluate/representation.py @@ -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()