17db96d56Sopenharmony_ci"""Find modules used by a script, using introspection."""
27db96d56Sopenharmony_ci
37db96d56Sopenharmony_ciimport dis
47db96d56Sopenharmony_ciimport importlib._bootstrap_external
57db96d56Sopenharmony_ciimport importlib.machinery
67db96d56Sopenharmony_ciimport marshal
77db96d56Sopenharmony_ciimport os
87db96d56Sopenharmony_ciimport io
97db96d56Sopenharmony_ciimport sys
107db96d56Sopenharmony_ci
117db96d56Sopenharmony_ci# Old imp constants:
127db96d56Sopenharmony_ci
137db96d56Sopenharmony_ci_SEARCH_ERROR = 0
147db96d56Sopenharmony_ci_PY_SOURCE = 1
157db96d56Sopenharmony_ci_PY_COMPILED = 2
167db96d56Sopenharmony_ci_C_EXTENSION = 3
177db96d56Sopenharmony_ci_PKG_DIRECTORY = 5
187db96d56Sopenharmony_ci_C_BUILTIN = 6
197db96d56Sopenharmony_ci_PY_FROZEN = 7
207db96d56Sopenharmony_ci
217db96d56Sopenharmony_ci# Modulefinder does a good job at simulating Python's, but it can not
227db96d56Sopenharmony_ci# handle __path__ modifications packages make at runtime.  Therefore there
237db96d56Sopenharmony_ci# is a mechanism whereby you can register extra paths in this map for a
247db96d56Sopenharmony_ci# package, and it will be honored.
257db96d56Sopenharmony_ci
267db96d56Sopenharmony_ci# Note this is a mapping is lists of paths.
277db96d56Sopenharmony_cipackagePathMap = {}
287db96d56Sopenharmony_ci
297db96d56Sopenharmony_ci# A Public interface
307db96d56Sopenharmony_cidef AddPackagePath(packagename, path):
317db96d56Sopenharmony_ci    packagePathMap.setdefault(packagename, []).append(path)
327db96d56Sopenharmony_ci
337db96d56Sopenharmony_cireplacePackageMap = {}
347db96d56Sopenharmony_ci
357db96d56Sopenharmony_ci# This ReplacePackage mechanism allows modulefinder to work around
367db96d56Sopenharmony_ci# situations in which a package injects itself under the name
377db96d56Sopenharmony_ci# of another package into sys.modules at runtime by calling
387db96d56Sopenharmony_ci# ReplacePackage("real_package_name", "faked_package_name")
397db96d56Sopenharmony_ci# before running ModuleFinder.
407db96d56Sopenharmony_ci
417db96d56Sopenharmony_cidef ReplacePackage(oldname, newname):
427db96d56Sopenharmony_ci    replacePackageMap[oldname] = newname
437db96d56Sopenharmony_ci
447db96d56Sopenharmony_ci
457db96d56Sopenharmony_cidef _find_module(name, path=None):
467db96d56Sopenharmony_ci    """An importlib reimplementation of imp.find_module (for our purposes)."""
477db96d56Sopenharmony_ci
487db96d56Sopenharmony_ci    # It's necessary to clear the caches for our Finder first, in case any
497db96d56Sopenharmony_ci    # modules are being added/deleted/modified at runtime. In particular,
507db96d56Sopenharmony_ci    # test_modulefinder.py changes file tree contents in a cache-breaking way:
517db96d56Sopenharmony_ci
527db96d56Sopenharmony_ci    importlib.machinery.PathFinder.invalidate_caches()
537db96d56Sopenharmony_ci
547db96d56Sopenharmony_ci    spec = importlib.machinery.PathFinder.find_spec(name, path)
557db96d56Sopenharmony_ci
567db96d56Sopenharmony_ci    if spec is None:
577db96d56Sopenharmony_ci        raise ImportError("No module named {name!r}".format(name=name), name=name)
587db96d56Sopenharmony_ci
597db96d56Sopenharmony_ci    # Some special cases:
607db96d56Sopenharmony_ci
617db96d56Sopenharmony_ci    if spec.loader is importlib.machinery.BuiltinImporter:
627db96d56Sopenharmony_ci        return None, None, ("", "", _C_BUILTIN)
637db96d56Sopenharmony_ci
647db96d56Sopenharmony_ci    if spec.loader is importlib.machinery.FrozenImporter:
657db96d56Sopenharmony_ci        return None, None, ("", "", _PY_FROZEN)
667db96d56Sopenharmony_ci
677db96d56Sopenharmony_ci    file_path = spec.origin
687db96d56Sopenharmony_ci
697db96d56Sopenharmony_ci    if spec.loader.is_package(name):
707db96d56Sopenharmony_ci        return None, os.path.dirname(file_path), ("", "", _PKG_DIRECTORY)
717db96d56Sopenharmony_ci
727db96d56Sopenharmony_ci    if isinstance(spec.loader, importlib.machinery.SourceFileLoader):
737db96d56Sopenharmony_ci        kind = _PY_SOURCE
747db96d56Sopenharmony_ci
757db96d56Sopenharmony_ci    elif isinstance(spec.loader, importlib.machinery.ExtensionFileLoader):
767db96d56Sopenharmony_ci        kind = _C_EXTENSION
777db96d56Sopenharmony_ci
787db96d56Sopenharmony_ci    elif isinstance(spec.loader, importlib.machinery.SourcelessFileLoader):
797db96d56Sopenharmony_ci        kind = _PY_COMPILED
807db96d56Sopenharmony_ci
817db96d56Sopenharmony_ci    else:  # Should never happen.
827db96d56Sopenharmony_ci        return None, None, ("", "", _SEARCH_ERROR)
837db96d56Sopenharmony_ci
847db96d56Sopenharmony_ci    file = io.open_code(file_path)
857db96d56Sopenharmony_ci    suffix = os.path.splitext(file_path)[-1]
867db96d56Sopenharmony_ci
877db96d56Sopenharmony_ci    return file, file_path, (suffix, "rb", kind)
887db96d56Sopenharmony_ci
897db96d56Sopenharmony_ci
907db96d56Sopenharmony_ciclass Module:
917db96d56Sopenharmony_ci
927db96d56Sopenharmony_ci    def __init__(self, name, file=None, path=None):
937db96d56Sopenharmony_ci        self.__name__ = name
947db96d56Sopenharmony_ci        self.__file__ = file
957db96d56Sopenharmony_ci        self.__path__ = path
967db96d56Sopenharmony_ci        self.__code__ = None
977db96d56Sopenharmony_ci        # The set of global names that are assigned to in the module.
987db96d56Sopenharmony_ci        # This includes those names imported through starimports of
997db96d56Sopenharmony_ci        # Python modules.
1007db96d56Sopenharmony_ci        self.globalnames = {}
1017db96d56Sopenharmony_ci        # The set of starimports this module did that could not be
1027db96d56Sopenharmony_ci        # resolved, ie. a starimport from a non-Python module.
1037db96d56Sopenharmony_ci        self.starimports = {}
1047db96d56Sopenharmony_ci
1057db96d56Sopenharmony_ci    def __repr__(self):
1067db96d56Sopenharmony_ci        s = "Module(%r" % (self.__name__,)
1077db96d56Sopenharmony_ci        if self.__file__ is not None:
1087db96d56Sopenharmony_ci            s = s + ", %r" % (self.__file__,)
1097db96d56Sopenharmony_ci        if self.__path__ is not None:
1107db96d56Sopenharmony_ci            s = s + ", %r" % (self.__path__,)
1117db96d56Sopenharmony_ci        s = s + ")"
1127db96d56Sopenharmony_ci        return s
1137db96d56Sopenharmony_ci
1147db96d56Sopenharmony_ciclass ModuleFinder:
1157db96d56Sopenharmony_ci
1167db96d56Sopenharmony_ci    def __init__(self, path=None, debug=0, excludes=None, replace_paths=None):
1177db96d56Sopenharmony_ci        if path is None:
1187db96d56Sopenharmony_ci            path = sys.path
1197db96d56Sopenharmony_ci        self.path = path
1207db96d56Sopenharmony_ci        self.modules = {}
1217db96d56Sopenharmony_ci        self.badmodules = {}
1227db96d56Sopenharmony_ci        self.debug = debug
1237db96d56Sopenharmony_ci        self.indent = 0
1247db96d56Sopenharmony_ci        self.excludes = excludes if excludes is not None else []
1257db96d56Sopenharmony_ci        self.replace_paths = replace_paths if replace_paths is not None else []
1267db96d56Sopenharmony_ci        self.processed_paths = []   # Used in debugging only
1277db96d56Sopenharmony_ci
1287db96d56Sopenharmony_ci    def msg(self, level, str, *args):
1297db96d56Sopenharmony_ci        if level <= self.debug:
1307db96d56Sopenharmony_ci            for i in range(self.indent):
1317db96d56Sopenharmony_ci                print("   ", end=' ')
1327db96d56Sopenharmony_ci            print(str, end=' ')
1337db96d56Sopenharmony_ci            for arg in args:
1347db96d56Sopenharmony_ci                print(repr(arg), end=' ')
1357db96d56Sopenharmony_ci            print()
1367db96d56Sopenharmony_ci
1377db96d56Sopenharmony_ci    def msgin(self, *args):
1387db96d56Sopenharmony_ci        level = args[0]
1397db96d56Sopenharmony_ci        if level <= self.debug:
1407db96d56Sopenharmony_ci            self.indent = self.indent + 1
1417db96d56Sopenharmony_ci            self.msg(*args)
1427db96d56Sopenharmony_ci
1437db96d56Sopenharmony_ci    def msgout(self, *args):
1447db96d56Sopenharmony_ci        level = args[0]
1457db96d56Sopenharmony_ci        if level <= self.debug:
1467db96d56Sopenharmony_ci            self.indent = self.indent - 1
1477db96d56Sopenharmony_ci            self.msg(*args)
1487db96d56Sopenharmony_ci
1497db96d56Sopenharmony_ci    def run_script(self, pathname):
1507db96d56Sopenharmony_ci        self.msg(2, "run_script", pathname)
1517db96d56Sopenharmony_ci        with io.open_code(pathname) as fp:
1527db96d56Sopenharmony_ci            stuff = ("", "rb", _PY_SOURCE)
1537db96d56Sopenharmony_ci            self.load_module('__main__', fp, pathname, stuff)
1547db96d56Sopenharmony_ci
1557db96d56Sopenharmony_ci    def load_file(self, pathname):
1567db96d56Sopenharmony_ci        dir, name = os.path.split(pathname)
1577db96d56Sopenharmony_ci        name, ext = os.path.splitext(name)
1587db96d56Sopenharmony_ci        with io.open_code(pathname) as fp:
1597db96d56Sopenharmony_ci            stuff = (ext, "rb", _PY_SOURCE)
1607db96d56Sopenharmony_ci            self.load_module(name, fp, pathname, stuff)
1617db96d56Sopenharmony_ci
1627db96d56Sopenharmony_ci    def import_hook(self, name, caller=None, fromlist=None, level=-1):
1637db96d56Sopenharmony_ci        self.msg(3, "import_hook", name, caller, fromlist, level)
1647db96d56Sopenharmony_ci        parent = self.determine_parent(caller, level=level)
1657db96d56Sopenharmony_ci        q, tail = self.find_head_package(parent, name)
1667db96d56Sopenharmony_ci        m = self.load_tail(q, tail)
1677db96d56Sopenharmony_ci        if not fromlist:
1687db96d56Sopenharmony_ci            return q
1697db96d56Sopenharmony_ci        if m.__path__:
1707db96d56Sopenharmony_ci            self.ensure_fromlist(m, fromlist)
1717db96d56Sopenharmony_ci        return None
1727db96d56Sopenharmony_ci
1737db96d56Sopenharmony_ci    def determine_parent(self, caller, level=-1):
1747db96d56Sopenharmony_ci        self.msgin(4, "determine_parent", caller, level)
1757db96d56Sopenharmony_ci        if not caller or level == 0:
1767db96d56Sopenharmony_ci            self.msgout(4, "determine_parent -> None")
1777db96d56Sopenharmony_ci            return None
1787db96d56Sopenharmony_ci        pname = caller.__name__
1797db96d56Sopenharmony_ci        if level >= 1: # relative import
1807db96d56Sopenharmony_ci            if caller.__path__:
1817db96d56Sopenharmony_ci                level -= 1
1827db96d56Sopenharmony_ci            if level == 0:
1837db96d56Sopenharmony_ci                parent = self.modules[pname]
1847db96d56Sopenharmony_ci                assert parent is caller
1857db96d56Sopenharmony_ci                self.msgout(4, "determine_parent ->", parent)
1867db96d56Sopenharmony_ci                return parent
1877db96d56Sopenharmony_ci            if pname.count(".") < level:
1887db96d56Sopenharmony_ci                raise ImportError("relative importpath too deep")
1897db96d56Sopenharmony_ci            pname = ".".join(pname.split(".")[:-level])
1907db96d56Sopenharmony_ci            parent = self.modules[pname]
1917db96d56Sopenharmony_ci            self.msgout(4, "determine_parent ->", parent)
1927db96d56Sopenharmony_ci            return parent
1937db96d56Sopenharmony_ci        if caller.__path__:
1947db96d56Sopenharmony_ci            parent = self.modules[pname]
1957db96d56Sopenharmony_ci            assert caller is parent
1967db96d56Sopenharmony_ci            self.msgout(4, "determine_parent ->", parent)
1977db96d56Sopenharmony_ci            return parent
1987db96d56Sopenharmony_ci        if '.' in pname:
1997db96d56Sopenharmony_ci            i = pname.rfind('.')
2007db96d56Sopenharmony_ci            pname = pname[:i]
2017db96d56Sopenharmony_ci            parent = self.modules[pname]
2027db96d56Sopenharmony_ci            assert parent.__name__ == pname
2037db96d56Sopenharmony_ci            self.msgout(4, "determine_parent ->", parent)
2047db96d56Sopenharmony_ci            return parent
2057db96d56Sopenharmony_ci        self.msgout(4, "determine_parent -> None")
2067db96d56Sopenharmony_ci        return None
2077db96d56Sopenharmony_ci
2087db96d56Sopenharmony_ci    def find_head_package(self, parent, name):
2097db96d56Sopenharmony_ci        self.msgin(4, "find_head_package", parent, name)
2107db96d56Sopenharmony_ci        if '.' in name:
2117db96d56Sopenharmony_ci            i = name.find('.')
2127db96d56Sopenharmony_ci            head = name[:i]
2137db96d56Sopenharmony_ci            tail = name[i+1:]
2147db96d56Sopenharmony_ci        else:
2157db96d56Sopenharmony_ci            head = name
2167db96d56Sopenharmony_ci            tail = ""
2177db96d56Sopenharmony_ci        if parent:
2187db96d56Sopenharmony_ci            qname = "%s.%s" % (parent.__name__, head)
2197db96d56Sopenharmony_ci        else:
2207db96d56Sopenharmony_ci            qname = head
2217db96d56Sopenharmony_ci        q = self.import_module(head, qname, parent)
2227db96d56Sopenharmony_ci        if q:
2237db96d56Sopenharmony_ci            self.msgout(4, "find_head_package ->", (q, tail))
2247db96d56Sopenharmony_ci            return q, tail
2257db96d56Sopenharmony_ci        if parent:
2267db96d56Sopenharmony_ci            qname = head
2277db96d56Sopenharmony_ci            parent = None
2287db96d56Sopenharmony_ci            q = self.import_module(head, qname, parent)
2297db96d56Sopenharmony_ci            if q:
2307db96d56Sopenharmony_ci                self.msgout(4, "find_head_package ->", (q, tail))
2317db96d56Sopenharmony_ci                return q, tail
2327db96d56Sopenharmony_ci        self.msgout(4, "raise ImportError: No module named", qname)
2337db96d56Sopenharmony_ci        raise ImportError("No module named " + qname)
2347db96d56Sopenharmony_ci
2357db96d56Sopenharmony_ci    def load_tail(self, q, tail):
2367db96d56Sopenharmony_ci        self.msgin(4, "load_tail", q, tail)
2377db96d56Sopenharmony_ci        m = q
2387db96d56Sopenharmony_ci        while tail:
2397db96d56Sopenharmony_ci            i = tail.find('.')
2407db96d56Sopenharmony_ci            if i < 0: i = len(tail)
2417db96d56Sopenharmony_ci            head, tail = tail[:i], tail[i+1:]
2427db96d56Sopenharmony_ci            mname = "%s.%s" % (m.__name__, head)
2437db96d56Sopenharmony_ci            m = self.import_module(head, mname, m)
2447db96d56Sopenharmony_ci            if not m:
2457db96d56Sopenharmony_ci                self.msgout(4, "raise ImportError: No module named", mname)
2467db96d56Sopenharmony_ci                raise ImportError("No module named " + mname)
2477db96d56Sopenharmony_ci        self.msgout(4, "load_tail ->", m)
2487db96d56Sopenharmony_ci        return m
2497db96d56Sopenharmony_ci
2507db96d56Sopenharmony_ci    def ensure_fromlist(self, m, fromlist, recursive=0):
2517db96d56Sopenharmony_ci        self.msg(4, "ensure_fromlist", m, fromlist, recursive)
2527db96d56Sopenharmony_ci        for sub in fromlist:
2537db96d56Sopenharmony_ci            if sub == "*":
2547db96d56Sopenharmony_ci                if not recursive:
2557db96d56Sopenharmony_ci                    all = self.find_all_submodules(m)
2567db96d56Sopenharmony_ci                    if all:
2577db96d56Sopenharmony_ci                        self.ensure_fromlist(m, all, 1)
2587db96d56Sopenharmony_ci            elif not hasattr(m, sub):
2597db96d56Sopenharmony_ci                subname = "%s.%s" % (m.__name__, sub)
2607db96d56Sopenharmony_ci                submod = self.import_module(sub, subname, m)
2617db96d56Sopenharmony_ci                if not submod:
2627db96d56Sopenharmony_ci                    raise ImportError("No module named " + subname)
2637db96d56Sopenharmony_ci
2647db96d56Sopenharmony_ci    def find_all_submodules(self, m):
2657db96d56Sopenharmony_ci        if not m.__path__:
2667db96d56Sopenharmony_ci            return
2677db96d56Sopenharmony_ci        modules = {}
2687db96d56Sopenharmony_ci        # 'suffixes' used to be a list hardcoded to [".py", ".pyc"].
2697db96d56Sopenharmony_ci        # But we must also collect Python extension modules - although
2707db96d56Sopenharmony_ci        # we cannot separate normal dlls from Python extensions.
2717db96d56Sopenharmony_ci        suffixes = []
2727db96d56Sopenharmony_ci        suffixes += importlib.machinery.EXTENSION_SUFFIXES[:]
2737db96d56Sopenharmony_ci        suffixes += importlib.machinery.SOURCE_SUFFIXES[:]
2747db96d56Sopenharmony_ci        suffixes += importlib.machinery.BYTECODE_SUFFIXES[:]
2757db96d56Sopenharmony_ci        for dir in m.__path__:
2767db96d56Sopenharmony_ci            try:
2777db96d56Sopenharmony_ci                names = os.listdir(dir)
2787db96d56Sopenharmony_ci            except OSError:
2797db96d56Sopenharmony_ci                self.msg(2, "can't list directory", dir)
2807db96d56Sopenharmony_ci                continue
2817db96d56Sopenharmony_ci            for name in names:
2827db96d56Sopenharmony_ci                mod = None
2837db96d56Sopenharmony_ci                for suff in suffixes:
2847db96d56Sopenharmony_ci                    n = len(suff)
2857db96d56Sopenharmony_ci                    if name[-n:] == suff:
2867db96d56Sopenharmony_ci                        mod = name[:-n]
2877db96d56Sopenharmony_ci                        break
2887db96d56Sopenharmony_ci                if mod and mod != "__init__":
2897db96d56Sopenharmony_ci                    modules[mod] = mod
2907db96d56Sopenharmony_ci        return modules.keys()
2917db96d56Sopenharmony_ci
2927db96d56Sopenharmony_ci    def import_module(self, partname, fqname, parent):
2937db96d56Sopenharmony_ci        self.msgin(3, "import_module", partname, fqname, parent)
2947db96d56Sopenharmony_ci        try:
2957db96d56Sopenharmony_ci            m = self.modules[fqname]
2967db96d56Sopenharmony_ci        except KeyError:
2977db96d56Sopenharmony_ci            pass
2987db96d56Sopenharmony_ci        else:
2997db96d56Sopenharmony_ci            self.msgout(3, "import_module ->", m)
3007db96d56Sopenharmony_ci            return m
3017db96d56Sopenharmony_ci        if fqname in self.badmodules:
3027db96d56Sopenharmony_ci            self.msgout(3, "import_module -> None")
3037db96d56Sopenharmony_ci            return None
3047db96d56Sopenharmony_ci        if parent and parent.__path__ is None:
3057db96d56Sopenharmony_ci            self.msgout(3, "import_module -> None")
3067db96d56Sopenharmony_ci            return None
3077db96d56Sopenharmony_ci        try:
3087db96d56Sopenharmony_ci            fp, pathname, stuff = self.find_module(partname,
3097db96d56Sopenharmony_ci                                                   parent and parent.__path__, parent)
3107db96d56Sopenharmony_ci        except ImportError:
3117db96d56Sopenharmony_ci            self.msgout(3, "import_module ->", None)
3127db96d56Sopenharmony_ci            return None
3137db96d56Sopenharmony_ci
3147db96d56Sopenharmony_ci        try:
3157db96d56Sopenharmony_ci            m = self.load_module(fqname, fp, pathname, stuff)
3167db96d56Sopenharmony_ci        finally:
3177db96d56Sopenharmony_ci            if fp:
3187db96d56Sopenharmony_ci                fp.close()
3197db96d56Sopenharmony_ci        if parent:
3207db96d56Sopenharmony_ci            setattr(parent, partname, m)
3217db96d56Sopenharmony_ci        self.msgout(3, "import_module ->", m)
3227db96d56Sopenharmony_ci        return m
3237db96d56Sopenharmony_ci
3247db96d56Sopenharmony_ci    def load_module(self, fqname, fp, pathname, file_info):
3257db96d56Sopenharmony_ci        suffix, mode, type = file_info
3267db96d56Sopenharmony_ci        self.msgin(2, "load_module", fqname, fp and "fp", pathname)
3277db96d56Sopenharmony_ci        if type == _PKG_DIRECTORY:
3287db96d56Sopenharmony_ci            m = self.load_package(fqname, pathname)
3297db96d56Sopenharmony_ci            self.msgout(2, "load_module ->", m)
3307db96d56Sopenharmony_ci            return m
3317db96d56Sopenharmony_ci        if type == _PY_SOURCE:
3327db96d56Sopenharmony_ci            co = compile(fp.read(), pathname, 'exec')
3337db96d56Sopenharmony_ci        elif type == _PY_COMPILED:
3347db96d56Sopenharmony_ci            try:
3357db96d56Sopenharmony_ci                data = fp.read()
3367db96d56Sopenharmony_ci                importlib._bootstrap_external._classify_pyc(data, fqname, {})
3377db96d56Sopenharmony_ci            except ImportError as exc:
3387db96d56Sopenharmony_ci                self.msgout(2, "raise ImportError: " + str(exc), pathname)
3397db96d56Sopenharmony_ci                raise
3407db96d56Sopenharmony_ci            co = marshal.loads(memoryview(data)[16:])
3417db96d56Sopenharmony_ci        else:
3427db96d56Sopenharmony_ci            co = None
3437db96d56Sopenharmony_ci        m = self.add_module(fqname)
3447db96d56Sopenharmony_ci        m.__file__ = pathname
3457db96d56Sopenharmony_ci        if co:
3467db96d56Sopenharmony_ci            if self.replace_paths:
3477db96d56Sopenharmony_ci                co = self.replace_paths_in_code(co)
3487db96d56Sopenharmony_ci            m.__code__ = co
3497db96d56Sopenharmony_ci            self.scan_code(co, m)
3507db96d56Sopenharmony_ci        self.msgout(2, "load_module ->", m)
3517db96d56Sopenharmony_ci        return m
3527db96d56Sopenharmony_ci
3537db96d56Sopenharmony_ci    def _add_badmodule(self, name, caller):
3547db96d56Sopenharmony_ci        if name not in self.badmodules:
3557db96d56Sopenharmony_ci            self.badmodules[name] = {}
3567db96d56Sopenharmony_ci        if caller:
3577db96d56Sopenharmony_ci            self.badmodules[name][caller.__name__] = 1
3587db96d56Sopenharmony_ci        else:
3597db96d56Sopenharmony_ci            self.badmodules[name]["-"] = 1
3607db96d56Sopenharmony_ci
3617db96d56Sopenharmony_ci    def _safe_import_hook(self, name, caller, fromlist, level=-1):
3627db96d56Sopenharmony_ci        # wrapper for self.import_hook() that won't raise ImportError
3637db96d56Sopenharmony_ci        if name in self.badmodules:
3647db96d56Sopenharmony_ci            self._add_badmodule(name, caller)
3657db96d56Sopenharmony_ci            return
3667db96d56Sopenharmony_ci        try:
3677db96d56Sopenharmony_ci            self.import_hook(name, caller, level=level)
3687db96d56Sopenharmony_ci        except ImportError as msg:
3697db96d56Sopenharmony_ci            self.msg(2, "ImportError:", str(msg))
3707db96d56Sopenharmony_ci            self._add_badmodule(name, caller)
3717db96d56Sopenharmony_ci        except SyntaxError as msg:
3727db96d56Sopenharmony_ci            self.msg(2, "SyntaxError:", str(msg))
3737db96d56Sopenharmony_ci            self._add_badmodule(name, caller)
3747db96d56Sopenharmony_ci        else:
3757db96d56Sopenharmony_ci            if fromlist:
3767db96d56Sopenharmony_ci                for sub in fromlist:
3777db96d56Sopenharmony_ci                    fullname = name + "." + sub
3787db96d56Sopenharmony_ci                    if fullname in self.badmodules:
3797db96d56Sopenharmony_ci                        self._add_badmodule(fullname, caller)
3807db96d56Sopenharmony_ci                        continue
3817db96d56Sopenharmony_ci                    try:
3827db96d56Sopenharmony_ci                        self.import_hook(name, caller, [sub], level=level)
3837db96d56Sopenharmony_ci                    except ImportError as msg:
3847db96d56Sopenharmony_ci                        self.msg(2, "ImportError:", str(msg))
3857db96d56Sopenharmony_ci                        self._add_badmodule(fullname, caller)
3867db96d56Sopenharmony_ci
3877db96d56Sopenharmony_ci    def scan_opcodes(self, co):
3887db96d56Sopenharmony_ci        # Scan the code, and yield 'interesting' opcode combinations
3897db96d56Sopenharmony_ci        for name in dis._find_store_names(co):
3907db96d56Sopenharmony_ci            yield "store", (name,)
3917db96d56Sopenharmony_ci        for name, level, fromlist in dis._find_imports(co):
3927db96d56Sopenharmony_ci            if level == 0:  # absolute import
3937db96d56Sopenharmony_ci                yield "absolute_import", (fromlist, name)
3947db96d56Sopenharmony_ci            else:  # relative import
3957db96d56Sopenharmony_ci                yield "relative_import", (level, fromlist, name)
3967db96d56Sopenharmony_ci
3977db96d56Sopenharmony_ci    def scan_code(self, co, m):
3987db96d56Sopenharmony_ci        code = co.co_code
3997db96d56Sopenharmony_ci        scanner = self.scan_opcodes
4007db96d56Sopenharmony_ci        for what, args in scanner(co):
4017db96d56Sopenharmony_ci            if what == "store":
4027db96d56Sopenharmony_ci                name, = args
4037db96d56Sopenharmony_ci                m.globalnames[name] = 1
4047db96d56Sopenharmony_ci            elif what == "absolute_import":
4057db96d56Sopenharmony_ci                fromlist, name = args
4067db96d56Sopenharmony_ci                have_star = 0
4077db96d56Sopenharmony_ci                if fromlist is not None:
4087db96d56Sopenharmony_ci                    if "*" in fromlist:
4097db96d56Sopenharmony_ci                        have_star = 1
4107db96d56Sopenharmony_ci                    fromlist = [f for f in fromlist if f != "*"]
4117db96d56Sopenharmony_ci                self._safe_import_hook(name, m, fromlist, level=0)
4127db96d56Sopenharmony_ci                if have_star:
4137db96d56Sopenharmony_ci                    # We've encountered an "import *". If it is a Python module,
4147db96d56Sopenharmony_ci                    # the code has already been parsed and we can suck out the
4157db96d56Sopenharmony_ci                    # global names.
4167db96d56Sopenharmony_ci                    mm = None
4177db96d56Sopenharmony_ci                    if m.__path__:
4187db96d56Sopenharmony_ci                        # At this point we don't know whether 'name' is a
4197db96d56Sopenharmony_ci                        # submodule of 'm' or a global module. Let's just try
4207db96d56Sopenharmony_ci                        # the full name first.
4217db96d56Sopenharmony_ci                        mm = self.modules.get(m.__name__ + "." + name)
4227db96d56Sopenharmony_ci                    if mm is None:
4237db96d56Sopenharmony_ci                        mm = self.modules.get(name)
4247db96d56Sopenharmony_ci                    if mm is not None:
4257db96d56Sopenharmony_ci                        m.globalnames.update(mm.globalnames)
4267db96d56Sopenharmony_ci                        m.starimports.update(mm.starimports)
4277db96d56Sopenharmony_ci                        if mm.__code__ is None:
4287db96d56Sopenharmony_ci                            m.starimports[name] = 1
4297db96d56Sopenharmony_ci                    else:
4307db96d56Sopenharmony_ci                        m.starimports[name] = 1
4317db96d56Sopenharmony_ci            elif what == "relative_import":
4327db96d56Sopenharmony_ci                level, fromlist, name = args
4337db96d56Sopenharmony_ci                if name:
4347db96d56Sopenharmony_ci                    self._safe_import_hook(name, m, fromlist, level=level)
4357db96d56Sopenharmony_ci                else:
4367db96d56Sopenharmony_ci                    parent = self.determine_parent(m, level=level)
4377db96d56Sopenharmony_ci                    self._safe_import_hook(parent.__name__, None, fromlist, level=0)
4387db96d56Sopenharmony_ci            else:
4397db96d56Sopenharmony_ci                # We don't expect anything else from the generator.
4407db96d56Sopenharmony_ci                raise RuntimeError(what)
4417db96d56Sopenharmony_ci
4427db96d56Sopenharmony_ci        for c in co.co_consts:
4437db96d56Sopenharmony_ci            if isinstance(c, type(co)):
4447db96d56Sopenharmony_ci                self.scan_code(c, m)
4457db96d56Sopenharmony_ci
4467db96d56Sopenharmony_ci    def load_package(self, fqname, pathname):
4477db96d56Sopenharmony_ci        self.msgin(2, "load_package", fqname, pathname)
4487db96d56Sopenharmony_ci        newname = replacePackageMap.get(fqname)
4497db96d56Sopenharmony_ci        if newname:
4507db96d56Sopenharmony_ci            fqname = newname
4517db96d56Sopenharmony_ci        m = self.add_module(fqname)
4527db96d56Sopenharmony_ci        m.__file__ = pathname
4537db96d56Sopenharmony_ci        m.__path__ = [pathname]
4547db96d56Sopenharmony_ci
4557db96d56Sopenharmony_ci        # As per comment at top of file, simulate runtime __path__ additions.
4567db96d56Sopenharmony_ci        m.__path__ = m.__path__ + packagePathMap.get(fqname, [])
4577db96d56Sopenharmony_ci
4587db96d56Sopenharmony_ci        fp, buf, stuff = self.find_module("__init__", m.__path__)
4597db96d56Sopenharmony_ci        try:
4607db96d56Sopenharmony_ci            self.load_module(fqname, fp, buf, stuff)
4617db96d56Sopenharmony_ci            self.msgout(2, "load_package ->", m)
4627db96d56Sopenharmony_ci            return m
4637db96d56Sopenharmony_ci        finally:
4647db96d56Sopenharmony_ci            if fp:
4657db96d56Sopenharmony_ci                fp.close()
4667db96d56Sopenharmony_ci
4677db96d56Sopenharmony_ci    def add_module(self, fqname):
4687db96d56Sopenharmony_ci        if fqname in self.modules:
4697db96d56Sopenharmony_ci            return self.modules[fqname]
4707db96d56Sopenharmony_ci        self.modules[fqname] = m = Module(fqname)
4717db96d56Sopenharmony_ci        return m
4727db96d56Sopenharmony_ci
4737db96d56Sopenharmony_ci    def find_module(self, name, path, parent=None):
4747db96d56Sopenharmony_ci        if parent is not None:
4757db96d56Sopenharmony_ci            # assert path is not None
4767db96d56Sopenharmony_ci            fullname = parent.__name__+'.'+name
4777db96d56Sopenharmony_ci        else:
4787db96d56Sopenharmony_ci            fullname = name
4797db96d56Sopenharmony_ci        if fullname in self.excludes:
4807db96d56Sopenharmony_ci            self.msgout(3, "find_module -> Excluded", fullname)
4817db96d56Sopenharmony_ci            raise ImportError(name)
4827db96d56Sopenharmony_ci
4837db96d56Sopenharmony_ci        if path is None:
4847db96d56Sopenharmony_ci            if name in sys.builtin_module_names:
4857db96d56Sopenharmony_ci                return (None, None, ("", "", _C_BUILTIN))
4867db96d56Sopenharmony_ci
4877db96d56Sopenharmony_ci            path = self.path
4887db96d56Sopenharmony_ci
4897db96d56Sopenharmony_ci        return _find_module(name, path)
4907db96d56Sopenharmony_ci
4917db96d56Sopenharmony_ci    def report(self):
4927db96d56Sopenharmony_ci        """Print a report to stdout, listing the found modules with their
4937db96d56Sopenharmony_ci        paths, as well as modules that are missing, or seem to be missing.
4947db96d56Sopenharmony_ci        """
4957db96d56Sopenharmony_ci        print()
4967db96d56Sopenharmony_ci        print("  %-25s %s" % ("Name", "File"))
4977db96d56Sopenharmony_ci        print("  %-25s %s" % ("----", "----"))
4987db96d56Sopenharmony_ci        # Print modules found
4997db96d56Sopenharmony_ci        keys = sorted(self.modules.keys())
5007db96d56Sopenharmony_ci        for key in keys:
5017db96d56Sopenharmony_ci            m = self.modules[key]
5027db96d56Sopenharmony_ci            if m.__path__:
5037db96d56Sopenharmony_ci                print("P", end=' ')
5047db96d56Sopenharmony_ci            else:
5057db96d56Sopenharmony_ci                print("m", end=' ')
5067db96d56Sopenharmony_ci            print("%-25s" % key, m.__file__ or "")
5077db96d56Sopenharmony_ci
5087db96d56Sopenharmony_ci        # Print missing modules
5097db96d56Sopenharmony_ci        missing, maybe = self.any_missing_maybe()
5107db96d56Sopenharmony_ci        if missing:
5117db96d56Sopenharmony_ci            print()
5127db96d56Sopenharmony_ci            print("Missing modules:")
5137db96d56Sopenharmony_ci            for name in missing:
5147db96d56Sopenharmony_ci                mods = sorted(self.badmodules[name].keys())
5157db96d56Sopenharmony_ci                print("?", name, "imported from", ', '.join(mods))
5167db96d56Sopenharmony_ci        # Print modules that may be missing, but then again, maybe not...
5177db96d56Sopenharmony_ci        if maybe:
5187db96d56Sopenharmony_ci            print()
5197db96d56Sopenharmony_ci            print("Submodules that appear to be missing, but could also be", end=' ')
5207db96d56Sopenharmony_ci            print("global names in the parent package:")
5217db96d56Sopenharmony_ci            for name in maybe:
5227db96d56Sopenharmony_ci                mods = sorted(self.badmodules[name].keys())
5237db96d56Sopenharmony_ci                print("?", name, "imported from", ', '.join(mods))
5247db96d56Sopenharmony_ci
5257db96d56Sopenharmony_ci    def any_missing(self):
5267db96d56Sopenharmony_ci        """Return a list of modules that appear to be missing. Use
5277db96d56Sopenharmony_ci        any_missing_maybe() if you want to know which modules are
5287db96d56Sopenharmony_ci        certain to be missing, and which *may* be missing.
5297db96d56Sopenharmony_ci        """
5307db96d56Sopenharmony_ci        missing, maybe = self.any_missing_maybe()
5317db96d56Sopenharmony_ci        return missing + maybe
5327db96d56Sopenharmony_ci
5337db96d56Sopenharmony_ci    def any_missing_maybe(self):
5347db96d56Sopenharmony_ci        """Return two lists, one with modules that are certainly missing
5357db96d56Sopenharmony_ci        and one with modules that *may* be missing. The latter names could
5367db96d56Sopenharmony_ci        either be submodules *or* just global names in the package.
5377db96d56Sopenharmony_ci
5387db96d56Sopenharmony_ci        The reason it can't always be determined is that it's impossible to
5397db96d56Sopenharmony_ci        tell which names are imported when "from module import *" is done
5407db96d56Sopenharmony_ci        with an extension module, short of actually importing it.
5417db96d56Sopenharmony_ci        """
5427db96d56Sopenharmony_ci        missing = []
5437db96d56Sopenharmony_ci        maybe = []
5447db96d56Sopenharmony_ci        for name in self.badmodules:
5457db96d56Sopenharmony_ci            if name in self.excludes:
5467db96d56Sopenharmony_ci                continue
5477db96d56Sopenharmony_ci            i = name.rfind(".")
5487db96d56Sopenharmony_ci            if i < 0:
5497db96d56Sopenharmony_ci                missing.append(name)
5507db96d56Sopenharmony_ci                continue
5517db96d56Sopenharmony_ci            subname = name[i+1:]
5527db96d56Sopenharmony_ci            pkgname = name[:i]
5537db96d56Sopenharmony_ci            pkg = self.modules.get(pkgname)
5547db96d56Sopenharmony_ci            if pkg is not None:
5557db96d56Sopenharmony_ci                if pkgname in self.badmodules[name]:
5567db96d56Sopenharmony_ci                    # The package tried to import this module itself and
5577db96d56Sopenharmony_ci                    # failed. It's definitely missing.
5587db96d56Sopenharmony_ci                    missing.append(name)
5597db96d56Sopenharmony_ci                elif subname in pkg.globalnames:
5607db96d56Sopenharmony_ci                    # It's a global in the package: definitely not missing.
5617db96d56Sopenharmony_ci                    pass
5627db96d56Sopenharmony_ci                elif pkg.starimports:
5637db96d56Sopenharmony_ci                    # It could be missing, but the package did an "import *"
5647db96d56Sopenharmony_ci                    # from a non-Python module, so we simply can't be sure.
5657db96d56Sopenharmony_ci                    maybe.append(name)
5667db96d56Sopenharmony_ci                else:
5677db96d56Sopenharmony_ci                    # It's not a global in the package, the package didn't
5687db96d56Sopenharmony_ci                    # do funny star imports, it's very likely to be missing.
5697db96d56Sopenharmony_ci                    # The symbol could be inserted into the package from the
5707db96d56Sopenharmony_ci                    # outside, but since that's not good style we simply list
5717db96d56Sopenharmony_ci                    # it missing.
5727db96d56Sopenharmony_ci                    missing.append(name)
5737db96d56Sopenharmony_ci            else:
5747db96d56Sopenharmony_ci                missing.append(name)
5757db96d56Sopenharmony_ci        missing.sort()
5767db96d56Sopenharmony_ci        maybe.sort()
5777db96d56Sopenharmony_ci        return missing, maybe
5787db96d56Sopenharmony_ci
5797db96d56Sopenharmony_ci    def replace_paths_in_code(self, co):
5807db96d56Sopenharmony_ci        new_filename = original_filename = os.path.normpath(co.co_filename)
5817db96d56Sopenharmony_ci        for f, r in self.replace_paths:
5827db96d56Sopenharmony_ci            if original_filename.startswith(f):
5837db96d56Sopenharmony_ci                new_filename = r + original_filename[len(f):]
5847db96d56Sopenharmony_ci                break
5857db96d56Sopenharmony_ci
5867db96d56Sopenharmony_ci        if self.debug and original_filename not in self.processed_paths:
5877db96d56Sopenharmony_ci            if new_filename != original_filename:
5887db96d56Sopenharmony_ci                self.msgout(2, "co_filename %r changed to %r" \
5897db96d56Sopenharmony_ci                                    % (original_filename,new_filename,))
5907db96d56Sopenharmony_ci            else:
5917db96d56Sopenharmony_ci                self.msgout(2, "co_filename %r remains unchanged" \
5927db96d56Sopenharmony_ci                                    % (original_filename,))
5937db96d56Sopenharmony_ci            self.processed_paths.append(original_filename)
5947db96d56Sopenharmony_ci
5957db96d56Sopenharmony_ci        consts = list(co.co_consts)
5967db96d56Sopenharmony_ci        for i in range(len(consts)):
5977db96d56Sopenharmony_ci            if isinstance(consts[i], type(co)):
5987db96d56Sopenharmony_ci                consts[i] = self.replace_paths_in_code(consts[i])
5997db96d56Sopenharmony_ci
6007db96d56Sopenharmony_ci        return co.replace(co_consts=tuple(consts), co_filename=new_filename)
6017db96d56Sopenharmony_ci
6027db96d56Sopenharmony_ci
6037db96d56Sopenharmony_cidef test():
6047db96d56Sopenharmony_ci    # Parse command line
6057db96d56Sopenharmony_ci    import getopt
6067db96d56Sopenharmony_ci    try:
6077db96d56Sopenharmony_ci        opts, args = getopt.getopt(sys.argv[1:], "dmp:qx:")
6087db96d56Sopenharmony_ci    except getopt.error as msg:
6097db96d56Sopenharmony_ci        print(msg)
6107db96d56Sopenharmony_ci        return
6117db96d56Sopenharmony_ci
6127db96d56Sopenharmony_ci    # Process options
6137db96d56Sopenharmony_ci    debug = 1
6147db96d56Sopenharmony_ci    domods = 0
6157db96d56Sopenharmony_ci    addpath = []
6167db96d56Sopenharmony_ci    exclude = []
6177db96d56Sopenharmony_ci    for o, a in opts:
6187db96d56Sopenharmony_ci        if o == '-d':
6197db96d56Sopenharmony_ci            debug = debug + 1
6207db96d56Sopenharmony_ci        if o == '-m':
6217db96d56Sopenharmony_ci            domods = 1
6227db96d56Sopenharmony_ci        if o == '-p':
6237db96d56Sopenharmony_ci            addpath = addpath + a.split(os.pathsep)
6247db96d56Sopenharmony_ci        if o == '-q':
6257db96d56Sopenharmony_ci            debug = 0
6267db96d56Sopenharmony_ci        if o == '-x':
6277db96d56Sopenharmony_ci            exclude.append(a)
6287db96d56Sopenharmony_ci
6297db96d56Sopenharmony_ci    # Provide default arguments
6307db96d56Sopenharmony_ci    if not args:
6317db96d56Sopenharmony_ci        script = "hello.py"
6327db96d56Sopenharmony_ci    else:
6337db96d56Sopenharmony_ci        script = args[0]
6347db96d56Sopenharmony_ci
6357db96d56Sopenharmony_ci    # Set the path based on sys.path and the script directory
6367db96d56Sopenharmony_ci    path = sys.path[:]
6377db96d56Sopenharmony_ci    path[0] = os.path.dirname(script)
6387db96d56Sopenharmony_ci    path = addpath + path
6397db96d56Sopenharmony_ci    if debug > 1:
6407db96d56Sopenharmony_ci        print("path:")
6417db96d56Sopenharmony_ci        for item in path:
6427db96d56Sopenharmony_ci            print("   ", repr(item))
6437db96d56Sopenharmony_ci
6447db96d56Sopenharmony_ci    # Create the module finder and turn its crank
6457db96d56Sopenharmony_ci    mf = ModuleFinder(path, debug, exclude)
6467db96d56Sopenharmony_ci    for arg in args[1:]:
6477db96d56Sopenharmony_ci        if arg == '-m':
6487db96d56Sopenharmony_ci            domods = 1
6497db96d56Sopenharmony_ci            continue
6507db96d56Sopenharmony_ci        if domods:
6517db96d56Sopenharmony_ci            if arg[-2:] == '.*':
6527db96d56Sopenharmony_ci                mf.import_hook(arg[:-2], None, ["*"])
6537db96d56Sopenharmony_ci            else:
6547db96d56Sopenharmony_ci                mf.import_hook(arg)
6557db96d56Sopenharmony_ci        else:
6567db96d56Sopenharmony_ci            mf.load_file(arg)
6577db96d56Sopenharmony_ci    mf.run_script(script)
6587db96d56Sopenharmony_ci    mf.report()
6597db96d56Sopenharmony_ci    return mf  # for -i debugging
6607db96d56Sopenharmony_ci
6617db96d56Sopenharmony_ci
6627db96d56Sopenharmony_ciif __name__ == '__main__':
6637db96d56Sopenharmony_ci    try:
6647db96d56Sopenharmony_ci        mf = test()
6657db96d56Sopenharmony_ci    except KeyboardInterrupt:
6667db96d56Sopenharmony_ci        print("\n[interrupted]")
667