diff --git a/jedi/api/completion.py b/jedi/api/completion.py index 45de5f46..6bd3a390 100644 --- a/jedi/api/completion.py +++ b/jedi/api/completion.py @@ -183,7 +183,8 @@ class Completion: filters = get_global_filters( self._evaluator, scope, - self._position + self._position, + origin_scope=scope ) completion_names = [] for filter in filters: @@ -191,11 +192,12 @@ class Completion: return completion_names def _trailer_completions(self, atom_expr): + user_scope = get_user_scope(self._module, self._position) scopes = self._evaluator.eval_element(atom_expr) completion_names = [] debug.dbg('trailer completion scopes: %s', scopes) for s in scopes: - for filter in s.get_filters(search_global=False): + for filter in s.get_filters(search_global=False, origin_scope=user_scope): completion_names += filter.values() return completion_names diff --git a/jedi/evaluate/compiled/__init__.py b/jedi/evaluate/compiled/__init__.py index f89de013..2e2ccfa9 100644 --- a/jedi/evaluate/compiled/__init__.py +++ b/jedi/evaluate/compiled/__init__.py @@ -157,7 +157,8 @@ 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=False, is_instance=False, until_position=None): + def get_filters(self, search_global=False, is_instance=False, + until_position=None, origin_scope=None): yield self._ensure_one_filter(is_instance) @memoize_method diff --git a/jedi/evaluate/filters.py b/jedi/evaluate/filters.py index 68d9b2c8..d6e8915b 100644 --- a/jedi/evaluate/filters.py +++ b/jedi/evaluate/filters.py @@ -123,7 +123,7 @@ class DictFilter(AbstractFilter): return self._filter(self._dct.values()) -def get_global_filters(evaluator, context, until_position): +def get_global_filters(evaluator, context, until_position, origin_scope): """ Returns all filters in order of priority for name resolution. """ @@ -131,7 +131,10 @@ def get_global_filters(evaluator, context, until_position): while context is not None: if not (context.type == 'classdef' and in_func): # Names in methods cannot be resolved within the class. - for filter in context.get_filters(search_global=True, until_position=until_position): + for filter in context.get_filters( + search_global=True, + until_position=until_position, + origin_scope=origin_scope): yield filter if context.type == 'funcdef': # The position should be reset if the current scope is a function. diff --git a/jedi/evaluate/finder.py b/jedi/evaluate/finder.py index bd7aebf0..75a96f27 100644 --- a/jedi/evaluate/finder.py +++ b/jedi/evaluate/finder.py @@ -134,10 +134,15 @@ class NameFinder(object): return types def get_filters(self, search_global=False): - if search_global: - return get_global_filters(self._evaluator, self.scope, self.position) + if isinstance(self.name_str, tree.Name): + origin_scope = self.name_str.get_parent_until(tree.Scope, reverse=True) else: - return self.scope.get_filters(search_global, self.position) + origin_scope = None + + if search_global: + return get_global_filters(self._evaluator, self.scope, self.position, origin_scope) + else: + return self.scope.get_filters(search_global, self.position, origin_scope=origin_scope) def names_dict_lookup(self, names_dict, position): def get_param(scope, el): diff --git a/jedi/evaluate/representation.py b/jedi/evaluate/representation.py index 7e9f1b73..8a581737 100644 --- a/jedi/evaluate/representation.py +++ b/jedi/evaluate/representation.py @@ -300,10 +300,22 @@ class InstanceClassFilter(ParserTreeFilter): ) self._instance = instance + def _equals_origin_scope(self): + node = self._origin_scope + while node is not None: + if node == self._parser_scope or node == self._instance: + return True + node = node.get_parent_scope() + return False + + def _access_possible(self, name): + return not name.value.startswith('__') or name.value.endswith('__') \ + or self._equals_origin_scope() + def _filter(self, names): names = super(InstanceClassFilter, self)._filter(names) return [get_instance_el(self._evaluator, self._instance, name, True) - for name in names] + for name in names if self._access_possible(name)] def _check_flows(self, names): return names @@ -324,7 +336,7 @@ class SelfNameFilter(InstanceClassFilter): if tree.is_node(trailer, 'trailer') \ and len(trailer.children) == 2 \ and trailer.children[0] == '.': - if name.is_definition(): + if name.is_definition() and self._access_possible(name): init_execution = self._instance._get_init_execution() # Hopefully we can somehow change this. if init_execution is not None and \ @@ -563,16 +575,16 @@ class Class(use_metaclass(CachedMetaClass, Wrapper)): else: yield scope.names_dict - def get_filters(self, search_global, until_position=None, is_instance=False): + def get_filters(self, search_global, until_position=None, origin_scope=None, is_instance=False): if search_global: - yield ParserTreeFilter(self._evaluator, self.base, until_position) + yield ParserTreeFilter(self._evaluator, self.base, until_position, origin_scope=origin_scope) else: for scope in self.py__mro__(): if isinstance(scope, compiled.CompiledObject): for filter in scope.get_filters(is_instance=is_instance): yield filter else: - yield ParserTreeFilter(self._evaluator, scope.base) + yield ParserTreeFilter(self._evaluator, scope.base, origin_scope=origin_scope) def is_class(self): return True @@ -670,12 +682,12 @@ class Function(use_metaclass(CachedMetaClass, Wrapper)): for names_dict in scope.names_dicts(False): yield names_dict - def get_filters(self, search_global, until_position=None): + def get_filters(self, search_global, until_position=None, origin_scope=None): if search_global: - yield ParserTreeFilter(self._evaluator, self.base, until_position) + yield ParserTreeFilter(self._evaluator, self.base, until_position, origin_scope=origin_scope) else: scope = self.py__class__() - for filter in scope.get_filters(search_global=False): + for filter in scope.get_filters(search_global=False, origin_scope=origin_scope): yield filter @Python3Method @@ -849,11 +861,12 @@ class FunctionExecution(Executed): yield result del evaluator.predefined_if_name_dict_dict[for_stmt] - def get_filters(self, search_global, until_position=None): + def get_filters(self, search_global, until_position=None, origin_scope=None): yield FunctionExecutionFilter(self._evaluator, self._original_function, self._copied_funcdef, self.param_by_name, - until_position) + until_position, + origin_scope=origin_scope) @memoize_default(default=NO_DEFAULT) def _get_params(self): @@ -926,8 +939,13 @@ 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, until_position=None): - yield ParserTreeFilter(self._evaluator, self._module, until_position) + def get_filters(self, search_global, until_position=None, origin_scope=None): + yield ParserTreeFilter( + self._evaluator, + self._module, + until_position, + origin_scope=origin_scope + ) yield GlobalNameFilter(self._module) yield DictFilter(self._sub_modules_dict()) yield DictFilter(self._module_attributes_dict()) diff --git a/test/completion/classes.py b/test/completion/classes.py index b4f67a1f..6bbd9d6a 100644 --- a/test/completion/classes.py +++ b/test/completion/classes.py @@ -384,10 +384,33 @@ class PrivateVar(): self.__var #? ['__var'] self.__var + + def __private_func(self): + return 1 + + def wrap_private(self): + return self.__private_func() #? [] PrivateVar().__var #? PrivateVar().__var +#? [] +PrivateVar().__private_func +#? int() +PrivateVar().wrap_private() + + +class PrivateSub(PrivateVar): + def test(self): + #? [] + self.__var + + def wrap_private(self): + #? [] + self.__var + +#? [] +PrivateSub().__var # ----------------- # super