forked from VimPlug/jedi
Move the importing of modules out of compatibility
This commit is contained in:
@@ -2,12 +2,16 @@ import sys
|
||||
import os
|
||||
import re
|
||||
import inspect
|
||||
import importlib
|
||||
import warnings
|
||||
from zipimport import zipimporter
|
||||
from importlib.machinery import all_suffixes
|
||||
|
||||
from jedi._compatibility import find_module, cast_path
|
||||
from jedi._compatibility import cast_path
|
||||
from jedi.inference.compiled import access
|
||||
from jedi import debug
|
||||
from jedi import parser_utils
|
||||
from jedi.file_io import KnownContentFileIO, ZipFileIO
|
||||
|
||||
|
||||
def get_sys_path():
|
||||
@@ -34,7 +38,7 @@ def get_module_info(inference_state, sys_path=None, full_name=None, **kwargs):
|
||||
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(full_name=full_name, **kwargs)
|
||||
except ImportError:
|
||||
return None, None
|
||||
finally:
|
||||
@@ -112,3 +116,123 @@ def _iter_module_names(inference_state, paths):
|
||||
if modname and '.' not in modname:
|
||||
if modname != '__init__':
|
||||
yield modname
|
||||
|
||||
|
||||
def _find_module(string, path=None, full_name=None, is_global_search=True):
|
||||
"""
|
||||
Provides information about a module.
|
||||
|
||||
This function isolates the differences in importing libraries introduced with
|
||||
python 3.3 on; it gets a module name and optionally a path. It will return a
|
||||
tuple containin an open file for the module (if not builtin), the filename
|
||||
or the name of the module if it is a builtin one and a boolean indicating
|
||||
if the module is contained in a package.
|
||||
"""
|
||||
spec = None
|
||||
loader = None
|
||||
|
||||
for finder in sys.meta_path:
|
||||
if is_global_search and finder != importlib.machinery.PathFinder:
|
||||
p = None
|
||||
else:
|
||||
p = path
|
||||
try:
|
||||
find_spec = finder.find_spec
|
||||
except AttributeError:
|
||||
# These are old-school clases that still have a different API, just
|
||||
# ignore those.
|
||||
continue
|
||||
|
||||
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.
|
||||
full_name = string if not path else full_name
|
||||
implicit_ns_info = ImplicitNSInfo(full_name, spec.submodule_search_locations._path)
|
||||
return implicit_ns_info, True
|
||||
break
|
||||
|
||||
return _find_module_py33(string, path, 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)
|
||||
|
||||
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()
|
||||
# instead." While we should replace that in the future, it's
|
||||
# probably good to wait until we deprecate Python 3.3, since
|
||||
# it was added in Python 3.4 and find_loader hasn't been
|
||||
# removed in 3.6.
|
||||
loader = importlib.find_loader(string)
|
||||
except ValueError as e:
|
||||
# See #491. Importlib might raise a ValueError, to avoid this, we
|
||||
# just raise an ImportError to fix the issue.
|
||||
raise ImportError("Originally " + repr(e))
|
||||
|
||||
if loader is None:
|
||||
raise ImportError("Couldn't find a loader for {}".format(string))
|
||||
|
||||
return _from_loader(loader, string)
|
||||
|
||||
|
||||
def _from_loader(loader, string):
|
||||
try:
|
||||
is_package_method = loader.is_package
|
||||
except AttributeError:
|
||||
is_package = False
|
||||
else:
|
||||
is_package = is_package_method(string)
|
||||
try:
|
||||
get_filename = loader.get_filename
|
||||
except AttributeError:
|
||||
return None, is_package
|
||||
else:
|
||||
module_path = cast_path(get_filename(string))
|
||||
|
||||
# To avoid unicode and read bytes, "overwrite" loader.get_source if
|
||||
# possible.
|
||||
try:
|
||||
f = type(loader).get_source
|
||||
except AttributeError:
|
||||
raise ImportError("get_source was not defined on loader")
|
||||
|
||||
if f is not importlib.machinery.SourceFileLoader.get_source:
|
||||
# Unfortunately we are reading unicode here, not bytes.
|
||||
# It seems hard to get bytes, because the zip importer
|
||||
# logic just unpacks the zip file and returns a file descriptor
|
||||
# that we cannot as easily access. Therefore we just read it as
|
||||
# a string in the cases where get_source was overwritten.
|
||||
code = loader.get_source(string)
|
||||
else:
|
||||
code = _get_source(loader, string)
|
||||
|
||||
if code is None:
|
||||
return None, is_package
|
||||
if isinstance(loader, zipimporter):
|
||||
return ZipFileIO(module_path, code, cast_path(loader.archive)), is_package
|
||||
|
||||
return KnownContentFileIO(module_path, code), is_package
|
||||
|
||||
|
||||
def _get_source(loader, fullname):
|
||||
"""
|
||||
This method is here as a replacement for SourceLoader.get_source. That
|
||||
method returns unicode, but we prefer bytes.
|
||||
"""
|
||||
path = loader.get_filename(fullname)
|
||||
try:
|
||||
return loader.get_data(path)
|
||||
except OSError:
|
||||
raise ImportError('source not available through get_data()',
|
||||
name=fullname)
|
||||
|
||||
|
||||
class ImplicitNSInfo(object):
|
||||
"""Stores information returned from an implicit namespace spec"""
|
||||
def __init__(self, name, paths):
|
||||
self.name = name
|
||||
self.paths = paths
|
||||
|
||||
Reference in New Issue
Block a user