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