1*cda5da8dSAndroid Build Coastguard Worker"""Utilities to support packages.""" 2*cda5da8dSAndroid Build Coastguard Worker 3*cda5da8dSAndroid Build Coastguard Workerfrom collections import namedtuple 4*cda5da8dSAndroid Build Coastguard Workerfrom functools import singledispatch as simplegeneric 5*cda5da8dSAndroid Build Coastguard Workerimport importlib 6*cda5da8dSAndroid Build Coastguard Workerimport importlib.util 7*cda5da8dSAndroid Build Coastguard Workerimport importlib.machinery 8*cda5da8dSAndroid Build Coastguard Workerimport os 9*cda5da8dSAndroid Build Coastguard Workerimport os.path 10*cda5da8dSAndroid Build Coastguard Workerimport sys 11*cda5da8dSAndroid Build Coastguard Workerfrom types import ModuleType 12*cda5da8dSAndroid Build Coastguard Workerimport warnings 13*cda5da8dSAndroid Build Coastguard Worker 14*cda5da8dSAndroid Build Coastguard Worker__all__ = [ 15*cda5da8dSAndroid Build Coastguard Worker 'get_importer', 'iter_importers', 'get_loader', 'find_loader', 16*cda5da8dSAndroid Build Coastguard Worker 'walk_packages', 'iter_modules', 'get_data', 17*cda5da8dSAndroid Build Coastguard Worker 'ImpImporter', 'ImpLoader', 'read_code', 'extend_path', 18*cda5da8dSAndroid Build Coastguard Worker 'ModuleInfo', 19*cda5da8dSAndroid Build Coastguard Worker] 20*cda5da8dSAndroid Build Coastguard Worker 21*cda5da8dSAndroid Build Coastguard Worker 22*cda5da8dSAndroid Build Coastguard WorkerModuleInfo = namedtuple('ModuleInfo', 'module_finder name ispkg') 23*cda5da8dSAndroid Build Coastguard WorkerModuleInfo.__doc__ = 'A namedtuple with minimal info about a module.' 24*cda5da8dSAndroid Build Coastguard Worker 25*cda5da8dSAndroid Build Coastguard Worker 26*cda5da8dSAndroid Build Coastguard Workerdef _get_spec(finder, name): 27*cda5da8dSAndroid Build Coastguard Worker """Return the finder-specific module spec.""" 28*cda5da8dSAndroid Build Coastguard Worker # Works with legacy finders. 29*cda5da8dSAndroid Build Coastguard Worker try: 30*cda5da8dSAndroid Build Coastguard Worker find_spec = finder.find_spec 31*cda5da8dSAndroid Build Coastguard Worker except AttributeError: 32*cda5da8dSAndroid Build Coastguard Worker loader = finder.find_module(name) 33*cda5da8dSAndroid Build Coastguard Worker if loader is None: 34*cda5da8dSAndroid Build Coastguard Worker return None 35*cda5da8dSAndroid Build Coastguard Worker return importlib.util.spec_from_loader(name, loader) 36*cda5da8dSAndroid Build Coastguard Worker else: 37*cda5da8dSAndroid Build Coastguard Worker return find_spec(name) 38*cda5da8dSAndroid Build Coastguard Worker 39*cda5da8dSAndroid Build Coastguard Worker 40*cda5da8dSAndroid Build Coastguard Workerdef read_code(stream): 41*cda5da8dSAndroid Build Coastguard Worker # This helper is needed in order for the PEP 302 emulation to 42*cda5da8dSAndroid Build Coastguard Worker # correctly handle compiled files 43*cda5da8dSAndroid Build Coastguard Worker import marshal 44*cda5da8dSAndroid Build Coastguard Worker 45*cda5da8dSAndroid Build Coastguard Worker magic = stream.read(4) 46*cda5da8dSAndroid Build Coastguard Worker if magic != importlib.util.MAGIC_NUMBER: 47*cda5da8dSAndroid Build Coastguard Worker return None 48*cda5da8dSAndroid Build Coastguard Worker 49*cda5da8dSAndroid Build Coastguard Worker stream.read(12) # Skip rest of the header 50*cda5da8dSAndroid Build Coastguard Worker return marshal.load(stream) 51*cda5da8dSAndroid Build Coastguard Worker 52*cda5da8dSAndroid Build Coastguard Worker 53*cda5da8dSAndroid Build Coastguard Workerdef walk_packages(path=None, prefix='', onerror=None): 54*cda5da8dSAndroid Build Coastguard Worker """Yields ModuleInfo for all modules recursively 55*cda5da8dSAndroid Build Coastguard Worker on path, or, if path is None, all accessible modules. 56*cda5da8dSAndroid Build Coastguard Worker 57*cda5da8dSAndroid Build Coastguard Worker 'path' should be either None or a list of paths to look for 58*cda5da8dSAndroid Build Coastguard Worker modules in. 59*cda5da8dSAndroid Build Coastguard Worker 60*cda5da8dSAndroid Build Coastguard Worker 'prefix' is a string to output on the front of every module name 61*cda5da8dSAndroid Build Coastguard Worker on output. 62*cda5da8dSAndroid Build Coastguard Worker 63*cda5da8dSAndroid Build Coastguard Worker Note that this function must import all *packages* (NOT all 64*cda5da8dSAndroid Build Coastguard Worker modules!) on the given path, in order to access the __path__ 65*cda5da8dSAndroid Build Coastguard Worker attribute to find submodules. 66*cda5da8dSAndroid Build Coastguard Worker 67*cda5da8dSAndroid Build Coastguard Worker 'onerror' is a function which gets called with one argument (the 68*cda5da8dSAndroid Build Coastguard Worker name of the package which was being imported) if any exception 69*cda5da8dSAndroid Build Coastguard Worker occurs while trying to import a package. If no onerror function is 70*cda5da8dSAndroid Build Coastguard Worker supplied, ImportErrors are caught and ignored, while all other 71*cda5da8dSAndroid Build Coastguard Worker exceptions are propagated, terminating the search. 72*cda5da8dSAndroid Build Coastguard Worker 73*cda5da8dSAndroid Build Coastguard Worker Examples: 74*cda5da8dSAndroid Build Coastguard Worker 75*cda5da8dSAndroid Build Coastguard Worker # list all modules python can access 76*cda5da8dSAndroid Build Coastguard Worker walk_packages() 77*cda5da8dSAndroid Build Coastguard Worker 78*cda5da8dSAndroid Build Coastguard Worker # list all submodules of ctypes 79*cda5da8dSAndroid Build Coastguard Worker walk_packages(ctypes.__path__, ctypes.__name__+'.') 80*cda5da8dSAndroid Build Coastguard Worker """ 81*cda5da8dSAndroid Build Coastguard Worker 82*cda5da8dSAndroid Build Coastguard Worker def seen(p, m={}): 83*cda5da8dSAndroid Build Coastguard Worker if p in m: 84*cda5da8dSAndroid Build Coastguard Worker return True 85*cda5da8dSAndroid Build Coastguard Worker m[p] = True 86*cda5da8dSAndroid Build Coastguard Worker 87*cda5da8dSAndroid Build Coastguard Worker for info in iter_modules(path, prefix): 88*cda5da8dSAndroid Build Coastguard Worker yield info 89*cda5da8dSAndroid Build Coastguard Worker 90*cda5da8dSAndroid Build Coastguard Worker if info.ispkg: 91*cda5da8dSAndroid Build Coastguard Worker try: 92*cda5da8dSAndroid Build Coastguard Worker __import__(info.name) 93*cda5da8dSAndroid Build Coastguard Worker except ImportError: 94*cda5da8dSAndroid Build Coastguard Worker if onerror is not None: 95*cda5da8dSAndroid Build Coastguard Worker onerror(info.name) 96*cda5da8dSAndroid Build Coastguard Worker except Exception: 97*cda5da8dSAndroid Build Coastguard Worker if onerror is not None: 98*cda5da8dSAndroid Build Coastguard Worker onerror(info.name) 99*cda5da8dSAndroid Build Coastguard Worker else: 100*cda5da8dSAndroid Build Coastguard Worker raise 101*cda5da8dSAndroid Build Coastguard Worker else: 102*cda5da8dSAndroid Build Coastguard Worker path = getattr(sys.modules[info.name], '__path__', None) or [] 103*cda5da8dSAndroid Build Coastguard Worker 104*cda5da8dSAndroid Build Coastguard Worker # don't traverse path items we've seen before 105*cda5da8dSAndroid Build Coastguard Worker path = [p for p in path if not seen(p)] 106*cda5da8dSAndroid Build Coastguard Worker 107*cda5da8dSAndroid Build Coastguard Worker yield from walk_packages(path, info.name+'.', onerror) 108*cda5da8dSAndroid Build Coastguard Worker 109*cda5da8dSAndroid Build Coastguard Worker 110*cda5da8dSAndroid Build Coastguard Workerdef iter_modules(path=None, prefix=''): 111*cda5da8dSAndroid Build Coastguard Worker """Yields ModuleInfo for all submodules on path, 112*cda5da8dSAndroid Build Coastguard Worker or, if path is None, all top-level modules on sys.path. 113*cda5da8dSAndroid Build Coastguard Worker 114*cda5da8dSAndroid Build Coastguard Worker 'path' should be either None or a list of paths to look for 115*cda5da8dSAndroid Build Coastguard Worker modules in. 116*cda5da8dSAndroid Build Coastguard Worker 117*cda5da8dSAndroid Build Coastguard Worker 'prefix' is a string to output on the front of every module name 118*cda5da8dSAndroid Build Coastguard Worker on output. 119*cda5da8dSAndroid Build Coastguard Worker """ 120*cda5da8dSAndroid Build Coastguard Worker if path is None: 121*cda5da8dSAndroid Build Coastguard Worker importers = iter_importers() 122*cda5da8dSAndroid Build Coastguard Worker elif isinstance(path, str): 123*cda5da8dSAndroid Build Coastguard Worker raise ValueError("path must be None or list of paths to look for " 124*cda5da8dSAndroid Build Coastguard Worker "modules in") 125*cda5da8dSAndroid Build Coastguard Worker else: 126*cda5da8dSAndroid Build Coastguard Worker importers = map(get_importer, path) 127*cda5da8dSAndroid Build Coastguard Worker 128*cda5da8dSAndroid Build Coastguard Worker yielded = {} 129*cda5da8dSAndroid Build Coastguard Worker for i in importers: 130*cda5da8dSAndroid Build Coastguard Worker for name, ispkg in iter_importer_modules(i, prefix): 131*cda5da8dSAndroid Build Coastguard Worker if name not in yielded: 132*cda5da8dSAndroid Build Coastguard Worker yielded[name] = 1 133*cda5da8dSAndroid Build Coastguard Worker yield ModuleInfo(i, name, ispkg) 134*cda5da8dSAndroid Build Coastguard Worker 135*cda5da8dSAndroid Build Coastguard Worker 136*cda5da8dSAndroid Build Coastguard Worker@simplegeneric 137*cda5da8dSAndroid Build Coastguard Workerdef iter_importer_modules(importer, prefix=''): 138*cda5da8dSAndroid Build Coastguard Worker if not hasattr(importer, 'iter_modules'): 139*cda5da8dSAndroid Build Coastguard Worker return [] 140*cda5da8dSAndroid Build Coastguard Worker return importer.iter_modules(prefix) 141*cda5da8dSAndroid Build Coastguard Worker 142*cda5da8dSAndroid Build Coastguard Worker 143*cda5da8dSAndroid Build Coastguard Worker# Implement a file walker for the normal importlib path hook 144*cda5da8dSAndroid Build Coastguard Workerdef _iter_file_finder_modules(importer, prefix=''): 145*cda5da8dSAndroid Build Coastguard Worker if importer.path is None or not os.path.isdir(importer.path): 146*cda5da8dSAndroid Build Coastguard Worker return 147*cda5da8dSAndroid Build Coastguard Worker 148*cda5da8dSAndroid Build Coastguard Worker yielded = {} 149*cda5da8dSAndroid Build Coastguard Worker import inspect 150*cda5da8dSAndroid Build Coastguard Worker try: 151*cda5da8dSAndroid Build Coastguard Worker filenames = os.listdir(importer.path) 152*cda5da8dSAndroid Build Coastguard Worker except OSError: 153*cda5da8dSAndroid Build Coastguard Worker # ignore unreadable directories like import does 154*cda5da8dSAndroid Build Coastguard Worker filenames = [] 155*cda5da8dSAndroid Build Coastguard Worker filenames.sort() # handle packages before same-named modules 156*cda5da8dSAndroid Build Coastguard Worker 157*cda5da8dSAndroid Build Coastguard Worker for fn in filenames: 158*cda5da8dSAndroid Build Coastguard Worker modname = inspect.getmodulename(fn) 159*cda5da8dSAndroid Build Coastguard Worker if modname=='__init__' or modname in yielded: 160*cda5da8dSAndroid Build Coastguard Worker continue 161*cda5da8dSAndroid Build Coastguard Worker 162*cda5da8dSAndroid Build Coastguard Worker path = os.path.join(importer.path, fn) 163*cda5da8dSAndroid Build Coastguard Worker ispkg = False 164*cda5da8dSAndroid Build Coastguard Worker 165*cda5da8dSAndroid Build Coastguard Worker if not modname and os.path.isdir(path) and '.' not in fn: 166*cda5da8dSAndroid Build Coastguard Worker modname = fn 167*cda5da8dSAndroid Build Coastguard Worker try: 168*cda5da8dSAndroid Build Coastguard Worker dircontents = os.listdir(path) 169*cda5da8dSAndroid Build Coastguard Worker except OSError: 170*cda5da8dSAndroid Build Coastguard Worker # ignore unreadable directories like import does 171*cda5da8dSAndroid Build Coastguard Worker dircontents = [] 172*cda5da8dSAndroid Build Coastguard Worker for fn in dircontents: 173*cda5da8dSAndroid Build Coastguard Worker subname = inspect.getmodulename(fn) 174*cda5da8dSAndroid Build Coastguard Worker if subname=='__init__': 175*cda5da8dSAndroid Build Coastguard Worker ispkg = True 176*cda5da8dSAndroid Build Coastguard Worker break 177*cda5da8dSAndroid Build Coastguard Worker else: 178*cda5da8dSAndroid Build Coastguard Worker continue # not a package 179*cda5da8dSAndroid Build Coastguard Worker 180*cda5da8dSAndroid Build Coastguard Worker if modname and '.' not in modname: 181*cda5da8dSAndroid Build Coastguard Worker yielded[modname] = 1 182*cda5da8dSAndroid Build Coastguard Worker yield prefix + modname, ispkg 183*cda5da8dSAndroid Build Coastguard Worker 184*cda5da8dSAndroid Build Coastguard Workeriter_importer_modules.register( 185*cda5da8dSAndroid Build Coastguard Worker importlib.machinery.FileFinder, _iter_file_finder_modules) 186*cda5da8dSAndroid Build Coastguard Worker 187*cda5da8dSAndroid Build Coastguard Worker 188*cda5da8dSAndroid Build Coastguard Workerdef _import_imp(): 189*cda5da8dSAndroid Build Coastguard Worker global imp 190*cda5da8dSAndroid Build Coastguard Worker with warnings.catch_warnings(): 191*cda5da8dSAndroid Build Coastguard Worker warnings.simplefilter('ignore', DeprecationWarning) 192*cda5da8dSAndroid Build Coastguard Worker imp = importlib.import_module('imp') 193*cda5da8dSAndroid Build Coastguard Worker 194*cda5da8dSAndroid Build Coastguard Workerclass ImpImporter: 195*cda5da8dSAndroid Build Coastguard Worker """PEP 302 Finder that wraps Python's "classic" import algorithm 196*cda5da8dSAndroid Build Coastguard Worker 197*cda5da8dSAndroid Build Coastguard Worker ImpImporter(dirname) produces a PEP 302 finder that searches that 198*cda5da8dSAndroid Build Coastguard Worker directory. ImpImporter(None) produces a PEP 302 finder that searches 199*cda5da8dSAndroid Build Coastguard Worker the current sys.path, plus any modules that are frozen or built-in. 200*cda5da8dSAndroid Build Coastguard Worker 201*cda5da8dSAndroid Build Coastguard Worker Note that ImpImporter does not currently support being used by placement 202*cda5da8dSAndroid Build Coastguard Worker on sys.meta_path. 203*cda5da8dSAndroid Build Coastguard Worker """ 204*cda5da8dSAndroid Build Coastguard Worker 205*cda5da8dSAndroid Build Coastguard Worker def __init__(self, path=None): 206*cda5da8dSAndroid Build Coastguard Worker global imp 207*cda5da8dSAndroid Build Coastguard Worker warnings.warn("This emulation is deprecated and slated for removal " 208*cda5da8dSAndroid Build Coastguard Worker "in Python 3.12; use 'importlib' instead", 209*cda5da8dSAndroid Build Coastguard Worker DeprecationWarning) 210*cda5da8dSAndroid Build Coastguard Worker _import_imp() 211*cda5da8dSAndroid Build Coastguard Worker self.path = path 212*cda5da8dSAndroid Build Coastguard Worker 213*cda5da8dSAndroid Build Coastguard Worker def find_module(self, fullname, path=None): 214*cda5da8dSAndroid Build Coastguard Worker # Note: we ignore 'path' argument since it is only used via meta_path 215*cda5da8dSAndroid Build Coastguard Worker subname = fullname.split(".")[-1] 216*cda5da8dSAndroid Build Coastguard Worker if subname != fullname and self.path is None: 217*cda5da8dSAndroid Build Coastguard Worker return None 218*cda5da8dSAndroid Build Coastguard Worker if self.path is None: 219*cda5da8dSAndroid Build Coastguard Worker path = None 220*cda5da8dSAndroid Build Coastguard Worker else: 221*cda5da8dSAndroid Build Coastguard Worker path = [os.path.realpath(self.path)] 222*cda5da8dSAndroid Build Coastguard Worker try: 223*cda5da8dSAndroid Build Coastguard Worker file, filename, etc = imp.find_module(subname, path) 224*cda5da8dSAndroid Build Coastguard Worker except ImportError: 225*cda5da8dSAndroid Build Coastguard Worker return None 226*cda5da8dSAndroid Build Coastguard Worker return ImpLoader(fullname, file, filename, etc) 227*cda5da8dSAndroid Build Coastguard Worker 228*cda5da8dSAndroid Build Coastguard Worker def iter_modules(self, prefix=''): 229*cda5da8dSAndroid Build Coastguard Worker if self.path is None or not os.path.isdir(self.path): 230*cda5da8dSAndroid Build Coastguard Worker return 231*cda5da8dSAndroid Build Coastguard Worker 232*cda5da8dSAndroid Build Coastguard Worker yielded = {} 233*cda5da8dSAndroid Build Coastguard Worker import inspect 234*cda5da8dSAndroid Build Coastguard Worker try: 235*cda5da8dSAndroid Build Coastguard Worker filenames = os.listdir(self.path) 236*cda5da8dSAndroid Build Coastguard Worker except OSError: 237*cda5da8dSAndroid Build Coastguard Worker # ignore unreadable directories like import does 238*cda5da8dSAndroid Build Coastguard Worker filenames = [] 239*cda5da8dSAndroid Build Coastguard Worker filenames.sort() # handle packages before same-named modules 240*cda5da8dSAndroid Build Coastguard Worker 241*cda5da8dSAndroid Build Coastguard Worker for fn in filenames: 242*cda5da8dSAndroid Build Coastguard Worker modname = inspect.getmodulename(fn) 243*cda5da8dSAndroid Build Coastguard Worker if modname=='__init__' or modname in yielded: 244*cda5da8dSAndroid Build Coastguard Worker continue 245*cda5da8dSAndroid Build Coastguard Worker 246*cda5da8dSAndroid Build Coastguard Worker path = os.path.join(self.path, fn) 247*cda5da8dSAndroid Build Coastguard Worker ispkg = False 248*cda5da8dSAndroid Build Coastguard Worker 249*cda5da8dSAndroid Build Coastguard Worker if not modname and os.path.isdir(path) and '.' not in fn: 250*cda5da8dSAndroid Build Coastguard Worker modname = fn 251*cda5da8dSAndroid Build Coastguard Worker try: 252*cda5da8dSAndroid Build Coastguard Worker dircontents = os.listdir(path) 253*cda5da8dSAndroid Build Coastguard Worker except OSError: 254*cda5da8dSAndroid Build Coastguard Worker # ignore unreadable directories like import does 255*cda5da8dSAndroid Build Coastguard Worker dircontents = [] 256*cda5da8dSAndroid Build Coastguard Worker for fn in dircontents: 257*cda5da8dSAndroid Build Coastguard Worker subname = inspect.getmodulename(fn) 258*cda5da8dSAndroid Build Coastguard Worker if subname=='__init__': 259*cda5da8dSAndroid Build Coastguard Worker ispkg = True 260*cda5da8dSAndroid Build Coastguard Worker break 261*cda5da8dSAndroid Build Coastguard Worker else: 262*cda5da8dSAndroid Build Coastguard Worker continue # not a package 263*cda5da8dSAndroid Build Coastguard Worker 264*cda5da8dSAndroid Build Coastguard Worker if modname and '.' not in modname: 265*cda5da8dSAndroid Build Coastguard Worker yielded[modname] = 1 266*cda5da8dSAndroid Build Coastguard Worker yield prefix + modname, ispkg 267*cda5da8dSAndroid Build Coastguard Worker 268*cda5da8dSAndroid Build Coastguard Worker 269*cda5da8dSAndroid Build Coastguard Workerclass ImpLoader: 270*cda5da8dSAndroid Build Coastguard Worker """PEP 302 Loader that wraps Python's "classic" import algorithm 271*cda5da8dSAndroid Build Coastguard Worker """ 272*cda5da8dSAndroid Build Coastguard Worker code = source = None 273*cda5da8dSAndroid Build Coastguard Worker 274*cda5da8dSAndroid Build Coastguard Worker def __init__(self, fullname, file, filename, etc): 275*cda5da8dSAndroid Build Coastguard Worker warnings.warn("This emulation is deprecated and slated for removal in " 276*cda5da8dSAndroid Build Coastguard Worker "Python 3.12; use 'importlib' instead", 277*cda5da8dSAndroid Build Coastguard Worker DeprecationWarning) 278*cda5da8dSAndroid Build Coastguard Worker _import_imp() 279*cda5da8dSAndroid Build Coastguard Worker self.file = file 280*cda5da8dSAndroid Build Coastguard Worker self.filename = filename 281*cda5da8dSAndroid Build Coastguard Worker self.fullname = fullname 282*cda5da8dSAndroid Build Coastguard Worker self.etc = etc 283*cda5da8dSAndroid Build Coastguard Worker 284*cda5da8dSAndroid Build Coastguard Worker def load_module(self, fullname): 285*cda5da8dSAndroid Build Coastguard Worker self._reopen() 286*cda5da8dSAndroid Build Coastguard Worker try: 287*cda5da8dSAndroid Build Coastguard Worker mod = imp.load_module(fullname, self.file, self.filename, self.etc) 288*cda5da8dSAndroid Build Coastguard Worker finally: 289*cda5da8dSAndroid Build Coastguard Worker if self.file: 290*cda5da8dSAndroid Build Coastguard Worker self.file.close() 291*cda5da8dSAndroid Build Coastguard Worker # Note: we don't set __loader__ because we want the module to look 292*cda5da8dSAndroid Build Coastguard Worker # normal; i.e. this is just a wrapper for standard import machinery 293*cda5da8dSAndroid Build Coastguard Worker return mod 294*cda5da8dSAndroid Build Coastguard Worker 295*cda5da8dSAndroid Build Coastguard Worker def get_data(self, pathname): 296*cda5da8dSAndroid Build Coastguard Worker with open(pathname, "rb") as file: 297*cda5da8dSAndroid Build Coastguard Worker return file.read() 298*cda5da8dSAndroid Build Coastguard Worker 299*cda5da8dSAndroid Build Coastguard Worker def _reopen(self): 300*cda5da8dSAndroid Build Coastguard Worker if self.file and self.file.closed: 301*cda5da8dSAndroid Build Coastguard Worker mod_type = self.etc[2] 302*cda5da8dSAndroid Build Coastguard Worker if mod_type==imp.PY_SOURCE: 303*cda5da8dSAndroid Build Coastguard Worker self.file = open(self.filename, 'r') 304*cda5da8dSAndroid Build Coastguard Worker elif mod_type in (imp.PY_COMPILED, imp.C_EXTENSION): 305*cda5da8dSAndroid Build Coastguard Worker self.file = open(self.filename, 'rb') 306*cda5da8dSAndroid Build Coastguard Worker 307*cda5da8dSAndroid Build Coastguard Worker def _fix_name(self, fullname): 308*cda5da8dSAndroid Build Coastguard Worker if fullname is None: 309*cda5da8dSAndroid Build Coastguard Worker fullname = self.fullname 310*cda5da8dSAndroid Build Coastguard Worker elif fullname != self.fullname: 311*cda5da8dSAndroid Build Coastguard Worker raise ImportError("Loader for module %s cannot handle " 312*cda5da8dSAndroid Build Coastguard Worker "module %s" % (self.fullname, fullname)) 313*cda5da8dSAndroid Build Coastguard Worker return fullname 314*cda5da8dSAndroid Build Coastguard Worker 315*cda5da8dSAndroid Build Coastguard Worker def is_package(self, fullname): 316*cda5da8dSAndroid Build Coastguard Worker fullname = self._fix_name(fullname) 317*cda5da8dSAndroid Build Coastguard Worker return self.etc[2]==imp.PKG_DIRECTORY 318*cda5da8dSAndroid Build Coastguard Worker 319*cda5da8dSAndroid Build Coastguard Worker def get_code(self, fullname=None): 320*cda5da8dSAndroid Build Coastguard Worker fullname = self._fix_name(fullname) 321*cda5da8dSAndroid Build Coastguard Worker if self.code is None: 322*cda5da8dSAndroid Build Coastguard Worker mod_type = self.etc[2] 323*cda5da8dSAndroid Build Coastguard Worker if mod_type==imp.PY_SOURCE: 324*cda5da8dSAndroid Build Coastguard Worker source = self.get_source(fullname) 325*cda5da8dSAndroid Build Coastguard Worker self.code = compile(source, self.filename, 'exec') 326*cda5da8dSAndroid Build Coastguard Worker elif mod_type==imp.PY_COMPILED: 327*cda5da8dSAndroid Build Coastguard Worker self._reopen() 328*cda5da8dSAndroid Build Coastguard Worker try: 329*cda5da8dSAndroid Build Coastguard Worker self.code = read_code(self.file) 330*cda5da8dSAndroid Build Coastguard Worker finally: 331*cda5da8dSAndroid Build Coastguard Worker self.file.close() 332*cda5da8dSAndroid Build Coastguard Worker elif mod_type==imp.PKG_DIRECTORY: 333*cda5da8dSAndroid Build Coastguard Worker self.code = self._get_delegate().get_code() 334*cda5da8dSAndroid Build Coastguard Worker return self.code 335*cda5da8dSAndroid Build Coastguard Worker 336*cda5da8dSAndroid Build Coastguard Worker def get_source(self, fullname=None): 337*cda5da8dSAndroid Build Coastguard Worker fullname = self._fix_name(fullname) 338*cda5da8dSAndroid Build Coastguard Worker if self.source is None: 339*cda5da8dSAndroid Build Coastguard Worker mod_type = self.etc[2] 340*cda5da8dSAndroid Build Coastguard Worker if mod_type==imp.PY_SOURCE: 341*cda5da8dSAndroid Build Coastguard Worker self._reopen() 342*cda5da8dSAndroid Build Coastguard Worker try: 343*cda5da8dSAndroid Build Coastguard Worker self.source = self.file.read() 344*cda5da8dSAndroid Build Coastguard Worker finally: 345*cda5da8dSAndroid Build Coastguard Worker self.file.close() 346*cda5da8dSAndroid Build Coastguard Worker elif mod_type==imp.PY_COMPILED: 347*cda5da8dSAndroid Build Coastguard Worker if os.path.exists(self.filename[:-1]): 348*cda5da8dSAndroid Build Coastguard Worker with open(self.filename[:-1], 'r') as f: 349*cda5da8dSAndroid Build Coastguard Worker self.source = f.read() 350*cda5da8dSAndroid Build Coastguard Worker elif mod_type==imp.PKG_DIRECTORY: 351*cda5da8dSAndroid Build Coastguard Worker self.source = self._get_delegate().get_source() 352*cda5da8dSAndroid Build Coastguard Worker return self.source 353*cda5da8dSAndroid Build Coastguard Worker 354*cda5da8dSAndroid Build Coastguard Worker def _get_delegate(self): 355*cda5da8dSAndroid Build Coastguard Worker finder = ImpImporter(self.filename) 356*cda5da8dSAndroid Build Coastguard Worker spec = _get_spec(finder, '__init__') 357*cda5da8dSAndroid Build Coastguard Worker return spec.loader 358*cda5da8dSAndroid Build Coastguard Worker 359*cda5da8dSAndroid Build Coastguard Worker def get_filename(self, fullname=None): 360*cda5da8dSAndroid Build Coastguard Worker fullname = self._fix_name(fullname) 361*cda5da8dSAndroid Build Coastguard Worker mod_type = self.etc[2] 362*cda5da8dSAndroid Build Coastguard Worker if mod_type==imp.PKG_DIRECTORY: 363*cda5da8dSAndroid Build Coastguard Worker return self._get_delegate().get_filename() 364*cda5da8dSAndroid Build Coastguard Worker elif mod_type in (imp.PY_SOURCE, imp.PY_COMPILED, imp.C_EXTENSION): 365*cda5da8dSAndroid Build Coastguard Worker return self.filename 366*cda5da8dSAndroid Build Coastguard Worker return None 367*cda5da8dSAndroid Build Coastguard Worker 368*cda5da8dSAndroid Build Coastguard Worker 369*cda5da8dSAndroid Build Coastguard Workertry: 370*cda5da8dSAndroid Build Coastguard Worker import zipimport 371*cda5da8dSAndroid Build Coastguard Worker from zipimport import zipimporter 372*cda5da8dSAndroid Build Coastguard Worker 373*cda5da8dSAndroid Build Coastguard Worker def iter_zipimport_modules(importer, prefix=''): 374*cda5da8dSAndroid Build Coastguard Worker dirlist = sorted(zipimport._zip_directory_cache[importer.archive]) 375*cda5da8dSAndroid Build Coastguard Worker _prefix = importer.prefix 376*cda5da8dSAndroid Build Coastguard Worker plen = len(_prefix) 377*cda5da8dSAndroid Build Coastguard Worker yielded = {} 378*cda5da8dSAndroid Build Coastguard Worker import inspect 379*cda5da8dSAndroid Build Coastguard Worker for fn in dirlist: 380*cda5da8dSAndroid Build Coastguard Worker if not fn.startswith(_prefix): 381*cda5da8dSAndroid Build Coastguard Worker continue 382*cda5da8dSAndroid Build Coastguard Worker 383*cda5da8dSAndroid Build Coastguard Worker fn = fn[plen:].split(os.sep) 384*cda5da8dSAndroid Build Coastguard Worker 385*cda5da8dSAndroid Build Coastguard Worker if len(fn)==2 and fn[1].startswith('__init__.py'): 386*cda5da8dSAndroid Build Coastguard Worker if fn[0] not in yielded: 387*cda5da8dSAndroid Build Coastguard Worker yielded[fn[0]] = 1 388*cda5da8dSAndroid Build Coastguard Worker yield prefix + fn[0], True 389*cda5da8dSAndroid Build Coastguard Worker 390*cda5da8dSAndroid Build Coastguard Worker if len(fn)!=1: 391*cda5da8dSAndroid Build Coastguard Worker continue 392*cda5da8dSAndroid Build Coastguard Worker 393*cda5da8dSAndroid Build Coastguard Worker modname = inspect.getmodulename(fn[0]) 394*cda5da8dSAndroid Build Coastguard Worker if modname=='__init__': 395*cda5da8dSAndroid Build Coastguard Worker continue 396*cda5da8dSAndroid Build Coastguard Worker 397*cda5da8dSAndroid Build Coastguard Worker if modname and '.' not in modname and modname not in yielded: 398*cda5da8dSAndroid Build Coastguard Worker yielded[modname] = 1 399*cda5da8dSAndroid Build Coastguard Worker yield prefix + modname, False 400*cda5da8dSAndroid Build Coastguard Worker 401*cda5da8dSAndroid Build Coastguard Worker iter_importer_modules.register(zipimporter, iter_zipimport_modules) 402*cda5da8dSAndroid Build Coastguard Worker 403*cda5da8dSAndroid Build Coastguard Workerexcept ImportError: 404*cda5da8dSAndroid Build Coastguard Worker pass 405*cda5da8dSAndroid Build Coastguard Worker 406*cda5da8dSAndroid Build Coastguard Worker 407*cda5da8dSAndroid Build Coastguard Workerdef get_importer(path_item): 408*cda5da8dSAndroid Build Coastguard Worker """Retrieve a finder for the given path item 409*cda5da8dSAndroid Build Coastguard Worker 410*cda5da8dSAndroid Build Coastguard Worker The returned finder is cached in sys.path_importer_cache 411*cda5da8dSAndroid Build Coastguard Worker if it was newly created by a path hook. 412*cda5da8dSAndroid Build Coastguard Worker 413*cda5da8dSAndroid Build Coastguard Worker The cache (or part of it) can be cleared manually if a 414*cda5da8dSAndroid Build Coastguard Worker rescan of sys.path_hooks is necessary. 415*cda5da8dSAndroid Build Coastguard Worker """ 416*cda5da8dSAndroid Build Coastguard Worker path_item = os.fsdecode(path_item) 417*cda5da8dSAndroid Build Coastguard Worker try: 418*cda5da8dSAndroid Build Coastguard Worker importer = sys.path_importer_cache[path_item] 419*cda5da8dSAndroid Build Coastguard Worker except KeyError: 420*cda5da8dSAndroid Build Coastguard Worker for path_hook in sys.path_hooks: 421*cda5da8dSAndroid Build Coastguard Worker try: 422*cda5da8dSAndroid Build Coastguard Worker importer = path_hook(path_item) 423*cda5da8dSAndroid Build Coastguard Worker sys.path_importer_cache.setdefault(path_item, importer) 424*cda5da8dSAndroid Build Coastguard Worker break 425*cda5da8dSAndroid Build Coastguard Worker except ImportError: 426*cda5da8dSAndroid Build Coastguard Worker pass 427*cda5da8dSAndroid Build Coastguard Worker else: 428*cda5da8dSAndroid Build Coastguard Worker importer = None 429*cda5da8dSAndroid Build Coastguard Worker return importer 430*cda5da8dSAndroid Build Coastguard Worker 431*cda5da8dSAndroid Build Coastguard Worker 432*cda5da8dSAndroid Build Coastguard Workerdef iter_importers(fullname=""): 433*cda5da8dSAndroid Build Coastguard Worker """Yield finders for the given module name 434*cda5da8dSAndroid Build Coastguard Worker 435*cda5da8dSAndroid Build Coastguard Worker If fullname contains a '.', the finders will be for the package 436*cda5da8dSAndroid Build Coastguard Worker containing fullname, otherwise they will be all registered top level 437*cda5da8dSAndroid Build Coastguard Worker finders (i.e. those on both sys.meta_path and sys.path_hooks). 438*cda5da8dSAndroid Build Coastguard Worker 439*cda5da8dSAndroid Build Coastguard Worker If the named module is in a package, that package is imported as a side 440*cda5da8dSAndroid Build Coastguard Worker effect of invoking this function. 441*cda5da8dSAndroid Build Coastguard Worker 442*cda5da8dSAndroid Build Coastguard Worker If no module name is specified, all top level finders are produced. 443*cda5da8dSAndroid Build Coastguard Worker """ 444*cda5da8dSAndroid Build Coastguard Worker if fullname.startswith('.'): 445*cda5da8dSAndroid Build Coastguard Worker msg = "Relative module name {!r} not supported".format(fullname) 446*cda5da8dSAndroid Build Coastguard Worker raise ImportError(msg) 447*cda5da8dSAndroid Build Coastguard Worker if '.' in fullname: 448*cda5da8dSAndroid Build Coastguard Worker # Get the containing package's __path__ 449*cda5da8dSAndroid Build Coastguard Worker pkg_name = fullname.rpartition(".")[0] 450*cda5da8dSAndroid Build Coastguard Worker pkg = importlib.import_module(pkg_name) 451*cda5da8dSAndroid Build Coastguard Worker path = getattr(pkg, '__path__', None) 452*cda5da8dSAndroid Build Coastguard Worker if path is None: 453*cda5da8dSAndroid Build Coastguard Worker return 454*cda5da8dSAndroid Build Coastguard Worker else: 455*cda5da8dSAndroid Build Coastguard Worker yield from sys.meta_path 456*cda5da8dSAndroid Build Coastguard Worker path = sys.path 457*cda5da8dSAndroid Build Coastguard Worker for item in path: 458*cda5da8dSAndroid Build Coastguard Worker yield get_importer(item) 459*cda5da8dSAndroid Build Coastguard Worker 460*cda5da8dSAndroid Build Coastguard Worker 461*cda5da8dSAndroid Build Coastguard Workerdef get_loader(module_or_name): 462*cda5da8dSAndroid Build Coastguard Worker """Get a "loader" object for module_or_name 463*cda5da8dSAndroid Build Coastguard Worker 464*cda5da8dSAndroid Build Coastguard Worker Returns None if the module cannot be found or imported. 465*cda5da8dSAndroid Build Coastguard Worker If the named module is not already imported, its containing package 466*cda5da8dSAndroid Build Coastguard Worker (if any) is imported, in order to establish the package __path__. 467*cda5da8dSAndroid Build Coastguard Worker """ 468*cda5da8dSAndroid Build Coastguard Worker if module_or_name in sys.modules: 469*cda5da8dSAndroid Build Coastguard Worker module_or_name = sys.modules[module_or_name] 470*cda5da8dSAndroid Build Coastguard Worker if module_or_name is None: 471*cda5da8dSAndroid Build Coastguard Worker return None 472*cda5da8dSAndroid Build Coastguard Worker if isinstance(module_or_name, ModuleType): 473*cda5da8dSAndroid Build Coastguard Worker module = module_or_name 474*cda5da8dSAndroid Build Coastguard Worker loader = getattr(module, '__loader__', None) 475*cda5da8dSAndroid Build Coastguard Worker if loader is not None: 476*cda5da8dSAndroid Build Coastguard Worker return loader 477*cda5da8dSAndroid Build Coastguard Worker if getattr(module, '__spec__', None) is None: 478*cda5da8dSAndroid Build Coastguard Worker return None 479*cda5da8dSAndroid Build Coastguard Worker fullname = module.__name__ 480*cda5da8dSAndroid Build Coastguard Worker else: 481*cda5da8dSAndroid Build Coastguard Worker fullname = module_or_name 482*cda5da8dSAndroid Build Coastguard Worker return find_loader(fullname) 483*cda5da8dSAndroid Build Coastguard Worker 484*cda5da8dSAndroid Build Coastguard Worker 485*cda5da8dSAndroid Build Coastguard Workerdef find_loader(fullname): 486*cda5da8dSAndroid Build Coastguard Worker """Find a "loader" object for fullname 487*cda5da8dSAndroid Build Coastguard Worker 488*cda5da8dSAndroid Build Coastguard Worker This is a backwards compatibility wrapper around 489*cda5da8dSAndroid Build Coastguard Worker importlib.util.find_spec that converts most failures to ImportError 490*cda5da8dSAndroid Build Coastguard Worker and only returns the loader rather than the full spec 491*cda5da8dSAndroid Build Coastguard Worker """ 492*cda5da8dSAndroid Build Coastguard Worker if fullname.startswith('.'): 493*cda5da8dSAndroid Build Coastguard Worker msg = "Relative module name {!r} not supported".format(fullname) 494*cda5da8dSAndroid Build Coastguard Worker raise ImportError(msg) 495*cda5da8dSAndroid Build Coastguard Worker try: 496*cda5da8dSAndroid Build Coastguard Worker spec = importlib.util.find_spec(fullname) 497*cda5da8dSAndroid Build Coastguard Worker except (ImportError, AttributeError, TypeError, ValueError) as ex: 498*cda5da8dSAndroid Build Coastguard Worker # This hack fixes an impedance mismatch between pkgutil and 499*cda5da8dSAndroid Build Coastguard Worker # importlib, where the latter raises other errors for cases where 500*cda5da8dSAndroid Build Coastguard Worker # pkgutil previously raised ImportError 501*cda5da8dSAndroid Build Coastguard Worker msg = "Error while finding loader for {!r} ({}: {})" 502*cda5da8dSAndroid Build Coastguard Worker raise ImportError(msg.format(fullname, type(ex), ex)) from ex 503*cda5da8dSAndroid Build Coastguard Worker return spec.loader if spec is not None else None 504*cda5da8dSAndroid Build Coastguard Worker 505*cda5da8dSAndroid Build Coastguard Worker 506*cda5da8dSAndroid Build Coastguard Workerdef extend_path(path, name): 507*cda5da8dSAndroid Build Coastguard Worker """Extend a package's path. 508*cda5da8dSAndroid Build Coastguard Worker 509*cda5da8dSAndroid Build Coastguard Worker Intended use is to place the following code in a package's __init__.py: 510*cda5da8dSAndroid Build Coastguard Worker 511*cda5da8dSAndroid Build Coastguard Worker from pkgutil import extend_path 512*cda5da8dSAndroid Build Coastguard Worker __path__ = extend_path(__path__, __name__) 513*cda5da8dSAndroid Build Coastguard Worker 514*cda5da8dSAndroid Build Coastguard Worker For each directory on sys.path that has a subdirectory that 515*cda5da8dSAndroid Build Coastguard Worker matches the package name, add the subdirectory to the package's 516*cda5da8dSAndroid Build Coastguard Worker __path__. This is useful if one wants to distribute different 517*cda5da8dSAndroid Build Coastguard Worker parts of a single logical package as multiple directories. 518*cda5da8dSAndroid Build Coastguard Worker 519*cda5da8dSAndroid Build Coastguard Worker It also looks for *.pkg files beginning where * matches the name 520*cda5da8dSAndroid Build Coastguard Worker argument. This feature is similar to *.pth files (see site.py), 521*cda5da8dSAndroid Build Coastguard Worker except that it doesn't special-case lines starting with 'import'. 522*cda5da8dSAndroid Build Coastguard Worker A *.pkg file is trusted at face value: apart from checking for 523*cda5da8dSAndroid Build Coastguard Worker duplicates, all entries found in a *.pkg file are added to the 524*cda5da8dSAndroid Build Coastguard Worker path, regardless of whether they are exist the filesystem. (This 525*cda5da8dSAndroid Build Coastguard Worker is a feature.) 526*cda5da8dSAndroid Build Coastguard Worker 527*cda5da8dSAndroid Build Coastguard Worker If the input path is not a list (as is the case for frozen 528*cda5da8dSAndroid Build Coastguard Worker packages) it is returned unchanged. The input path is not 529*cda5da8dSAndroid Build Coastguard Worker modified; an extended copy is returned. Items are only appended 530*cda5da8dSAndroid Build Coastguard Worker to the copy at the end. 531*cda5da8dSAndroid Build Coastguard Worker 532*cda5da8dSAndroid Build Coastguard Worker It is assumed that sys.path is a sequence. Items of sys.path that 533*cda5da8dSAndroid Build Coastguard Worker are not (unicode or 8-bit) strings referring to existing 534*cda5da8dSAndroid Build Coastguard Worker directories are ignored. Unicode items of sys.path that cause 535*cda5da8dSAndroid Build Coastguard Worker errors when used as filenames may cause this function to raise an 536*cda5da8dSAndroid Build Coastguard Worker exception (in line with os.path.isdir() behavior). 537*cda5da8dSAndroid Build Coastguard Worker """ 538*cda5da8dSAndroid Build Coastguard Worker 539*cda5da8dSAndroid Build Coastguard Worker if not isinstance(path, list): 540*cda5da8dSAndroid Build Coastguard Worker # This could happen e.g. when this is called from inside a 541*cda5da8dSAndroid Build Coastguard Worker # frozen package. Return the path unchanged in that case. 542*cda5da8dSAndroid Build Coastguard Worker return path 543*cda5da8dSAndroid Build Coastguard Worker 544*cda5da8dSAndroid Build Coastguard Worker sname_pkg = name + ".pkg" 545*cda5da8dSAndroid Build Coastguard Worker 546*cda5da8dSAndroid Build Coastguard Worker path = path[:] # Start with a copy of the existing path 547*cda5da8dSAndroid Build Coastguard Worker 548*cda5da8dSAndroid Build Coastguard Worker parent_package, _, final_name = name.rpartition('.') 549*cda5da8dSAndroid Build Coastguard Worker if parent_package: 550*cda5da8dSAndroid Build Coastguard Worker try: 551*cda5da8dSAndroid Build Coastguard Worker search_path = sys.modules[parent_package].__path__ 552*cda5da8dSAndroid Build Coastguard Worker except (KeyError, AttributeError): 553*cda5da8dSAndroid Build Coastguard Worker # We can't do anything: find_loader() returns None when 554*cda5da8dSAndroid Build Coastguard Worker # passed a dotted name. 555*cda5da8dSAndroid Build Coastguard Worker return path 556*cda5da8dSAndroid Build Coastguard Worker else: 557*cda5da8dSAndroid Build Coastguard Worker search_path = sys.path 558*cda5da8dSAndroid Build Coastguard Worker 559*cda5da8dSAndroid Build Coastguard Worker for dir in search_path: 560*cda5da8dSAndroid Build Coastguard Worker if not isinstance(dir, str): 561*cda5da8dSAndroid Build Coastguard Worker continue 562*cda5da8dSAndroid Build Coastguard Worker 563*cda5da8dSAndroid Build Coastguard Worker finder = get_importer(dir) 564*cda5da8dSAndroid Build Coastguard Worker if finder is not None: 565*cda5da8dSAndroid Build Coastguard Worker portions = [] 566*cda5da8dSAndroid Build Coastguard Worker if hasattr(finder, 'find_spec'): 567*cda5da8dSAndroid Build Coastguard Worker spec = finder.find_spec(final_name) 568*cda5da8dSAndroid Build Coastguard Worker if spec is not None: 569*cda5da8dSAndroid Build Coastguard Worker portions = spec.submodule_search_locations or [] 570*cda5da8dSAndroid Build Coastguard Worker # Is this finder PEP 420 compliant? 571*cda5da8dSAndroid Build Coastguard Worker elif hasattr(finder, 'find_loader'): 572*cda5da8dSAndroid Build Coastguard Worker _, portions = finder.find_loader(final_name) 573*cda5da8dSAndroid Build Coastguard Worker 574*cda5da8dSAndroid Build Coastguard Worker for portion in portions: 575*cda5da8dSAndroid Build Coastguard Worker # XXX This may still add duplicate entries to path on 576*cda5da8dSAndroid Build Coastguard Worker # case-insensitive filesystems 577*cda5da8dSAndroid Build Coastguard Worker if portion not in path: 578*cda5da8dSAndroid Build Coastguard Worker path.append(portion) 579*cda5da8dSAndroid Build Coastguard Worker 580*cda5da8dSAndroid Build Coastguard Worker # XXX Is this the right thing for subpackages like zope.app? 581*cda5da8dSAndroid Build Coastguard Worker # It looks for a file named "zope.app.pkg" 582*cda5da8dSAndroid Build Coastguard Worker pkgfile = os.path.join(dir, sname_pkg) 583*cda5da8dSAndroid Build Coastguard Worker if os.path.isfile(pkgfile): 584*cda5da8dSAndroid Build Coastguard Worker try: 585*cda5da8dSAndroid Build Coastguard Worker f = open(pkgfile) 586*cda5da8dSAndroid Build Coastguard Worker except OSError as msg: 587*cda5da8dSAndroid Build Coastguard Worker sys.stderr.write("Can't open %s: %s\n" % 588*cda5da8dSAndroid Build Coastguard Worker (pkgfile, msg)) 589*cda5da8dSAndroid Build Coastguard Worker else: 590*cda5da8dSAndroid Build Coastguard Worker with f: 591*cda5da8dSAndroid Build Coastguard Worker for line in f: 592*cda5da8dSAndroid Build Coastguard Worker line = line.rstrip('\n') 593*cda5da8dSAndroid Build Coastguard Worker if not line or line.startswith('#'): 594*cda5da8dSAndroid Build Coastguard Worker continue 595*cda5da8dSAndroid Build Coastguard Worker path.append(line) # Don't check for existence! 596*cda5da8dSAndroid Build Coastguard Worker 597*cda5da8dSAndroid Build Coastguard Worker return path 598*cda5da8dSAndroid Build Coastguard Worker 599*cda5da8dSAndroid Build Coastguard Worker 600*cda5da8dSAndroid Build Coastguard Workerdef get_data(package, resource): 601*cda5da8dSAndroid Build Coastguard Worker """Get a resource from a package. 602*cda5da8dSAndroid Build Coastguard Worker 603*cda5da8dSAndroid Build Coastguard Worker This is a wrapper round the PEP 302 loader get_data API. The package 604*cda5da8dSAndroid Build Coastguard Worker argument should be the name of a package, in standard module format 605*cda5da8dSAndroid Build Coastguard Worker (foo.bar). The resource argument should be in the form of a relative 606*cda5da8dSAndroid Build Coastguard Worker filename, using '/' as the path separator. The parent directory name '..' 607*cda5da8dSAndroid Build Coastguard Worker is not allowed, and nor is a rooted name (starting with a '/'). 608*cda5da8dSAndroid Build Coastguard Worker 609*cda5da8dSAndroid Build Coastguard Worker The function returns a binary string, which is the contents of the 610*cda5da8dSAndroid Build Coastguard Worker specified resource. 611*cda5da8dSAndroid Build Coastguard Worker 612*cda5da8dSAndroid Build Coastguard Worker For packages located in the filesystem, which have already been imported, 613*cda5da8dSAndroid Build Coastguard Worker this is the rough equivalent of 614*cda5da8dSAndroid Build Coastguard Worker 615*cda5da8dSAndroid Build Coastguard Worker d = os.path.dirname(sys.modules[package].__file__) 616*cda5da8dSAndroid Build Coastguard Worker data = open(os.path.join(d, resource), 'rb').read() 617*cda5da8dSAndroid Build Coastguard Worker 618*cda5da8dSAndroid Build Coastguard Worker If the package cannot be located or loaded, or it uses a PEP 302 loader 619*cda5da8dSAndroid Build Coastguard Worker which does not support get_data(), then None is returned. 620*cda5da8dSAndroid Build Coastguard Worker """ 621*cda5da8dSAndroid Build Coastguard Worker 622*cda5da8dSAndroid Build Coastguard Worker spec = importlib.util.find_spec(package) 623*cda5da8dSAndroid Build Coastguard Worker if spec is None: 624*cda5da8dSAndroid Build Coastguard Worker return None 625*cda5da8dSAndroid Build Coastguard Worker loader = spec.loader 626*cda5da8dSAndroid Build Coastguard Worker if loader is None or not hasattr(loader, 'get_data'): 627*cda5da8dSAndroid Build Coastguard Worker return None 628*cda5da8dSAndroid Build Coastguard Worker # XXX needs test 629*cda5da8dSAndroid Build Coastguard Worker mod = (sys.modules.get(package) or 630*cda5da8dSAndroid Build Coastguard Worker importlib._bootstrap._load(spec)) 631*cda5da8dSAndroid Build Coastguard Worker if mod is None or not hasattr(mod, '__file__'): 632*cda5da8dSAndroid Build Coastguard Worker return None 633*cda5da8dSAndroid Build Coastguard Worker 634*cda5da8dSAndroid Build Coastguard Worker # Modify the resource name to be compatible with the loader.get_data 635*cda5da8dSAndroid Build Coastguard Worker # signature - an os.path format "filename" starting with the dirname of 636*cda5da8dSAndroid Build Coastguard Worker # the package's __file__ 637*cda5da8dSAndroid Build Coastguard Worker parts = resource.split('/') 638*cda5da8dSAndroid Build Coastguard Worker parts.insert(0, os.path.dirname(mod.__file__)) 639*cda5da8dSAndroid Build Coastguard Worker resource_name = os.path.join(*parts) 640*cda5da8dSAndroid Build Coastguard Worker return loader.get_data(resource_name) 641*cda5da8dSAndroid Build Coastguard Worker 642*cda5da8dSAndroid Build Coastguard Worker 643*cda5da8dSAndroid Build Coastguard Worker_NAME_PATTERN = None 644*cda5da8dSAndroid Build Coastguard Worker 645*cda5da8dSAndroid Build Coastguard Workerdef resolve_name(name): 646*cda5da8dSAndroid Build Coastguard Worker """ 647*cda5da8dSAndroid Build Coastguard Worker Resolve a name to an object. 648*cda5da8dSAndroid Build Coastguard Worker 649*cda5da8dSAndroid Build Coastguard Worker It is expected that `name` will be a string in one of the following 650*cda5da8dSAndroid Build Coastguard Worker formats, where W is shorthand for a valid Python identifier and dot stands 651*cda5da8dSAndroid Build Coastguard Worker for a literal period in these pseudo-regexes: 652*cda5da8dSAndroid Build Coastguard Worker 653*cda5da8dSAndroid Build Coastguard Worker W(.W)* 654*cda5da8dSAndroid Build Coastguard Worker W(.W)*:(W(.W)*)? 655*cda5da8dSAndroid Build Coastguard Worker 656*cda5da8dSAndroid Build Coastguard Worker The first form is intended for backward compatibility only. It assumes that 657*cda5da8dSAndroid Build Coastguard Worker some part of the dotted name is a package, and the rest is an object 658*cda5da8dSAndroid Build Coastguard Worker somewhere within that package, possibly nested inside other objects. 659*cda5da8dSAndroid Build Coastguard Worker Because the place where the package stops and the object hierarchy starts 660*cda5da8dSAndroid Build Coastguard Worker can't be inferred by inspection, repeated attempts to import must be done 661*cda5da8dSAndroid Build Coastguard Worker with this form. 662*cda5da8dSAndroid Build Coastguard Worker 663*cda5da8dSAndroid Build Coastguard Worker In the second form, the caller makes the division point clear through the 664*cda5da8dSAndroid Build Coastguard Worker provision of a single colon: the dotted name to the left of the colon is a 665*cda5da8dSAndroid Build Coastguard Worker package to be imported, and the dotted name to the right is the object 666*cda5da8dSAndroid Build Coastguard Worker hierarchy within that package. Only one import is needed in this form. If 667*cda5da8dSAndroid Build Coastguard Worker it ends with the colon, then a module object is returned. 668*cda5da8dSAndroid Build Coastguard Worker 669*cda5da8dSAndroid Build Coastguard Worker The function will return an object (which might be a module), or raise one 670*cda5da8dSAndroid Build Coastguard Worker of the following exceptions: 671*cda5da8dSAndroid Build Coastguard Worker 672*cda5da8dSAndroid Build Coastguard Worker ValueError - if `name` isn't in a recognised format 673*cda5da8dSAndroid Build Coastguard Worker ImportError - if an import failed when it shouldn't have 674*cda5da8dSAndroid Build Coastguard Worker AttributeError - if a failure occurred when traversing the object hierarchy 675*cda5da8dSAndroid Build Coastguard Worker within the imported package to get to the desired object. 676*cda5da8dSAndroid Build Coastguard Worker """ 677*cda5da8dSAndroid Build Coastguard Worker global _NAME_PATTERN 678*cda5da8dSAndroid Build Coastguard Worker if _NAME_PATTERN is None: 679*cda5da8dSAndroid Build Coastguard Worker # Lazy import to speedup Python startup time 680*cda5da8dSAndroid Build Coastguard Worker import re 681*cda5da8dSAndroid Build Coastguard Worker dotted_words = r'(?!\d)(\w+)(\.(?!\d)(\w+))*' 682*cda5da8dSAndroid Build Coastguard Worker _NAME_PATTERN = re.compile(f'^(?P<pkg>{dotted_words})' 683*cda5da8dSAndroid Build Coastguard Worker f'(?P<cln>:(?P<obj>{dotted_words})?)?$', 684*cda5da8dSAndroid Build Coastguard Worker re.UNICODE) 685*cda5da8dSAndroid Build Coastguard Worker 686*cda5da8dSAndroid Build Coastguard Worker m = _NAME_PATTERN.match(name) 687*cda5da8dSAndroid Build Coastguard Worker if not m: 688*cda5da8dSAndroid Build Coastguard Worker raise ValueError(f'invalid format: {name!r}') 689*cda5da8dSAndroid Build Coastguard Worker gd = m.groupdict() 690*cda5da8dSAndroid Build Coastguard Worker if gd.get('cln'): 691*cda5da8dSAndroid Build Coastguard Worker # there is a colon - a one-step import is all that's needed 692*cda5da8dSAndroid Build Coastguard Worker mod = importlib.import_module(gd['pkg']) 693*cda5da8dSAndroid Build Coastguard Worker parts = gd.get('obj') 694*cda5da8dSAndroid Build Coastguard Worker parts = parts.split('.') if parts else [] 695*cda5da8dSAndroid Build Coastguard Worker else: 696*cda5da8dSAndroid Build Coastguard Worker # no colon - have to iterate to find the package boundary 697*cda5da8dSAndroid Build Coastguard Worker parts = name.split('.') 698*cda5da8dSAndroid Build Coastguard Worker modname = parts.pop(0) 699*cda5da8dSAndroid Build Coastguard Worker # first part *must* be a module/package. 700*cda5da8dSAndroid Build Coastguard Worker mod = importlib.import_module(modname) 701*cda5da8dSAndroid Build Coastguard Worker while parts: 702*cda5da8dSAndroid Build Coastguard Worker p = parts[0] 703*cda5da8dSAndroid Build Coastguard Worker s = f'{modname}.{p}' 704*cda5da8dSAndroid Build Coastguard Worker try: 705*cda5da8dSAndroid Build Coastguard Worker mod = importlib.import_module(s) 706*cda5da8dSAndroid Build Coastguard Worker parts.pop(0) 707*cda5da8dSAndroid Build Coastguard Worker modname = s 708*cda5da8dSAndroid Build Coastguard Worker except ImportError: 709*cda5da8dSAndroid Build Coastguard Worker break 710*cda5da8dSAndroid Build Coastguard Worker # if we reach this point, mod is the module, already imported, and 711*cda5da8dSAndroid Build Coastguard Worker # parts is the list of parts in the object hierarchy to be traversed, or 712*cda5da8dSAndroid Build Coastguard Worker # an empty list if just the module is wanted. 713*cda5da8dSAndroid Build Coastguard Worker result = mod 714*cda5da8dSAndroid Build Coastguard Worker for p in parts: 715*cda5da8dSAndroid Build Coastguard Worker result = getattr(result, p) 716*cda5da8dSAndroid Build Coastguard Worker return result 717