From 6a11b7d89ec984ac3bc0b20df99e6263165a49f3 Mon Sep 17 00:00:00 2001 From: Dave Halter Date: Fri, 16 Feb 2018 12:40:31 +0100 Subject: [PATCH] Generalize the use of smart import paths Now a lot more parts of the current scripts path are used as a sys path. --- jedi/api/__init__.py | 6 ++++-- jedi/api/project.py | 26 ++++++++++++++++++++++++-- jedi/evaluate/imports.py | 9 --------- 3 files changed, 28 insertions(+), 13 deletions(-) diff --git a/jedi/api/__init__.py b/jedi/api/__init__.py index 6354de3c..5736c58d 100644 --- a/jedi/api/__init__.py +++ b/jedi/api/__init__.py @@ -101,11 +101,13 @@ class Script(object): sys_path = list(map(force_unicode, sys_path)) # Load the Python grammar of the current interpreter. - project = get_default_project(path or os.getcwd()) + project = get_default_project(self.path or os.getcwd()) # TODO deprecate and remove sys_path from the Script API. if sys_path is not None: project._sys_path = sys_path - self._evaluator = Evaluator(project, environment=environment, script_path=path) + self._evaluator = Evaluator( + project, environment=environment, script_path=self.path + ) self._project = project debug.speed('init') self._module_node, source = self._evaluator.parse_and_get_code( diff --git a/jedi/api/project.py b/jedi/api/project.py index d6689948..c5f9c5d9 100644 --- a/jedi/api/project.py +++ b/jedi/api/project.py @@ -16,6 +16,15 @@ _CONTAINS_POTENTIAL_PROJECT = 'setup.py', '.git', '.hg', 'MANIFEST.in' _SERIALIZER_VERSION = 1 +def _remove_duplicates_from_path(path): + used = set() + for p in path: + if p in used: + continue + used.add(p) + yield p + + def _force_unicode_list(lst): return list(map(force_unicode, lst)) @@ -98,15 +107,28 @@ class Project(object): sys_path = list(self._get_base_sys_path(environment)) if self._smart_sys_path: + suffixed.append(self._path) + if evaluator.script_path is not None: suffixed += detect_additional_paths(evaluator, evaluator.script_path) - suffixed.append(self._path) + traversed = [] + for parent in traverse_parents(evaluator.script_path): + traversed.append(parent) + if parent == self._path: + # Don't go futher than the project path. + break + + # AFAIK some libraries have imports like `foo.foo.bar`, which + # leads to the conclusion to by default prefer longer paths + # rather than shorter ones by default. + suffixed += reversed(traversed) if self._django: prefixed.append(self._path) - return _force_unicode_list(prefixed) + sys_path + _force_unicode_list(suffixed) + path = _force_unicode_list(prefixed) + sys_path + _force_unicode_list(suffixed) + return list(_remove_duplicates_from_path(path)) def save(self): data = dict(self.__dict__) diff --git a/jedi/evaluate/imports.py b/jedi/evaluate/imports.py index 637f284a..ee2bce32 100644 --- a/jedi/evaluate/imports.py +++ b/jedi/evaluate/imports.py @@ -265,15 +265,6 @@ class Importer(object): sys_path_mod = self._evaluator.get_sys_path() \ + sys_path.check_sys_path_modifications(self.module_context) if self.file_path is not None: - # If you edit e.g. gunicorn, there will be imports like this: - # `from gunicorn import something`. But gunicorn is not in the - # sys.path. Therefore look if gunicorn is a parent directory, #56. - if self.import_path: - for path in sys_path.traverse_parents(self.file_path): - if os.path.basename(path) == self.str_import_path[0]: - sys_path_mod.insert(0, force_unicode(os.path.dirname(path))) - break - # Since we know nothing about the call location of the sys.path, # it's a possibility that the current directory is the origin of # the Python execution.