xref: /aosp_15_r20/prebuilts/build-tools/common/py3-stdlib/pkgutil.py (revision cda5da8d549138a6648c5ee6d7a49cf8f4a657be)
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