diff --git a/jedi/api/completion.py b/jedi/api/completion.py index 76771669..ef95f740 100644 --- a/jedi/api/completion.py +++ b/jedi/api/completion.py @@ -20,7 +20,7 @@ from jedi.inference.helpers import infer_call_of_leaf, parse_dotted_names from jedi.inference.context import get_global_filters from jedi.inference.value import TreeInstance, ModuleValue from jedi.inference.names import ParamNameWrapper -from jedi.inference.gradual.conversion import convert_values +from jedi.inference.gradual.conversion import convert_values, convert_names from jedi.parser_utils import cut_value_at_position from jedi.plugins import plugin_manager @@ -577,7 +577,7 @@ def _complete_getattr(user_context, instance): def search_in_module(inference_state, module_context, names, wanted_names, wanted_type, complete=False, fuzzy=False, - ignore_imports=False): + ignore_imports=False, convert=False): for s in wanted_names[:-1]: new_names = [] for n in names: @@ -597,6 +597,10 @@ def search_in_module(inference_state, module_context, names, wanted_names, string = n.string_name.lower() if complete and helpers.match(string, last_name, fuzzy=fuzzy) \ or not complete and string == last_name: - def_ = classes.Definition(inference_state, n) - if not wanted_type or wanted_type == def_.type: - yield def_ + names = [n] + if convert: + names = convert_names(names) + for n2 in names: + def_ = classes.Definition(inference_state, n2) + if not wanted_type or wanted_type == def_.type: + yield def_ diff --git a/jedi/api/project.py b/jedi/api/project.py index 49c335f4..c04d703c 100644 --- a/jedi/api/project.py +++ b/jedi/api/project.py @@ -188,9 +188,10 @@ class Project(object): raise NotImplementedError( "No support for refactorings/search on Python 2/3.5" ) - debug.dbg('Search for string %s', string) + debug.dbg('Search for string %s, complete=%s', string, complete) wanted_type, wanted_names = split_search_string(string) name = wanted_names[0] + stub_folder_name = name + '-stubs' ios = recurse_find_python_folders_and_files(FolderIO(self._path)) file_ios = [] @@ -199,7 +200,7 @@ class Project(object): for folder_io, file_io in ios: if file_io is None: file_name = folder_io.get_base_name() - if file_name == name: + if file_name == name or file_name == stub_folder_name: f = folder_io.get_file_io('__init__.py') try: m = load_module_from_path(inference_state, f).as_context() @@ -226,7 +227,8 @@ class Project(object): names=[m.name], wanted_type=wanted_type, wanted_names=wanted_names, - complete=complete + complete=complete, + convert=True, ): yield x # Python 2... @@ -259,6 +261,7 @@ class Project(object): wanted_type=wanted_type, wanted_names=wanted_names, complete=complete, + convert=True, ): yield x # Python 2... diff --git a/jedi/inference/sys_path.py b/jedi/inference/sys_path.py index 81b14624..32a3f6b0 100644 --- a/jedi/inference/sys_path.py +++ b/jedi/inference/sys_path.py @@ -1,4 +1,5 @@ import os +import re from jedi._compatibility import unicode, force_unicode, all_suffixes from jedi.inference.cache import inference_state_method_cache @@ -254,7 +255,9 @@ def transform_path_to_dotted(sys_path, module_path): # is very strange and is probably a file that is called # `.py`. return - yield tuple(split) + # Stub folders for foo can end with foo-stubs. Just remove + # it. + yield tuple(re.sub(r'-stubs$', '', s) for s in split) potential_solutions = tuple(iter_potential_solutions()) if not potential_solutions: diff --git a/test/test_api/test_project.py b/test/test_api/test_project.py index b86d5813..dbd5cd37 100644 --- a/test/test_api/test_project.py +++ b/test/test_api/test_project.py @@ -93,17 +93,19 @@ def test_load_save_project(tmpdir): ('with_python.module', ['examples.stub_packages.with_python.module'], {}), ('with_python.modul', ['examples.stub_packages.with_python.module'], dict(complete=True)), - ('no_python.module', ['stub:examples.stub_packages.no_python.module'], {}), - ('no_python.modul', ['stub:examples.stub_packages.no_python.module'], + ('no_python.foo', ['stub:examples.stub_packages.no_python.foo'], {}), + ('no_python.fo', ['stub:examples.stub_packages.no_python.foo'], dict(complete=True)), - ('with_python-stubs.module', ['stub:examples.stub_packages.with_python-stubs.module'], {}), - ('no_python-stubs.module', ['stub:examples.stub_packages.no_python-stubs.module'], {}), + ('with_python-stubs.module', [], {}), + ('no_python-stubs.foo', [], {}), + ('with_python', ['stub:examples.stub_packages.with_python', 'examples.stub_packages.with_python'], {}), + ('no_python', ['stub:examples.stub_packages.no_python'], {}), # On sys path ('sys.path', ['stub:sys.path'], {}), - ('json.dumps', ['stub:json.dumps', 'json.dumps'], {}), # stdlib + stub - ('multiprocessing', ['stub:multiprocessing'], {}), - ('multiprocessin', ['stub:multiprocessing'], dict(complete=True)), + ('json.dumps', ['json.dumps'], {}), # stdlib + stub + ('multiprocessing', ['multiprocessing'], {}), + ('multiprocessin', ['multiprocessing'], dict(complete=True)), ] ) @pytest.mark.skipif(sys.version_info < (3, 6), reason="Ignore Python 2, because EOL")