1
0
forked from VimPlug/jedi

Fixed ZIP completion.

This commit is contained in:
Joseph Birkner
2021-04-28 19:12:58 +02:00
parent dcea842ac2
commit a340fe077e
3 changed files with 38 additions and 8 deletions

View File

@@ -61,6 +61,7 @@ Code Contributors
- Vladislav Serebrennikov (@endilll)
- Andrii Kolomoiets (@muffinmad)
- Leo Ryu (@Leo-Ryu)
- Joseph Birkner (@josephbirkner)
And a few more "anonymous" contributors.

View File

@@ -4,7 +4,8 @@ import inspect
import importlib
import warnings
from pathlib import Path
from zipimport import zipimporter
from zipfile import ZipFile
from zipimport import zipimporter, ZipImportError
from importlib.machinery import all_suffixes
from jedi.inference.compiled import access
@@ -92,15 +93,22 @@ def _iter_module_names(inference_state, paths):
# Python modules/packages
for path in paths:
try:
dirs = os.scandir(path)
dir_entries = ((entry.name, entry.is_dir()) for entry in os.scandir(path))
except OSError:
try:
zip_import_info = zipimporter(path)
# Unfortunately, there is no public way to access zipimporter's
# private _files member. We therefore have to use a
# custom function to iterate over the files.
dir_entries = _zip_list_subdirectory(
zip_import_info.archive, zip_import_info.prefix)
except ZipImportError:
# The file might not exist or reading it might lead to an error.
debug.warning("Not possible to list directory: %s", path)
continue
for dir_entry in dirs:
name = dir_entry.name
for name, is_dir in dir_entries:
# First Namespaces then modules/stubs
if dir_entry.is_dir():
if is_dir:
# pycache is obviously not an interesting namespace. Also the
# name must be a valid identifier.
if name != '__pycache__' and name.isidentifier():
@@ -229,6 +237,17 @@ def _get_source(loader, fullname):
name=fullname)
def _zip_list_subdirectory(zip_path, zip_subdir_path):
zip_file = ZipFile(zip_path)
zip_subdir_path = Path(zip_subdir_path)
zip_content_file_paths = zip_file.namelist()
for raw_file_name in zip_content_file_paths:
file_path = Path(raw_file_name)
if file_path.parent == zip_subdir_path:
file_path = file_path.relative_to(zip_subdir_path)
yield file_path.name, raw_file_name.endswith("/")
class ImplicitNSInfo:
"""Stores information returned from an implicit namespace spec"""
def __init__(self, name, paths):

View File

@@ -101,6 +101,16 @@ def test_correct_zip_package_behavior(Script, inference_state, environment, code
assert value.py__package__() == []
@pytest.mark.parametrize("code,names", [
("from pkg.", {"module", "nested", "namespace"}),
("from pkg.nested.", {"nested_module"})
])
def test_zip_package_import_complete(Script, environment, code, names):
sys_path = environment.get_sys_path() + [str(pkg_zip_path)]
completions = Script(code, project=Project('.', sys_path=sys_path)).complete()
assert names == {c.name for c in completions}
def test_find_module_not_package_zipped(Script, inference_state, environment):
path = get_example_dir('zipped_imports', 'not_pkg.zip')
sys_path = environment.get_sys_path() + [path]