mirror of
https://github.com/davidhalter/jedi.git
synced 2025-12-06 22:14:27 +08:00
Fixed ZIP completion.
This commit is contained in:
@@ -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.
|
||||
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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]
|
||||
|
||||
Reference in New Issue
Block a user