diff --git a/jedi/evaluate.py b/jedi/evaluate.py index e091bb3c..0290c996 100644 --- a/jedi/evaluate.py +++ b/jedi/evaluate.py @@ -502,7 +502,7 @@ class Execution(Executable): for name in names: key = name.var_args.get_only_subelement() - stmts += follow_path(iter([key]), obj) + stmts += follow_path(iter([key]), obj, self.base) return stmts if self.base.isinstance(Class): @@ -1480,10 +1480,10 @@ def follow_call_path(path, scope, position): # Reset the position, when imports where stripped. position = None - return follow_paths(path, result, position=position) + return follow_paths(path, result, scope, position=position) -def follow_paths(path, results, position=None): +def follow_paths(path, results, call_scope, position=None): """ In each result, `path` must be followed. Copies the path iterator. """ @@ -1495,7 +1495,7 @@ def follow_paths(path, results, position=None): iter_paths = [path] for i, r in enumerate(results): - fp = follow_path(iter_paths[i], r, position=position) + fp = follow_path(iter_paths[i], r, call_scope, position=position) if fp is not None: results_new += fp else: @@ -1504,7 +1504,7 @@ def follow_paths(path, results, position=None): return results_new -def follow_path(path, scope, position=None): +def follow_path(path, scope, call_scope, position=None): """ Uses a generator and tries to complete the path, e.g. >>> foo.bar.baz @@ -1540,9 +1540,24 @@ def follow_path(path, scope, position=None): else: # TODO Check magic class methods and return them also. # This is the typical lookup while chaining things. + if filter_private_variable(scope, call_scope, current): + return [] result = imports.strip_imports(get_scopes_for_name(scope, current, position=position)) - return follow_paths(path, set(result), position=position) + return follow_paths(path, set(result), call_scope, position=position) + + +def filter_private_variable(scope, call_scope, var_name): + if isinstance(var_name, (str, unicode)) \ + and var_name.startswith('__') and isinstance(scope, Instance): + #print 'a', scope, var_name, call_scope + s = call_scope.get_parent_until((parsing.Class, Instance), include_current=True) + if s != scope and s != scope.base.base: + #print s, scope, call_scope.parent() + return True + + return False + def goto(stmt, call_path=None): if call_path is None: diff --git a/jedi/imports.py b/jedi/imports.py index ebbf92bb..573441d6 100644 --- a/jedi/imports.py +++ b/jedi/imports.py @@ -157,6 +157,7 @@ class ImportPath(parsing.Base): scopes += itertools.chain.from_iterable( remove_star_imports(s) for s in scopes) + # follow the rest of the import (not FS -> classes, functions) if len(rest) > 1 or rest and self.is_like_search: scopes = [] elif rest: @@ -166,8 +167,8 @@ class ImportPath(parsing.Base): for s in scopes) else: scopes = itertools.chain.from_iterable( - evaluate.follow_path(iter(rest), s) - for s in scopes) + evaluate.follow_path(iter(rest), s, s) + for s in scopes) scopes = list(scopes) if self.is_nested_import(): diff --git a/test/completion/arrays.py b/test/completion/arrays.py index 86eff537..1654f329 100644 --- a/test/completion/arrays.py +++ b/test/completion/arrays.py @@ -253,5 +253,5 @@ tuple({1})[0] tuple((1,))[0] # implementation detail for lists, should not be visible -##? [] +#? [] list().__iterable diff --git a/test/completion/classes.py b/test/completion/classes.py index 28d72841..be5c85e9 100644 --- a/test/completion/classes.py +++ b/test/completion/classes.py @@ -510,3 +510,16 @@ Wrapper(Base()).ret(3) #? int() Wrapper2(Base()).ret(3) + +# ----------------- +# private vars +# ----------------- +class PrivateVar(): + def __init__(self): + self.__var = 1 + #? int() + self.__var +#? [] +PrivateVar().__var +#? +PrivateVar().__var