diff --git a/jedi/_compatibility.py b/jedi/_compatibility.py index 3245ae07..d7008941 100644 --- a/jedi/_compatibility.py +++ b/jedi/_compatibility.py @@ -11,7 +11,6 @@ import os import re import pkgutil import warnings -import inspect import subprocess import weakref try: @@ -219,65 +218,6 @@ if the module is contained in a package. """ -def _iter_modules(paths, prefix=''): - # Copy of pkgutil.iter_modules adapted to work with namespaces - - for path in paths: - importer = pkgutil.get_importer(path) - - if not isinstance(importer, importlib.machinery.FileFinder): - # We're only modifying the case for FileFinder. All the other cases - # still need to be checked (like zip-importing). Do this by just - # calling the pkgutil version. - for mod_info in pkgutil.iter_modules([path], prefix): - yield mod_info - continue - - # START COPY OF pkutils._iter_file_finder_modules. - if importer.path is None or not os.path.isdir(importer.path): - return - - yielded = {} - - try: - filenames = os.listdir(importer.path) - except OSError: - # ignore unreadable directories like import does - filenames = [] - filenames.sort() # handle packages before same-named modules - - for fn in filenames: - modname = inspect.getmodulename(fn) - if modname == '__init__' or modname in yielded: - continue - - # jedi addition: Avoid traversing special directories - if fn.startswith('.') or fn == '__pycache__': - continue - - path = os.path.join(importer.path, fn) - ispkg = False - - if not modname and os.path.isdir(path) and '.' not in fn: - modname = fn - # A few jedi modifications: Don't check if there's an - # __init__.py - try: - os.listdir(path) - except OSError: - # ignore unreadable directories like import does - continue - ispkg = True - - if modname and '.' not in modname: - yielded[modname] = 1 - yield importer, prefix + modname, ispkg - # END COPY - - -iter_modules = _iter_modules if py_version >= 35 else pkgutil.iter_modules - - class ImplicitNSInfo(object): """Stores information returned from an implicit namespace spec""" def __init__(self, name, paths): diff --git a/jedi/inference/compiled/subprocess/functions.py b/jedi/inference/compiled/subprocess/functions.py index 71749346..fe2c4d18 100644 --- a/jedi/inference/compiled/subprocess/functions.py +++ b/jedi/inference/compiled/subprocess/functions.py @@ -3,7 +3,7 @@ import sys import os from jedi._compatibility import find_module, cast_path, force_unicode, \ - iter_modules, all_suffixes + all_suffixes from jedi.inference.compiled import access from jedi import parser_utils @@ -40,13 +40,6 @@ def get_module_info(inference_state, sys_path=None, full_name=None, **kwargs): sys.path = temp -def list_module_names(inference_state, search_path): - return [ - force_unicode(name) - for module_loader, name, is_pkg in iter_modules(search_path) - ] - - def get_builtin_module_names(inference_state): return list(map(force_unicode, sys.builtin_module_names)) diff --git a/jedi/inference/value/module.py b/jedi/inference/value/module.py index 8f2ba9e1..70c86323 100644 --- a/jedi/inference/value/module.py +++ b/jedi/inference/value/module.py @@ -1,6 +1,7 @@ import re import os +from jedi._compatibility import scandir from jedi import debug from jedi.inference.cache import inference_state_method_cache from jedi.inference.names import AbstractNameDefinition, ModuleName @@ -39,28 +40,30 @@ class _ModuleAttributeName(AbstractNameDefinition): def iter_module_names(inference_state, paths): # Python modules/packages - for n in inference_state.compiled_subprocess.list_module_names(paths): - yield n - for path in paths: try: - dirs = os.listdir(path) + dirs = scandir(path) except OSError: # The file might not exist or reading it might lead to an error. debug.warning("Not possible to list directory: %s", path) continue - for name in dirs: - # Namespaces - if os.path.isdir(os.path.join(path, name)): + for dir_entry in dirs: + name = dir_entry.name + # First Namespaces then modules/stubs + if dir_entry.is_dir(): # pycache is obviously not an interestin namespace. Also the # name must be a valid identifier. # TODO use str.isidentifier, once Python 2 is removed if name != '__pycache__' and not re.search(r'\W|^\d', name): yield name - # Stub files - if name.endswith('.pyi'): - if name != '__init__.pyi': - yield name[:-4] + else: + if name.endswith('.py'): + if name != '__init__.py': + yield name[:-3] + # Stub files + elif name.endswith('.pyi'): + if name != '__init__.pyi': + yield name[:-4] class SubModuleDictMixin(object): diff --git a/test/test_api/test_project.py b/test/test_api/test_project.py index 54bc0bbb..22deaa79 100644 --- a/test/test_api/test_project.py +++ b/test/test_api/test_project.py @@ -76,21 +76,16 @@ def test_load_save_project(tmpdir): # With namespace ('implicit_namespace_package.ns1.pkg', - ['test_inference.implicit_namespace_package.ns1.pkg', - 'examples.implicit_namespace_package.ns1.pkg'], {}), + ['examples.implicit_namespace_package.ns1.pkg'], {}), ('implicit_namespace_package.ns1.pkg.ns1_file', - ['test_inference.implicit_namespace_package.ns1.pkg.ns1_file', - 'examples.implicit_namespace_package.ns1.pkg.ns1_file'], {}), + ['examples.implicit_namespace_package.ns1.pkg.ns1_file'], {}), ('examples.implicit_namespace_package.ns1.pkg.ns1_file', ['examples.implicit_namespace_package.ns1.pkg.ns1_file'], {}), ('implicit_namespace_package.ns1.pkg.', - ['test_inference.implicit_namespace_package.ns1.pkg.ns1_file', - 'examples.implicit_namespace_package.ns1.pkg.ns1_file'], + ['examples.implicit_namespace_package.ns1.pkg.ns1_file'], dict(complete=True)), ('implicit_namespace_package.', - ['test_inference.implicit_namespace_package.ns1', - 'test_inference.implicit_namespace_package.ns2', - 'examples.implicit_namespace_package.ns1', + ['examples.implicit_namespace_package.ns1', 'examples.implicit_namespace_package.ns2'], dict(complete=True)),