From 5f46b484330edfea747998510bb1dd42fde59a8b Mon Sep 17 00:00:00 2001 From: Dave Halter Date: Tue, 11 Oct 2016 16:01:26 +0200 Subject: [PATCH] Add a filter for global names. --- jedi/evaluate/filters.py | 64 ++++++++++++++++++++------------- jedi/evaluate/finder.py | 2 +- jedi/evaluate/representation.py | 41 +++++++++++++++------ 3 files changed, 72 insertions(+), 35 deletions(-) diff --git a/jedi/evaluate/filters.py b/jedi/evaluate/filters.py index 56b8e517..c3e4ac91 100644 --- a/jedi/evaluate/filters.py +++ b/jedi/evaluate/filters.py @@ -6,14 +6,15 @@ from abc import abstractmethod from jedi.parser.tree import search_ancestor from jedi.evaluate import flow_analysis +from jedi.common import to_list class AbstractFilter(object): + _until_position = None + 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] @@ -21,32 +22,46 @@ class AbstractFilter(object): @abstractmethod def get(self, name): - pass + raise NotImplementedError @abstractmethod def values(self): - pass + raise NotImplementedError -class ParserTreeFilter(AbstractFilter): - def __init__(self, parser_scope, until_position=None, origin_scope=None): - super(ParserTreeFilter, self).__init__(origin_scope) +class AbstractUsedNamesFilter(AbstractFilter): + def __init__(self, parser_scope, origin_scope=None): + super(AbstractUsedNamesFilter, self).__init__(origin_scope) self._parser_scope = parser_scope self._used_names = self._parser_scope.get_root_node().used_names + + def get(self, name): + try: + names = self._used_names[str(name)] + except KeyError: + return [] + + return list(self._filter(names)) + + def values(self): + return self._filter(self._used_names.values()) + + +class ParserTreeFilter(AbstractUsedNamesFilter): + def __init__(self, evaluator, parser_scope, until_position=None, origin_scope=None): + super(ParserTreeFilter, self).__init__(parser_scope, origin_scope) self._until_position = until_position - self._origin_scope = origin_scope + self._evaluator = evaluator 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 list(self._check_flows(sorted(names, key=lambda name: name.start_pos))) + return list(self._check_flows(names)) def _check_flows(self, names): - for name in names: - yield name - continue + for name in sorted(names, key=lambda name: name.start_pos, reverse=True): stmt = name.get_definition() name_scope = self._evaluator.wrap(stmt.get_parent_scope()) check = flow_analysis.break_check(self._evaluator, name_scope, @@ -57,22 +72,12 @@ class ParserTreeFilter(AbstractFilter): 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) - - def values(self): - return self._filter(self._used_names.values()) - class FunctionExecutionFilter(ParserTreeFilter): - def __init__(self, parser_scope, executed_function, param_by_name, + def __init__(self, evaluator, parser_scope, executed_function, param_by_name, until_position=None, origin_scope=None): super(FunctionExecutionFilter, self).__init__( + evaluator, parser_scope, until_position, origin_scope @@ -89,6 +94,17 @@ class FunctionExecutionFilter(ParserTreeFilter): return names +class GlobalNameFilter(AbstractUsedNamesFilter): + def __init__(self, parser_scope, origin_scope=None): + super(GlobalNameFilter, self).__init__(parser_scope) + + @to_list + def _filter(self, names): + for name in names: + if name.parent.type == 'global_stmt': + yield name + + def get_global_filters(evaluator, context, until_position): """ Returns all filters in order of priority for name resolution. diff --git a/jedi/evaluate/finder.py b/jedi/evaluate/finder.py index 77a34771..bd7aebf0 100644 --- a/jedi/evaluate/finder.py +++ b/jedi/evaluate/finder.py @@ -370,7 +370,7 @@ def _name_to_types(evaluator, name, scope): elif typ.type == 'global_stmt': for s in _get_global_stmt_scopes(evaluator, typ, name): finder = NameFinder(evaluator, s, str(name)) - names_dicts = finder.scopes(search_global=True) + names_dicts = finder.get_filters(search_global=True) # For global_stmt lookups, we only need the first possible scope, # which means the function itself. names_dicts = [next(names_dicts)] diff --git a/jedi/evaluate/representation.py b/jedi/evaluate/representation.py index 093c975f..b3fadeef 100644 --- a/jedi/evaluate/representation.py +++ b/jedi/evaluate/representation.py @@ -55,7 +55,7 @@ from jedi.evaluate import helpers from jedi.evaluate import param from jedi.evaluate import flow_analysis from jedi.evaluate import imports -from jedi.evaluate.filters import ParserTreeFilter, FunctionExecutionFilter +from jedi.evaluate.filters import ParserTreeFilter, FunctionExecutionFilter, GlobalNameFilter class Executed(tree.Base): @@ -208,8 +208,12 @@ 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 s in self.base.py__mro__(): - #yield self._self_names_dict() + for cls in self.base.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__(): if isinstance(cls, compiled.CompiledObject): yield CompiledInstanceClassFilter(self._evaluator, self, cls, origin_scope) @@ -286,10 +290,10 @@ class CompiledInstanceClassFilter(compiled.CompiledObjectFilter): class InstanceClassFilter(ParserTreeFilter): def __init__(self, evaluator, instance, parser_scope, origin_scope): super(InstanceClassFilter, self).__init__( - parser_scope, + evaluator=evaluator, + parser_scope=parser_scope, origin_scope=origin_scope ) - self._evaluator = evaluator self._instance = instance def _filter(self, names): @@ -298,6 +302,21 @@ class InstanceClassFilter(ParserTreeFilter): for name in names] +class SelfNameFilter(InstanceClassFilter): + def _filter(self, names): + names = self._filter_self_names(names) + return list(self._check_flows(names)) + + 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(): + yield get_instance_el(self._evaluator, self._instance, name) + + class LazyInstanceDict(object): def __init__(self, evaluator, instance, dct): self._evaluator = evaluator @@ -360,6 +379,7 @@ class InstanceElement(use_metaclass(CachedMetaClass, tree.Base)): def parent(self): par = self.var.parent if isinstance(par, Class) and par == self.instance.base \ + or not isinstance(self.instance.base, tree.Class) \ or isinstance(par, tree.Class) \ and par == self.instance.base.base: par = self.instance @@ -529,13 +549,13 @@ class Class(use_metaclass(CachedMetaClass, Wrapper)): def get_filters(self, search_global, until_position=None, is_instance=False): if search_global: - yield ParserTreeFilter(self.base, until_position) + yield ParserTreeFilter(self._evaluator, self.base, until_position) else: for scope in self.py__mro__(): if isinstance(scope, compiled.CompiledObject): raise NotImplementedError else: - yield ParserTreeFilter(self.base) + yield ParserTreeFilter(self._evaluator, self.base) def is_class(self): return True @@ -635,7 +655,7 @@ class Function(use_metaclass(CachedMetaClass, Wrapper)): def get_filters(self, search_global, until_position=None): if search_global: - yield ParserTreeFilter(self.base, until_position) + yield ParserTreeFilter(self._evaluator, self.base, until_position) else: scope = self.py__class__() for filter in scope.get_filters(search_global=False): @@ -813,7 +833,7 @@ class FunctionExecution(Executed): del evaluator.predefined_if_name_dict_dict[for_stmt] def get_filters(self, search_global, until_position=None): - yield FunctionExecutionFilter(self._original_function, + yield FunctionExecutionFilter(self._evaluator, self._original_function, self._copied_funcdef, self.param_by_name, until_position) @@ -890,7 +910,8 @@ class ModuleWrapper(use_metaclass(CachedMetaClass, tree.Module, Wrapper)): yield self._sub_modules_dict() def get_filters(self, search_global, until_position=None): - yield ParserTreeFilter(self._module, until_position) + yield ParserTreeFilter(self._evaluator, self._module, until_position) + yield GlobalNameFilter(self._module) # TODO ''' yield self._module_attributes_dict()