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