From 59f12533901fc345fcb9793dfc35a5671b8f0a8a Mon Sep 17 00:00:00 2001 From: Bryan Bugyi Date: Fri, 2 Jul 2021 19:41:51 -0400 Subject: [PATCH 01/24] Fix bug finding implicit namespace packages --- .../compiled/subprocess/functions.py | 35 +++++++++++++------ jedi/inference/imports.py | 21 ++++------- 2 files changed, 32 insertions(+), 24 deletions(-) diff --git a/jedi/inference/compiled/subprocess/functions.py b/jedi/inference/compiled/subprocess/functions.py index 5070c6643a..e6b02a1cd6 100644 --- a/jedi/inference/compiled/subprocess/functions.py +++ b/jedi/inference/compiled/subprocess/functions.py @@ -124,7 +124,7 @@ def _iter_module_names(inference_state, paths): yield modname -def _find_module(string, path=None, full_name=None, is_global_search=True): +def _find_module(string, paths=None, full_name=None, is_global_search=True): """ Provides information about a module. @@ -134,14 +134,17 @@ def _find_module(string, path=None, full_name=None, is_global_search=True): or the name of the module if it is a builtin one and a boolean indicating if the module is contained in a package. """ + if paths is None: + paths_iter = [None] + else: + paths_iter = paths + spec = None loader = None for finder in sys.meta_path: - if is_global_search and finder != importlib.machinery.PathFinder: - p = None - else: - p = path + implicit_ns_paths = [] + try: find_spec = finder.find_spec except AttributeError: @@ -149,17 +152,29 @@ def _find_module(string, path=None, full_name=None, is_global_search=True): # ignore those. continue - spec = find_spec(string, p) + spec = None + for path in paths_iter: + if is_global_search and finder != importlib.machinery.PathFinder: + p = None + else: + p = None if path is None else [path] + + spec = find_spec(string, p) + if spec is not None: + loader = spec.loader + if loader is None and not spec.has_location: + implicit_ns_paths.extend(spec.submodule_search_locations._path) + if spec is not None: - loader = spec.loader if loader is None and not spec.has_location: # This is a namespace package. - full_name = string if not path else full_name - implicit_ns_info = ImplicitNSInfo(full_name, spec.submodule_search_locations._path) + full_name = string if not paths else full_name + implicit_ns_info = ImplicitNSInfo(full_name, implicit_ns_paths) return implicit_ns_info, True + break - return _find_module_py33(string, path, loader) + return _find_module_py33(string, paths, loader) def _find_module_py33(string, path=None, loader=None, full_name=None, is_global_search=True): diff --git a/jedi/inference/imports.py b/jedi/inference/imports.py index 56d6ebb91a..b285c97a79 100644 --- a/jedi/inference/imports.py +++ b/jedi/inference/imports.py @@ -422,20 +422,13 @@ def import_module(inference_state, import_names, parent_module_value, sys_path): # The module might not be a package. return NO_VALUES - for path in paths: - # At the moment we are only using one path. So this is - # not important to be correct. - if not isinstance(path, list): - path = [path] - file_io_or_ns, is_pkg = inference_state.compiled_subprocess.get_module_info( - string=import_names[-1], - path=path, - full_name=module_name, - is_global_search=False, - ) - if is_pkg is not None: - break - else: + file_io_or_ns, is_pkg = inference_state.compiled_subprocess.get_module_info( + string=import_names[-1], + paths=paths, + full_name=module_name, + is_global_search=False, + ) + if is_pkg is None: return NO_VALUES if isinstance(file_io_or_ns, ImplicitNSInfo): From d17c1a13f5f6b3a351714da25d6ca3482ba5eb70 Mon Sep 17 00:00:00 2001 From: Bryan Bugyi Date: Fri, 2 Jul 2021 20:35:16 -0400 Subject: [PATCH 02/24] Add type hints to _find_module() function --- .../compiled/subprocess/functions.py | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/jedi/inference/compiled/subprocess/functions.py b/jedi/inference/compiled/subprocess/functions.py index e6b02a1cd6..e948230bb4 100644 --- a/jedi/inference/compiled/subprocess/functions.py +++ b/jedi/inference/compiled/subprocess/functions.py @@ -7,6 +7,7 @@ from zipfile import ZipFile from zipimport import zipimporter, ZipImportError from importlib.machinery import all_suffixes +from typing import Any, List, Optional, Sequence, cast from jedi.inference.compiled import access from jedi import debug @@ -124,7 +125,12 @@ def _iter_module_names(inference_state, paths): yield modname -def _find_module(string, paths=None, full_name=None, is_global_search=True): +def _find_module( + string: str, + paths: Sequence[str] = None, + full_name: str = None, + is_global_search: bool = True, +) -> Any: """ Provides information about a module. @@ -135,15 +141,15 @@ def _find_module(string, paths=None, full_name=None, is_global_search=True): if the module is contained in a package. """ if paths is None: - paths_iter = [None] + paths_iter: List[Optional[str]] = [None] else: - paths_iter = paths + paths_iter = list(paths) spec = None loader = None for finder in sys.meta_path: - implicit_ns_paths = [] + implicit_ns_paths: List[str] = [] try: find_spec = finder.find_spec @@ -154,7 +160,7 @@ def _find_module(string, paths=None, full_name=None, is_global_search=True): spec = None for path in paths_iter: - if is_global_search and finder != importlib.machinery.PathFinder: + if is_global_search and finder != importlib.machinery.PathFinder: # type: ignore p = None else: p = None if path is None else [path] @@ -163,7 +169,9 @@ def _find_module(string, paths=None, full_name=None, is_global_search=True): if spec is not None: loader = spec.loader if loader is None and not spec.has_location: - implicit_ns_paths.extend(spec.submodule_search_locations._path) + spec_locations = cast(Any, spec.submodule_search_locations) + spec_path = cast(str, spec_locations._path) + implicit_ns_paths.extend(spec_path) if spec is not None: if loader is None and not spec.has_location: From f979b2afde5157b74cb2348a4ab30b5348b9563a Mon Sep 17 00:00:00 2001 From: Bryan Bugyi Date: Sat, 3 Jul 2021 13:05:25 -0400 Subject: [PATCH 03/24] Add type signatures to get_module_info() function --- .../compiled/subprocess/functions.py | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/jedi/inference/compiled/subprocess/functions.py b/jedi/inference/compiled/subprocess/functions.py index e948230bb4..f6995deefd 100644 --- a/jedi/inference/compiled/subprocess/functions.py +++ b/jedi/inference/compiled/subprocess/functions.py @@ -9,6 +9,7 @@ from importlib.machinery import all_suffixes from typing import Any, List, Optional, Sequence, cast +from jedi.inference import InferenceState from jedi.inference.compiled import access from jedi import debug from jedi import parser_utils @@ -32,14 +33,29 @@ def create_simple_object(inference_state, obj): return access.create_access_path(inference_state, obj) -def get_module_info(inference_state, sys_path=None, full_name=None, **kwargs): +def get_module_info( + inference_state: InferenceState, + *, + string: str, + sys_path: List[str] = None, + full_name: str = None, + paths: Sequence[str] = None, + is_global_search: bool = True, +): """ Returns Tuple[Union[NamespaceInfo, FileIO, None], Optional[bool]] """ + del inference_state + if sys_path is not None: sys.path, temp = sys_path, sys.path try: - return _find_module(full_name=full_name, **kwargs) + return _find_module( + string=string, + full_name=full_name, + paths=paths, + is_global_search=is_global_search, + ) except ImportError: return None, None finally: @@ -144,7 +160,7 @@ def _find_module( paths_iter: List[Optional[str]] = [None] else: paths_iter = list(paths) - + spec = None loader = None From 39b8e07c3a88fd712b833ace1ccfe07cc06763de Mon Sep 17 00:00:00 2001 From: Bryan Bugyi Date: Sat, 3 Jul 2021 13:12:41 -0400 Subject: [PATCH 04/24] Add type annotations to import_module*() functions --- .../compiled/subprocess/functions.py | 9 +++++--- jedi/inference/imports.py | 22 ++++++++++++++++--- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/jedi/inference/compiled/subprocess/functions.py b/jedi/inference/compiled/subprocess/functions.py index f6995deefd..3be00a0d0a 100644 --- a/jedi/inference/compiled/subprocess/functions.py +++ b/jedi/inference/compiled/subprocess/functions.py @@ -7,15 +7,18 @@ from zipfile import ZipFile from zipimport import zipimporter, ZipImportError from importlib.machinery import all_suffixes -from typing import Any, List, Optional, Sequence, cast +from typing import Any, List, Optional, Sequence, TYPE_CHECKING, cast -from jedi.inference import InferenceState from jedi.inference.compiled import access from jedi import debug from jedi import parser_utils from jedi.file_io import KnownContentFileIO, ZipFileIO +if TYPE_CHECKING: + from jedi.inference import InferenceState + + def get_sys_path(): return sys.path @@ -34,7 +37,7 @@ def create_simple_object(inference_state, obj): def get_module_info( - inference_state: InferenceState, + inference_state: "InferenceState", *, string: str, sys_path: List[str] = None, diff --git a/jedi/inference/imports.py b/jedi/inference/imports.py index b285c97a79..5c1cdea1be 100644 --- a/jedi/inference/imports.py +++ b/jedi/inference/imports.py @@ -10,6 +10,7 @@ """ import os from pathlib import Path +from typing import Any, List, Sequence, TYPE_CHECKING from parso.python import tree from parso.tree import search_ancestor @@ -22,6 +23,7 @@ from jedi.inference import helpers from jedi.inference import compiled from jedi.inference import analysis +from jedi.inference.context import ModuleContext from jedi.inference.utils import unite from jedi.inference.cache import inference_state_method_cache from jedi.inference.names import ImportName, SubModuleName @@ -32,6 +34,10 @@ from jedi.plugins import plugin_manager +if TYPE_CHECKING: + from jedi.inference import InferenceState + + class ModuleCache: def __init__(self): self._name_cache = {} @@ -362,8 +368,13 @@ def completion_names(self, inference_state, only_modules=False): return names -def import_module_by_names(inference_state, import_names, sys_path=None, - module_context=None, prefer_stubs=True): +def import_module_by_names( + inference_state: "InferenceState", + import_names: Sequence[str], + sys_path: List[str] = None, + module_context: ModuleContext = None, + prefer_stubs: bool = True, +): if sys_path is None: sys_path = inference_state.get_sys_path() @@ -394,7 +405,12 @@ def import_module_by_names(inference_state, import_names, sys_path=None, @plugin_manager.decorate() @import_module_decorator -def import_module(inference_state, import_names, parent_module_value, sys_path): +def import_module( + inference_state: "InferenceState", + import_names: Sequence[str], + parent_module_value: Any, + sys_path: List[str], +): """ This method is very similar to importlib's `_gcd_import`. """ From 915bc000f7e38c0df1cb2d7336603780fcefbedb Mon Sep 17 00:00:00 2001 From: Bryan Bugyi Date: Sat, 3 Jul 2021 13:25:09 -0400 Subject: [PATCH 05/24] Add inline comment --- jedi/inference/compiled/subprocess/functions.py | 1 + 1 file changed, 1 insertion(+) diff --git a/jedi/inference/compiled/subprocess/functions.py b/jedi/inference/compiled/subprocess/functions.py index 3be00a0d0a..f56ea5a69f 100644 --- a/jedi/inference/compiled/subprocess/functions.py +++ b/jedi/inference/compiled/subprocess/functions.py @@ -188,6 +188,7 @@ def _find_module( if spec is not None: loader = spec.loader if loader is None and not spec.has_location: + # This is a namespace package. spec_locations = cast(Any, spec.submodule_search_locations) spec_path = cast(str, spec_locations._path) implicit_ns_paths.extend(spec_path) From cf0ae8bc39d5f2ef5cbed5fb3442f5413581e360 Mon Sep 17 00:00:00 2001 From: Bryan Bugyi Date: Sat, 3 Jul 2021 13:29:58 -0400 Subject: [PATCH 06/24] Improve return type annotations --- .../compiled/subprocess/functions.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/jedi/inference/compiled/subprocess/functions.py b/jedi/inference/compiled/subprocess/functions.py index f56ea5a69f..aa3767e5ee 100644 --- a/jedi/inference/compiled/subprocess/functions.py +++ b/jedi/inference/compiled/subprocess/functions.py @@ -7,7 +7,17 @@ from zipfile import ZipFile from zipimport import zipimporter, ZipImportError from importlib.machinery import all_suffixes -from typing import Any, List, Optional, Sequence, TYPE_CHECKING, cast +from io import FileIO +from typing import ( + Any, + List, + Optional, + Sequence, + Tuple, + TYPE_CHECKING, + Union, + cast, +) from jedi.inference.compiled import access from jedi import debug @@ -19,6 +29,9 @@ from jedi.inference import InferenceState +ModuleInfoResult = Tuple[Union[Any, FileIO, None], Optional[bool]] + + def get_sys_path(): return sys.path @@ -44,7 +57,7 @@ def get_module_info( full_name: str = None, paths: Sequence[str] = None, is_global_search: bool = True, -): +) -> ModuleInfoResult: """ Returns Tuple[Union[NamespaceInfo, FileIO, None], Optional[bool]] """ @@ -149,7 +162,7 @@ def _find_module( paths: Sequence[str] = None, full_name: str = None, is_global_search: bool = True, -) -> Any: +) -> ModuleInfoResult: """ Provides information about a module. From 48bcaa8f45e8f212562b0b0e59b0120e28c7853e Mon Sep 17 00:00:00 2001 From: Bryan Bugyi Date: Sat, 3 Jul 2021 18:18:58 -0400 Subject: [PATCH 07/24] Add tests for bugfix --- .../ns1/pkg/subpkg/ns3_file.py | 0 .../ns2/pkg/subpkg/ns4_file.py | 0 .../test_implicit_namespace_package.py | 41 +++++++++++++++---- 3 files changed, 33 insertions(+), 8 deletions(-) create mode 100644 test/examples/implicit_namespace_package/ns1/pkg/subpkg/ns3_file.py create mode 100644 test/examples/implicit_namespace_package/ns2/pkg/subpkg/ns4_file.py diff --git a/test/examples/implicit_namespace_package/ns1/pkg/subpkg/ns3_file.py b/test/examples/implicit_namespace_package/ns1/pkg/subpkg/ns3_file.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/examples/implicit_namespace_package/ns2/pkg/subpkg/ns4_file.py b/test/examples/implicit_namespace_package/ns2/pkg/subpkg/ns4_file.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/test_inference/test_implicit_namespace_package.py b/test/test_inference/test_implicit_namespace_package.py index 4fbbfccf93..bc8383d43e 100644 --- a/test/test_inference/test_implicit_namespace_package.py +++ b/test/test_inference/test_implicit_namespace_package.py @@ -1,4 +1,8 @@ -from test.helpers import get_example_dir, example_dir +from test.helpers import example_dir, get_example_dir +from typing import Any, Iterable + +import pytest + from jedi import Project @@ -68,19 +72,40 @@ def test_implicit_namespace_package_import_autocomplete(Script): assert [c.name for c in compl] == ['implicit_namespace_package'] -def test_namespace_package_in_multiple_directories_autocompletion(Script): - code = 'from pkg.' +@pytest.mark.parametrize( + 'code,expected', + [ + ('from pkg.', ['ns1_file', 'ns2_file', 'subpkg']), + ('from pkg.subpkg.', ['ns3_file', 'ns4_file']), + ] +) +def test_namespace_package_in_multiple_directories_autocompletion( + code: str, + expected: Iterable[str], + Script: Any, +) -> None: sys_path = [get_example_dir('implicit_namespace_package', 'ns1'), get_example_dir('implicit_namespace_package', 'ns2')] project = Project('.', sys_path=sys_path) script = Script(code, project=project) compl = script.complete() - assert set(c.name for c in compl) == set(['ns1_file', 'ns2_file']) - - -def test_namespace_package_in_multiple_directories_goto_definition(Script): - code = 'from pkg import ns1_file' + assert sorted(c.name for c in compl) == sorted(expected) + + +@pytest.mark.parametrize( + 'code', + [ + 'from pkg import ns1_file', + 'from pkg import ns2_file', + 'from pkg.subpkg import ns3_file', + 'from pkg.subpkg import ns4_file', + ] +) +def test_namespace_package_in_multiple_directories_goto_definition( + code: str, + Script: Any, +) -> None: sys_path = [get_example_dir('implicit_namespace_package', 'ns1'), get_example_dir('implicit_namespace_package', 'ns2')] project = Project('.', sys_path=sys_path) From 0396e72befa442565ce8a03edd6af595b8eb7611 Mon Sep 17 00:00:00 2001 From: Bryan Bugyi Date: Sat, 3 Jul 2021 18:25:38 -0400 Subject: [PATCH 08/24] Revert 'sorted' back to 'set' --- test/test_inference/test_implicit_namespace_package.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_inference/test_implicit_namespace_package.py b/test/test_inference/test_implicit_namespace_package.py index bc8383d43e..f4929efce2 100644 --- a/test/test_inference/test_implicit_namespace_package.py +++ b/test/test_inference/test_implicit_namespace_package.py @@ -90,7 +90,7 @@ def test_namespace_package_in_multiple_directories_autocompletion( project = Project('.', sys_path=sys_path) script = Script(code, project=project) compl = script.complete() - assert sorted(c.name for c in compl) == sorted(expected) + assert set(c.name for c in compl) == set(expected) @pytest.mark.parametrize( From 120be69d878e620bad66c168eb9e7e8f08ccc391 Mon Sep 17 00:00:00 2001 From: Bryan Bugyi Date: Sat, 3 Jul 2021 18:34:52 -0400 Subject: [PATCH 09/24] Fix tests that were broken by adding new example directories --- test/test_api/test_project.py | 3 ++- test/test_inference/test_implicit_namespace_package.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/test/test_api/test_project.py b/test/test_api/test_project.py index f2a5e99103..20d2dc6ee5 100644 --- a/test/test_api/test_project.py +++ b/test/test_api/test_project.py @@ -98,7 +98,8 @@ def test_load_save_project(tmpdir): ('examples.implicit_namespace_package.ns1.pkg.ns1_file', ['examples.implicit_namespace_package.ns1.pkg.ns1_file'], {}), ('implicit_namespace_package.ns1.pkg.', - ['examples.implicit_namespace_package.ns1.pkg.ns1_file'], + ['examples.implicit_namespace_package.ns1.pkg.ns1_file', + 'examples.implicit_namespace_package.ns1.pkg.subpkg'], dict(complete=True)), ('implicit_namespace_package.', ['examples.implicit_namespace_package.ns1', diff --git a/test/test_inference/test_implicit_namespace_package.py b/test/test_inference/test_implicit_namespace_package.py index f4929efce2..e1fc3acbe7 100644 --- a/test/test_inference/test_implicit_namespace_package.py +++ b/test/test_inference/test_implicit_namespace_package.py @@ -32,7 +32,7 @@ def script_with_path(*args, **kwargs): # completion completions = script_with_path('from pkg import ').complete() names = [c.name for c in completions] - compare = ['ns1_file', 'ns2_file'] + compare = ['ns1_file', 'ns2_file', 'subpkg'] # must at least contain these items, other items are not important assert set(compare) == set(names) From ca4ebe226c7654f3695b44faf197468315a6fdf9 Mon Sep 17 00:00:00 2001 From: Bryan Bugyi Date: Sat, 3 Jul 2021 18:46:08 -0400 Subject: [PATCH 10/24] Fix unrelated test_find_module_not_package_zipped() test Not sure how this is passing in CI but failing locally. --- test/test_inference/test_imports.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_inference/test_imports.py b/test/test_inference/test_imports.py index 51e654741e..70bc50ce2b 100644 --- a/test/test_inference/test_imports.py +++ b/test/test_inference/test_imports.py @@ -118,7 +118,7 @@ def test_find_module_not_package_zipped(Script, inference_state, environment): assert len(script.complete()) == 1 file_io, is_package = inference_state.compiled_subprocess.get_module_info( - sys_path=map(str, sys_path), + sys_path=list(map(str, sys_path)), string='not_pkg', full_name='not_pkg' ) From 53980af478564744087886b24f5f66ae683fd72d Mon Sep 17 00:00:00 2001 From: Bryan Bugyi Date: Sat, 3 Jul 2021 19:25:54 -0400 Subject: [PATCH 11/24] Remove unused function --- jedi/inference/compiled/subprocess/functions.py | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/jedi/inference/compiled/subprocess/functions.py b/jedi/inference/compiled/subprocess/functions.py index aa3767e5ee..0c4d33ea06 100644 --- a/jedi/inference/compiled/subprocess/functions.py +++ b/jedi/inference/compiled/subprocess/functions.py @@ -102,18 +102,6 @@ def _test_print(inference_state, stderr=None, stdout=None): sys.stdout.flush() -def _get_init_path(directory_path): - """ - The __init__ file can be searched in a directory. If found return it, else - None. - """ - for suffix in all_suffixes(): - path = os.path.join(directory_path, '__init__' + suffix) - if os.path.exists(path): - return path - return None - - def safe_literal_eval(inference_state, value): return parser_utils.safe_literal_eval(value) From 708eb3be8dab622493c55e2e52ee56047fb32d83 Mon Sep 17 00:00:00 2001 From: Bryan Bugyi Date: Sat, 3 Jul 2021 20:06:36 -0400 Subject: [PATCH 12/24] Remove type signature from import_module_by_names() ...since I never touched this function for anything else. --- jedi/inference/imports.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/jedi/inference/imports.py b/jedi/inference/imports.py index 5c1cdea1be..a208105d34 100644 --- a/jedi/inference/imports.py +++ b/jedi/inference/imports.py @@ -23,7 +23,6 @@ from jedi.inference import helpers from jedi.inference import compiled from jedi.inference import analysis -from jedi.inference.context import ModuleContext from jedi.inference.utils import unite from jedi.inference.cache import inference_state_method_cache from jedi.inference.names import ImportName, SubModuleName @@ -368,13 +367,8 @@ def completion_names(self, inference_state, only_modules=False): return names -def import_module_by_names( - inference_state: "InferenceState", - import_names: Sequence[str], - sys_path: List[str] = None, - module_context: ModuleContext = None, - prefer_stubs: bool = True, -): +def import_module_by_names(inference_state, import_names, sys_path=None, + module_context=None, prefer_stubs=True): if sys_path is None: sys_path = inference_state.get_sys_path() @@ -410,7 +404,7 @@ def import_module( import_names: Sequence[str], parent_module_value: Any, sys_path: List[str], -): +) -> ValueSet: """ This method is very similar to importlib's `_gcd_import`. """ From 6d8b66e4f1cd365b47736e8a6a29562a4ec5c2c5 Mon Sep 17 00:00:00 2001 From: Bryan Bugyi Date: Sun, 4 Jul 2021 14:26:58 -0400 Subject: [PATCH 13/24] Change type on sys_path to Sequence[str] --- jedi/inference/compiled/subprocess/functions.py | 4 ++-- jedi/inference/imports.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/jedi/inference/compiled/subprocess/functions.py b/jedi/inference/compiled/subprocess/functions.py index 0c4d33ea06..5cef09ce17 100644 --- a/jedi/inference/compiled/subprocess/functions.py +++ b/jedi/inference/compiled/subprocess/functions.py @@ -53,7 +53,7 @@ def get_module_info( inference_state: "InferenceState", *, string: str, - sys_path: List[str] = None, + sys_path: Sequence[str] = None, full_name: str = None, paths: Sequence[str] = None, is_global_search: bool = True, @@ -64,7 +64,7 @@ def get_module_info( del inference_state if sys_path is not None: - sys.path, temp = sys_path, sys.path + sys.path, temp = list(sys_path), sys.path try: return _find_module( string=string, diff --git a/jedi/inference/imports.py b/jedi/inference/imports.py index a208105d34..bc0c79087b 100644 --- a/jedi/inference/imports.py +++ b/jedi/inference/imports.py @@ -10,7 +10,7 @@ """ import os from pathlib import Path -from typing import Any, List, Sequence, TYPE_CHECKING +from typing import Any, Sequence, TYPE_CHECKING from parso.python import tree from parso.tree import search_ancestor @@ -403,7 +403,7 @@ def import_module( inference_state: "InferenceState", import_names: Sequence[str], parent_module_value: Any, - sys_path: List[str], + sys_path: Sequence[str], ) -> ValueSet: """ This method is very similar to importlib's `_gcd_import`. From 6aad95a589cc72cd75555c19efeaa28984329fb8 Mon Sep 17 00:00:00 2001 From: Bryan Bugyi Date: Sun, 4 Jul 2021 14:45:34 -0400 Subject: [PATCH 14/24] Add bullet to changelog --- CHANGELOG.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 3cc23d4303..742f8e27cf 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -7,6 +7,7 @@ Unreleased ++++++++++ - Implict namespaces are now a separate types in ``Name().type`` +- Fix bug finding implicit namespace packages (`PR:#1784 `_, `Issue:#1759 `_) 0.18.0 (2020-12-25) +++++++++++++++++++ From fe0e9f6e5b0b6b33eaa389f9307a834f09924f15 Mon Sep 17 00:00:00 2001 From: Bryan Bugyi Date: Sun, 4 Jul 2021 14:47:52 -0400 Subject: [PATCH 15/24] Add my name to AUTHORS.txt --- AUTHORS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.txt b/AUTHORS.txt index b8b10a9374..a22618872b 100644 --- a/AUTHORS.txt +++ b/AUTHORS.txt @@ -62,6 +62,7 @@ Code Contributors - Andrii Kolomoiets (@muffinmad) - Leo Ryu (@Leo-Ryu) - Joseph Birkner (@josephbirkner) +- Bryan Bugyi (@bbugyi200) And a few more "anonymous" contributors. From 3fa026a821f0ad0e743a75c96e0d74aac23f3fba Mon Sep 17 00:00:00 2001 From: Bryan Bugyi Date: Sun, 4 Jul 2021 14:49:05 -0400 Subject: [PATCH 16/24] Reverse PR/issue links in changelog bullet --- CHANGELOG.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 742f8e27cf..8b0510a3a4 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -7,7 +7,7 @@ Unreleased ++++++++++ - Implict namespaces are now a separate types in ``Name().type`` -- Fix bug finding implicit namespace packages (`PR:#1784 `_, `Issue:#1759 `_) +- Fix bug finding implicit namespace packages (`Issue:#1759 `_, `PR:#1784 `_) 0.18.0 (2020-12-25) +++++++++++++++++++ From a51f1e82fb3aa42d92efaac689b6f4bf1155bb4d Mon Sep 17 00:00:00 2001 From: Bryan Bugyi Date: Sun, 4 Jul 2021 15:02:51 -0400 Subject: [PATCH 17/24] Simplify main fix in _find_module() function --- .../compiled/subprocess/functions.py | 31 ++++++------------- 1 file changed, 9 insertions(+), 22 deletions(-) diff --git a/jedi/inference/compiled/subprocess/functions.py b/jedi/inference/compiled/subprocess/functions.py index 5cef09ce17..f128b2c78d 100644 --- a/jedi/inference/compiled/subprocess/functions.py +++ b/jedi/inference/compiled/subprocess/functions.py @@ -160,17 +160,10 @@ def _find_module( or the name of the module if it is a builtin one and a boolean indicating if the module is contained in a package. """ - if paths is None: - paths_iter: List[Optional[str]] = [None] - else: - paths_iter = list(paths) - spec = None loader = None for finder in sys.meta_path: - implicit_ns_paths: List[str] = [] - try: find_spec = finder.find_spec except AttributeError: @@ -179,26 +172,20 @@ def _find_module( continue spec = None - for path in paths_iter: - if is_global_search and finder != importlib.machinery.PathFinder: # type: ignore - p = None - else: - p = None if path is None else [path] - - spec = find_spec(string, p) - if spec is not None: - loader = spec.loader - if loader is None and not spec.has_location: - # This is a namespace package. - spec_locations = cast(Any, spec.submodule_search_locations) - spec_path = cast(str, spec_locations._path) - implicit_ns_paths.extend(spec_path) + if is_global_search and finder != importlib.machinery.PathFinder: # type: ignore + p = None + else: + p = paths + spec = find_spec(string, p) if spec is not None: + loader = spec.loader if loader is None and not spec.has_location: # This is a namespace package. + spec_locations = cast(Any, spec.submodule_search_locations) + spec_path = cast(str, spec_locations._path) full_name = string if not paths else full_name - implicit_ns_info = ImplicitNSInfo(full_name, implicit_ns_paths) + implicit_ns_info = ImplicitNSInfo(full_name, spec_path) return implicit_ns_info, True break From ead07aa3ff5e087316438c6019ae77713427f7ab Mon Sep 17 00:00:00 2001 From: Bryan Bugyi Date: Sun, 4 Jul 2021 15:03:40 -0400 Subject: [PATCH 18/24] Simplify changes to _find_module() further --- jedi/inference/compiled/subprocess/functions.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/jedi/inference/compiled/subprocess/functions.py b/jedi/inference/compiled/subprocess/functions.py index f128b2c78d..92f647cab1 100644 --- a/jedi/inference/compiled/subprocess/functions.py +++ b/jedi/inference/compiled/subprocess/functions.py @@ -164,6 +164,11 @@ def _find_module( loader = None for finder in sys.meta_path: + if is_global_search and finder != importlib.machinery.PathFinder: # type: ignore + p = None + else: + p = paths + try: find_spec = finder.find_spec except AttributeError: @@ -171,12 +176,6 @@ def _find_module( # ignore those. continue - spec = None - if is_global_search and finder != importlib.machinery.PathFinder: # type: ignore - p = None - else: - p = paths - spec = find_spec(string, p) if spec is not None: loader = spec.loader From 68161e5e0a92b9911219dc48644dd6dca4f012d4 Mon Sep 17 00:00:00 2001 From: Bryan Bugyi Date: Sun, 4 Jul 2021 15:06:11 -0400 Subject: [PATCH 19/24] Last simplification --- jedi/inference/compiled/subprocess/functions.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/jedi/inference/compiled/subprocess/functions.py b/jedi/inference/compiled/subprocess/functions.py index 92f647cab1..c07de58626 100644 --- a/jedi/inference/compiled/subprocess/functions.py +++ b/jedi/inference/compiled/subprocess/functions.py @@ -181,10 +181,11 @@ def _find_module( loader = spec.loader if loader is None and not spec.has_location: # This is a namespace package. - spec_locations = cast(Any, spec.submodule_search_locations) - spec_path = cast(str, spec_locations._path) full_name = string if not paths else full_name - implicit_ns_info = ImplicitNSInfo(full_name, spec_path) + implicit_ns_info = ImplicitNSInfo( + full_name, + spec.submodule_search_locations._path, # type: ignore + ) return implicit_ns_info, True break From 44706a3363d2ed78e445dce30e3e3c0780a4e9ed Mon Sep 17 00:00:00 2001 From: Bryan Bugyi Date: Sun, 4 Jul 2021 15:15:16 -0400 Subject: [PATCH 20/24] Remove unused imports --- jedi/inference/compiled/subprocess/functions.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/jedi/inference/compiled/subprocess/functions.py b/jedi/inference/compiled/subprocess/functions.py index c07de58626..0ce0665b34 100644 --- a/jedi/inference/compiled/subprocess/functions.py +++ b/jedi/inference/compiled/subprocess/functions.py @@ -6,17 +6,14 @@ from pathlib import Path from zipfile import ZipFile from zipimport import zipimporter, ZipImportError -from importlib.machinery import all_suffixes from io import FileIO from typing import ( Any, - List, Optional, Sequence, Tuple, TYPE_CHECKING, Union, - cast, ) from jedi.inference.compiled import access From 149148ceaff4cc3ed474d8ffb5bfd7398b7d6774 Mon Sep 17 00:00:00 2001 From: Bryan Bugyi Date: Sun, 4 Jul 2021 15:26:56 -0400 Subject: [PATCH 21/24] Change name from 'path' to 'paths' --- jedi/inference/compiled/subprocess/functions.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/jedi/inference/compiled/subprocess/functions.py b/jedi/inference/compiled/subprocess/functions.py index 0ce0665b34..7ef32e8dcf 100644 --- a/jedi/inference/compiled/subprocess/functions.py +++ b/jedi/inference/compiled/subprocess/functions.py @@ -190,10 +190,10 @@ def _find_module( return _find_module_py33(string, paths, loader) -def _find_module_py33(string, path=None, loader=None, full_name=None, is_global_search=True): - loader = loader or importlib.machinery.PathFinder.find_module(string, path) +def _find_module_py33(string, paths=None, loader=None, full_name=None, is_global_search=True): + loader = loader or importlib.machinery.PathFinder.find_module(string, paths) - if loader is None and path is None: # Fallback to find builtins + if loader is None and paths is None: # Fallback to find builtins try: with warnings.catch_warnings(record=True): # Mute "DeprecationWarning: Use importlib.util.find_spec() From daecd8b6fba9d855f03c6070345f1479d1b940c1 Mon Sep 17 00:00:00 2001 From: Bryan Bugyi Date: Sun, 4 Jul 2021 15:37:03 -0400 Subject: [PATCH 22/24] Rename 'paths' back to 'path' since that's what importlib uses --- jedi/inference/compiled/subprocess/functions.py | 16 ++++++++-------- jedi/inference/imports.py | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/jedi/inference/compiled/subprocess/functions.py b/jedi/inference/compiled/subprocess/functions.py index 7ef32e8dcf..f4ced58a76 100644 --- a/jedi/inference/compiled/subprocess/functions.py +++ b/jedi/inference/compiled/subprocess/functions.py @@ -66,7 +66,7 @@ def get_module_info( return _find_module( string=string, full_name=full_name, - paths=paths, + path=path, is_global_search=is_global_search, ) except ImportError: @@ -144,7 +144,7 @@ def _iter_module_names(inference_state, paths): def _find_module( string: str, - paths: Sequence[str] = None, + path: Sequence[str] = None, full_name: str = None, is_global_search: bool = True, ) -> ModuleInfoResult: @@ -164,7 +164,7 @@ def _find_module( if is_global_search and finder != importlib.machinery.PathFinder: # type: ignore p = None else: - p = paths + p = path try: find_spec = finder.find_spec @@ -178,7 +178,7 @@ def _find_module( loader = spec.loader if loader is None and not spec.has_location: # This is a namespace package. - full_name = string if not paths else full_name + full_name = string if not path else full_name implicit_ns_info = ImplicitNSInfo( full_name, spec.submodule_search_locations._path, # type: ignore @@ -187,13 +187,13 @@ def _find_module( break - return _find_module_py33(string, paths, loader) + return _find_module_py33(string, path, loader) -def _find_module_py33(string, paths=None, loader=None, full_name=None, is_global_search=True): - loader = loader or importlib.machinery.PathFinder.find_module(string, paths) +def _find_module_py33(string, path=None, loader=None, full_name=None, is_global_search=True): + loader = loader or importlib.machinery.PathFinder.find_module(string, path) - if loader is None and paths is None: # Fallback to find builtins + if loader is None and path is None: # Fallback to find builtins try: with warnings.catch_warnings(record=True): # Mute "DeprecationWarning: Use importlib.util.find_spec() diff --git a/jedi/inference/imports.py b/jedi/inference/imports.py index bc0c79087b..1eb1ca7ca7 100644 --- a/jedi/inference/imports.py +++ b/jedi/inference/imports.py @@ -434,7 +434,7 @@ def import_module( file_io_or_ns, is_pkg = inference_state.compiled_subprocess.get_module_info( string=import_names[-1], - paths=paths, + path=path, full_name=module_name, is_global_search=False, ) From 33d5a6e9253ee2194a3e344322e88733ead6d709 Mon Sep 17 00:00:00 2001 From: Bryan Bugyi Date: Sun, 4 Jul 2021 15:38:06 -0400 Subject: [PATCH 23/24] Add type annotations to two more functions in _functions.py --- jedi/inference/compiled/subprocess/functions.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/jedi/inference/compiled/subprocess/functions.py b/jedi/inference/compiled/subprocess/functions.py index f4ced58a76..ffb16a28c3 100644 --- a/jedi/inference/compiled/subprocess/functions.py +++ b/jedi/inference/compiled/subprocess/functions.py @@ -190,7 +190,11 @@ def _find_module( return _find_module_py33(string, path, loader) -def _find_module_py33(string, path=None, loader=None, full_name=None, is_global_search=True): +def _find_module_py33( + string: str, + path: Sequence[str] = None, + loader: Any = None, +) -> ModuleInfoResult: loader = loader or importlib.machinery.PathFinder.find_module(string, path) if loader is None and path is None: # Fallback to find builtins @@ -213,7 +217,7 @@ def _find_module_py33(string, path=None, loader=None, full_name=None, is_global_ return _from_loader(loader, string) -def _from_loader(loader, string): +def _from_loader(loader: Any, string: str) -> ModuleInfoResult: try: is_package_method = loader.is_package except AttributeError: From 06bac01a697253c720ecd74d6432bf06ac4cd72d Mon Sep 17 00:00:00 2001 From: Bryan Bugyi Date: Sun, 4 Jul 2021 15:39:06 -0400 Subject: [PATCH 24/24] Rename last 'paths' to 'path' --- jedi/inference/compiled/subprocess/functions.py | 2 +- jedi/inference/imports.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/jedi/inference/compiled/subprocess/functions.py b/jedi/inference/compiled/subprocess/functions.py index ffb16a28c3..8cfbb16de4 100644 --- a/jedi/inference/compiled/subprocess/functions.py +++ b/jedi/inference/compiled/subprocess/functions.py @@ -52,7 +52,7 @@ def get_module_info( string: str, sys_path: Sequence[str] = None, full_name: str = None, - paths: Sequence[str] = None, + path: Sequence[str] = None, is_global_search: bool = True, ) -> ModuleInfoResult: """ diff --git a/jedi/inference/imports.py b/jedi/inference/imports.py index 1eb1ca7ca7..284dd3e974 100644 --- a/jedi/inference/imports.py +++ b/jedi/inference/imports.py @@ -434,7 +434,7 @@ def import_module( file_io_or_ns, is_pkg = inference_state.compiled_subprocess.get_module_info( string=import_names[-1], - path=path, + path=paths, full_name=module_name, is_global_search=False, )