diff --git a/docs/source/conf.py b/docs/source/conf.py index 63d5fa2b..0716eb94 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -167,6 +167,8 @@ html_static_path = ['_static'] # Output file base name for HTML help builder. htmlhelp_basename = 'Jedidoc' +html_style = '/default.css' # Force usage of default template on RTD + # -- Options for LaTeX output -------------------------------------------------- @@ -249,8 +251,3 @@ todo_include_todos = False # -- Options for autodoc module ------------------------------------------------ autodoc_default_flags = ['members', 'undoc-members'] - - -# -- Options for readthedocs --------------------------------------------------- - -html_style = True # Force usage of default template on RTD diff --git a/jedi/api.py b/jedi/api.py index 5bdb9f0a..35f60b46 100644 --- a/jedi/api.py +++ b/jedi/api.py @@ -135,6 +135,9 @@ class Script(object): l = self._module.get_line(self.pos[0])[:self.pos[1]] if not l.endswith('import import'): continue + a = s.import_stmt.alias + if a and a.start_pos <= self.pos <= a.end_pos: + continue names = s.get_defined_names(on_import_stmt=True) else: names = s.get_defined_names() diff --git a/jedi/imports.py b/jedi/imports.py index 266fd879..9e013b92 100644 --- a/jedi/imports.py +++ b/jedi/imports.py @@ -104,6 +104,14 @@ class ImportPath(parsing.Base): for i in range(self.import_stmt.relative_count - 1): path = os.path.dirname(path) names += self.get_module_names([path]) + + if self.import_stmt.relative_count: + rel_path = self.get_relative_path() + '/__init__.py' + try: + m = modules.Module(rel_path) + names += m.parser.module.get_defined_names() + except IOError: + pass else: if on_import_stmt and isinstance(scope, parsing.Module) \ and scope.path.endswith('__init__.py'): @@ -158,8 +166,7 @@ class ImportPath(parsing.Base): return [] scopes = [scope] - scopes += itertools.chain.from_iterable( - remove_star_imports(s) for s in scopes) + scopes += remove_star_imports(scope) # follow the rest of the import (not FS -> classes, functions) if len(rest) > 1 or rest and self.is_like_search: @@ -184,20 +191,23 @@ class ImportPath(parsing.Base): evaluate.follow_statement.pop_stmt() return scopes + def get_relative_path(self): + path = self.file_path + for i in range(self.import_stmt.relative_count - 1): + path = os.path.dirname(path) + return path + def _follow_file_system(self): """ Find a module with a path (of the module, like usb.backend.libusb10). """ - def follow_str(ns, string): - debug.dbg('follow_module', ns, string) + def follow_str(ns_path, string): + debug.dbg('follow_module', ns_path, string) path = None - if ns: - path = ns[1] + if ns_path: + path = ns_path elif self.import_stmt.relative_count: - module = self.import_stmt.get_parent_until() - path = os.path.abspath(module.path) - for i in range(self.import_stmt.relative_count): - path = os.path.dirname(path) + path = self.get_relative_path() global imports_processed imports_processed += 1 @@ -222,14 +232,22 @@ class ImportPath(parsing.Base): else: sys_path_mod = list(builtin.get_sys_path()) - current_namespace = None + current_namespace = (None, None, None) # now execute those paths rest = [] for i, s in enumerate(self.import_path): try: - current_namespace = follow_str(current_namespace, s) + current_namespace = follow_str(current_namespace[1], s) except ImportError: - if current_namespace: + if self.import_stmt.relative_count \ + and len(self.import_path) == 1: + # follow `from . import some_variable` + rel_path = self.get_relative_path() + try: + current_namespace = follow_str(rel_path, '__init__') + except ImportError: + pass + if current_namespace[1]: rest = self.import_path[i:] else: raise ModuleNotFound( diff --git a/jedi/modules.py b/jedi/modules.py index 85a835da..79968946 100644 --- a/jedi/modules.py +++ b/jedi/modules.py @@ -23,8 +23,11 @@ class Module(builtin.CachedModule): :param path: The module path of the file. :param source: The source code of the file. """ - def __init__(self, path, source): + def __init__(self, path, source=None): super(Module, self).__init__(path=path) + if source is None: + with open(path) as f: + source = f.read() self.source = source_to_unicode(source) self._line_cache = None diff --git a/test/completion/__init__.py b/test/completion/__init__.py new file mode 100644 index 00000000..374dd947 --- /dev/null +++ b/test/completion/__init__.py @@ -0,0 +1,3 @@ +""" needed for some modules to test against packages. """ + +some_variable = 1 diff --git a/test/completion/goto.py b/test/completion/goto.py index a231eb00..451effdf 100644 --- a/test/completion/goto.py +++ b/test/completion/goto.py @@ -115,6 +115,12 @@ mod1.a #! ['a=1.0'] from import_tree.pkg.mod1 import a +#! ['import os'] +from .imports import os + +#! ['some_variable=1'] +from . import some_variable + # ----------------- # anonymous classes # ----------------- diff --git a/test/completion/imports.py b/test/completion/imports.py index e694a14b..9f5d4d24 100644 --- a/test/completion/imports.py +++ b/test/completion/imports.py @@ -175,6 +175,13 @@ mod1. #? str() imp_tree.a +#? ['some_variable'] +from . import some_variable +#? ['arrays'] +from . import arrays +#? [] +from . import import_tree as ren + # ----------------- # special positions -> edge cases