Format: text/x-python
⬇ Download File (8.3 KB)
   1  import sys
   2  import os
   3  import inspect
   4  import importlib
   5  from pathlib import Path
   6  from zipfile import ZipFile
   7  from zipimport import zipimporter, ZipImportError
   8  from importlib.machinery import all_suffixes
   9  
  10  from jedi.inference.compiled import access
  11  from jedi import debug
  12  from jedi import parser_utils
  13  from jedi.file_io import KnownContentFileIO, ZipFileIO
  14  
  15  
  16  def get_sys_path():
  17      return sys.path
  18  
  19  
  20  def load_module(inference_state, **kwargs):
  21      return access.load_module(inference_state, **kwargs)
  22  
  23  
  24  def get_compiled_method_return(inference_state, id, attribute, *args, **kwargs):
  25      handle = inference_state.compiled_subprocess.get_access_handle(id)
  26      return getattr(handle.access, attribute)(*args, **kwargs)
  27  
  28  
  29  def create_simple_object(inference_state, obj):
  30      return access.create_access_path(inference_state, obj)
  31  
  32  
  33  def get_module_info(inference_state, sys_path=None, full_name=None, **kwargs):
  34      """
  35      Returns Tuple[Union[NamespaceInfo, FileIO, None], Optional[bool]]
  36      """
  37      if sys_path is not None:
  38          sys.path, temp = sys_path, sys.path
  39      try:
  40          return _find_module(full_name=full_name, **kwargs)
  41      except ImportError:
  42          return None, None
  43      finally:
  44          if sys_path is not None:
  45              sys.path = temp
  46  
  47  
  48  def get_builtin_module_names(inference_state):
  49      return sys.builtin_module_names
  50  
  51  
  52  def _test_raise_error(inference_state, exception_type):
  53      """
  54      Raise an error to simulate certain problems for unit tests.
  55      """
  56      raise exception_type
  57  
  58  
  59  def _test_print(inference_state, stderr=None, stdout=None):
  60      """
  61      Force some prints in the subprocesses. This exists for unit tests.
  62      """
  63      if stderr is not None:
  64          print(stderr, file=sys.stderr)
  65          sys.stderr.flush()
  66      if stdout is not None:
  67          print(stdout)
  68          sys.stdout.flush()
  69  
  70  
  71  def _get_init_path(directory_path):
  72      """
  73      The __init__ file can be searched in a directory. If found return it, else
  74      None.
  75      """
  76      for suffix in all_suffixes():
  77          path = os.path.join(directory_path, '__init__' + suffix)
  78          if os.path.exists(path):
  79              return path
  80      return None
  81  
  82  
  83  def safe_literal_eval(inference_state, value):
  84      return parser_utils.safe_literal_eval(value)
  85  
  86  
  87  def iter_module_names(*args, **kwargs):
  88      return list(_iter_module_names(*args, **kwargs))
  89  
  90  
  91  def _iter_module_names(inference_state, paths):
  92      # Python modules/packages
  93      for path in paths:
  94          try:
  95              dir_entries = ((entry.name, entry.is_dir()) for entry in os.scandir(path))
  96          except OSError:
  97              try:
  98                  zip_import_info = zipimporter(path)
  99                  # Unfortunately, there is no public way to access zipimporter's
 100                  # private _files member. We therefore have to use a
 101                  # custom function to iterate over the files.
 102                  dir_entries = _zip_list_subdirectory(
 103                      zip_import_info.archive, zip_import_info.prefix)
 104              except ZipImportError:
 105                  # The file might not exist or reading it might lead to an error.
 106                  debug.warning("Not possible to list directory: %s", path)
 107                  continue
 108          for name, is_dir in dir_entries:
 109              # First Namespaces then modules/stubs
 110              if is_dir:
 111                  # pycache is obviously not an interesting namespace. Also the
 112                  # name must be a valid identifier.
 113                  if name != '__pycache__' and name.isidentifier():
 114                      yield name
 115              else:
 116                  if name.endswith('.pyi'):  # Stub files
 117                      modname = name[:-4]
 118                  else:
 119                      modname = inspect.getmodulename(name)
 120  
 121                  if modname and '.' not in modname:
 122                      if modname != '__init__':
 123                          yield modname
 124  
 125  
 126  def _find_module(string, path=None, full_name=None, is_global_search=True):
 127      """
 128      Provides information about a module.
 129  
 130      This function isolates the differences in importing libraries introduced with
 131      python 3.3 on; it gets a module name and optionally a path. It will return a
 132      tuple containin an open file for the module (if not builtin), the filename
 133      or the name of the module if it is a builtin one and a boolean indicating
 134      if the module is contained in a package.
 135      """
 136      spec = None
 137      loader = None
 138  
 139      for finder in sys.meta_path:
 140          if is_global_search and finder != importlib.machinery.PathFinder:
 141              p = None
 142          else:
 143              p = path
 144          try:
 145              find_spec = finder.find_spec
 146          except AttributeError:
 147              # These are old-school clases that still have a different API, just
 148              # ignore those.
 149              continue
 150  
 151          spec = find_spec(string, p)
 152          if spec is not None:
 153              if spec.origin == "frozen":
 154                  continue
 155  
 156              loader = spec.loader
 157  
 158              if loader is None and not spec.has_location:
 159                  # This is a namespace package.
 160                  full_name = string if not path else full_name
 161                  implicit_ns_info = ImplicitNSInfo(full_name, spec.submodule_search_locations._path)
 162                  return implicit_ns_info, True
 163              break
 164  
 165      return _find_module_py33(string, path, loader)
 166  
 167  
 168  def _find_module_py33(string, path=None, loader=None, full_name=None, is_global_search=True):
 169      if not loader:
 170          spec = importlib.machinery.PathFinder.find_spec(string, path)
 171          if spec is not None:
 172              loader = spec.loader
 173  
 174      if loader is None and path is None:  # Fallback to find builtins
 175          try:
 176              spec = importlib.util.find_spec(string)
 177              if spec is not None:
 178                  loader = spec.loader
 179          except ValueError as e:
 180              # See #491. Importlib might raise a ValueError, to avoid this, we
 181              # just raise an ImportError to fix the issue.
 182              raise ImportError("Originally  " + repr(e))
 183  
 184      if loader is None:
 185          raise ImportError("Couldn't find a loader for {}".format(string))
 186  
 187      return _from_loader(loader, string)
 188  
 189  
 190  def _from_loader(loader, string):
 191      try:
 192          is_package_method = loader.is_package
 193      except AttributeError:
 194          is_package = False
 195      else:
 196          is_package = is_package_method(string)
 197      try:
 198          get_filename = loader.get_filename
 199      except AttributeError:
 200          return None, is_package
 201      else:
 202          module_path = get_filename(string)
 203  
 204      # To avoid unicode and read bytes, "overwrite" loader.get_source if
 205      # possible.
 206      try:
 207          f = type(loader).get_source
 208      except AttributeError:
 209          raise ImportError("get_source was not defined on loader")
 210  
 211      if f is not importlib.machinery.SourceFileLoader.get_source:
 212          # Unfortunately we are reading unicode here, not bytes.
 213          # It seems hard to get bytes, because the zip importer
 214          # logic just unpacks the zip file and returns a file descriptor
 215          # that we cannot as easily access. Therefore we just read it as
 216          # a string in the cases where get_source was overwritten.
 217          code = loader.get_source(string)
 218      else:
 219          code = _get_source(loader, string)
 220  
 221      if code is None:
 222          return None, is_package
 223      if isinstance(loader, zipimporter):
 224          return ZipFileIO(module_path, code, Path(loader.archive)), is_package
 225  
 226      return KnownContentFileIO(module_path, code), is_package
 227  
 228  
 229  def _get_source(loader, fullname):
 230      """
 231      This method is here as a replacement for SourceLoader.get_source. That
 232      method returns unicode, but we prefer bytes.
 233      """
 234      path = loader.get_filename(fullname)
 235      try:
 236          return loader.get_data(path)
 237      except OSError:
 238          raise ImportError('source not available through get_data()',
 239                            name=fullname)
 240  
 241  
 242  def _zip_list_subdirectory(zip_path, zip_subdir_path):
 243      zip_file = ZipFile(zip_path)
 244      zip_subdir_path = Path(zip_subdir_path)
 245      zip_content_file_paths = zip_file.namelist()
 246      for raw_file_name in zip_content_file_paths:
 247          file_path = Path(raw_file_name)
 248          if file_path.parent == zip_subdir_path:
 249              file_path = file_path.relative_to(zip_subdir_path)
 250              yield file_path.name, raw_file_name.endswith("/")
 251  
 252  
 253  class ImplicitNSInfo:
 254      """Stores information returned from an implicit namespace spec"""
 255      def __init__(self, name, paths):
 256          self.name = name
 257          self.paths = paths
 258