From fcda62862c72fabf0ddfcec9d4a0383fc49c0bdf Mon Sep 17 00:00:00 2001 From: Dave Halter Date: Tue, 18 Dec 2018 09:30:49 +0100 Subject: [PATCH] Fix calculate_dotted_path_from_sys_path. It was broken beyond stupid. --- jedi/api/__init__.py | 7 +++++-- jedi/evaluate/context/module.py | 1 - jedi/evaluate/imports.py | 2 +- jedi/evaluate/sys_path.py | 21 ++++++++++++++------- test/test_evaluate/test_sys_path.py | 18 ++++++++++++++++++ 5 files changed, 38 insertions(+), 11 deletions(-) diff --git a/jedi/api/__init__.py b/jedi/api/__init__.py index bfc9f2d7..a04d1bc1 100644 --- a/jedi/api/__init__.py +++ b/jedi/api/__init__.py @@ -32,7 +32,7 @@ from jedi.evaluate import imports from jedi.evaluate import usages from jedi.evaluate.arguments import try_iter_content from jedi.evaluate.helpers import get_module_names, evaluate_call_of_leaf -from jedi.evaluate.sys_path import dotted_path_in_sys_path +from jedi.evaluate.sys_path import calculate_dotted_path_from_sys_path from jedi.evaluate.filters import TreeNameDefinition, ParamName from jedi.evaluate.syntax_tree import tree_name_to_contexts from jedi.evaluate.context import ModuleContext @@ -149,7 +149,10 @@ class Script(object): def _get_module(self): names = ('__main__',) if self.path is not None: - import_names = dotted_path_in_sys_path(self._evaluator.get_sys_path(), self.path) + import_names = calculate_dotted_path_from_sys_path( + self._evaluator.get_sys_path(), + self.path + ) if import_names is not None: names = import_names diff --git a/jedi/evaluate/context/module.py b/jedi/evaluate/context/module.py index a89d9ff5..31d4b747 100644 --- a/jedi/evaluate/context/module.py +++ b/jedi/evaluate/context/module.py @@ -145,7 +145,6 @@ class ModuleContext(ModuleMixin, TreeContext): self._path = path self._string_names = string_names self.code_lines = code_lines - #print(self._path) def _get_init_directory(self): """ diff --git a/jedi/evaluate/imports.py b/jedi/evaluate/imports.py index 865494cc..62db5889 100644 --- a/jedi/evaluate/imports.py +++ b/jedi/evaluate/imports.py @@ -517,7 +517,7 @@ 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.dotted_path_in_sys_path(e_sys_path, path) + 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, diff --git a/jedi/evaluate/sys_path.py b/jedi/evaluate/sys_path.py index d015d846..22d98c37 100644 --- a/jedi/evaluate/sys_path.py +++ b/jedi/evaluate/sys_path.py @@ -197,31 +197,38 @@ def _get_buildout_script_paths(search_path): continue -def dotted_path_in_sys_path(sys_path, module_path): +def calculate_dotted_path_from_sys_path(sys_path, module_path): """ Returns the dotted path inside a sys.path as a list of names. + + This function is supposed to be a backup plan in case there's no idea where + a file is lying. """ # First remove the suffix. for suffix in all_suffixes(): if module_path.endswith(suffix): module_path = module_path[:-len(suffix)] - break + break else: # There should always be a suffix in a valid Python file on the path. return None - if module_path.startswith(os.path.sep): - # The paths in sys.path most of the times don't end with a slash. - module_path = module_path[1:] + if module_path.endswith('__init__'): + module_path = module_path[:-len('__init__') - 1] + + if module_path.endswith(os.path.sep): + # The paths in sys.path may end with a slash. + module_path = module_path[:-1] for p in sys_path: if module_path.startswith(p): rest = module_path[len(p):] + if rest.startswith(os.path.sep) or rest.startswith('/'): + rest = rest[1:] if rest: split = rest.split(os.path.sep) for string in split: if not string or '.' in string: return None - return split - + return tuple(split) return None diff --git a/test/test_evaluate/test_sys_path.py b/test/test_evaluate/test_sys_path.py index dab4f471..e6c7d6f0 100644 --- a/test/test_evaluate/test_sys_path.py +++ b/test/test_evaluate/test_sys_path.py @@ -3,6 +3,8 @@ from glob import glob import sys import shutil +import pytest + from jedi.evaluate import sys_path from jedi.api.environment import create_environment @@ -59,3 +61,19 @@ def test_venv_and_pths(venv_path): # Ensure that none of venv dirs leaked to the interpreter. assert not set(sys.path).intersection(ETALON) + + +@pytest.mark.parametrize(('sys_path_', 'path', 'expected'), [ + (['/foo'], '/foo/bar.py', ('bar',)), + (['/foo'], '/foo/bar/baz.py', ('bar', 'baz')), + (['/foo'], '/foo/bar/__init__.py', ('bar',)), + (['/foo'], '/foo/bar/baz/__init__.py', ('bar', 'baz')), + + (['/foo'], '/foo/bar.so', ('bar',)), + (['/foo'], '/foo/bar/__init__.so', ('bar',)), + + (['/foo'], '/x/bar.py', None), + (['/foo'], '/foo/bar.xyz', None), +]) +def test_calculate_dotted_path_from_sys_path(path, sys_path_, expected): + assert sys_path.calculate_dotted_path_from_sys_path(sys_path_, path) == expected