diff --git a/jedi/evaluate/context/module.py b/jedi/evaluate/context/module.py index 31d4b747..7dd85f16 100644 --- a/jedi/evaluate/context/module.py +++ b/jedi/evaluate/context/module.py @@ -143,7 +143,7 @@ class ModuleContext(ModuleMixin, TreeContext): tree_node=module_node ) self._path = path - self._string_names = string_names + self.string_names = string_names self.code_lines = code_lines def _get_init_directory(self): @@ -160,9 +160,9 @@ class ModuleContext(ModuleMixin, TreeContext): return None def py__name__(self): - if self._string_names is None: + if self.string_names is None: return None - return '.'.join(self._string_names) + return '.'.join(self.string_names) def py__file__(self): """ diff --git a/jedi/evaluate/imports.py b/jedi/evaluate/imports.py index 62db5889..67274cd3 100644 --- a/jedi/evaluate/imports.py +++ b/jedi/evaluate/imports.py @@ -497,18 +497,14 @@ def get_modules_containing_name(evaluator, modules, name): """ Search a name in the directories of modules. """ - def check_directories(paths): - for p in paths: - if p is not None: - # We need abspath, because the seetings paths might not already - # have been converted to absolute paths. - d = os.path.dirname(os.path.abspath(p)) - for file_name in os.listdir(d): - path = os.path.join(d, file_name) - if file_name.endswith('.py'): - yield path + def check_directory(path): + d = os.path.dirname(os.path.abspath(path)) + for file_name in os.listdir(d): + path = os.path.join(d, file_name) + if file_name.endswith('.py'): + yield path - def check_fs(path): + def check_fs(path, base_names): try: f = open(path, 'rb') except FileNotFoundError: @@ -517,7 +513,15 @@ def get_modules_containing_name(evaluator, modules, name): code = python_bytes_to_unicode(f.read(), errors='replace') if name in code: e_sys_path = evaluator.get_sys_path() - import_names = sys_path.calculate_dotted_path_from_sys_path(e_sys_path, path) + module_name = os.path.basename(path) + if module_name.endswith('.py'): + module_name = module_name[:-3] + + if base_names: + import_names = base_names + (module_name,) + else: + import_names = sys_path.calculate_dotted_path_from_sys_path(e_sys_path, path) + module = _load_module( evaluator, path, code, sys_path=e_sys_path, @@ -528,26 +532,32 @@ def get_modules_containing_name(evaluator, modules, name): # skip non python modules used_mod_paths = set() + path_with_names_to_be_checked = [] for m in modules: try: path = m.py__file__() except AttributeError: pass else: - used_mod_paths.add(path) + if path is not None: + if path not in used_mod_paths: + used_mod_paths.add(path) + string_names = m.string_names + if not m.is_package() and string_names is not None: + string_names = string_names[:-1] + path_with_names_to_be_checked.append((path, string_names)) yield m if not settings.dynamic_params_for_other_modules: return - additional = set(os.path.abspath(p) for p in settings.additional_dynamic_modules) - # Check the directories of used modules. - paths = (additional | set(check_directories(used_mod_paths))) \ - - used_mod_paths + for p in settings.additional_dynamic_modules: + p = os.path.abspath(p) + if p not in used_mod_paths: + path_with_names_to_be_checked.append((p, None)) - # Sort here to make issues less random. - for p in sorted(paths): - # make testing easier, sort it - same results on every interpreter - m = check_fs(p) - if m is not None and not isinstance(m, compiled.CompiledObject): - yield m + for p, base_names in path_with_names_to_be_checked: + for file_path in check_directory(p): + m = check_fs(file_path, base_names) + if m is not None and not isinstance(m, compiled.CompiledObject): + yield m diff --git a/test/test_evaluate/test_imports.py b/test/test_evaluate/test_imports.py index ec349c01..4a06e2bd 100644 --- a/test/test_evaluate/test_imports.py +++ b/test/test_evaluate/test_imports.py @@ -9,6 +9,7 @@ import pytest from jedi._compatibility import find_module_py33, find_module from jedi.evaluate import compiled +from jedi.evaluate import imports from ..helpers import cwd_at @@ -251,3 +252,21 @@ def test_compiled_import_none(monkeypatch, Script): """ monkeypatch.setattr(compiled, 'load_module', lambda *args, **kwargs: None) assert not Script('import sys').goto_definitions() + + +@pytest.mark.parametrize( + ('path', 'goal'), [ + ('test_evaluate/test_docstring.py', ('ok', 'lala', 'test_imports')), + ('test_evaluate/__init__.py', ('ok', 'lala', 'x', 'test_imports')), + ] +) +def test_get_modules_containing_name(evaluator, path, goal): + module = imports._load_module(evaluator, path, import_names=('ok', 'lala', 'x')) + assert module + input_module, found_module = imports.get_modules_containing_name( + evaluator, + [module], + 'string_that_only_exists_here' + ) + assert input_module is module + assert found_module.string_names == goal