/usr/share/cagefs-skeleton/usr/lib64/python3.9
"""Find modules used by a script, using introspection.""" import dis import importlib._bootstrap_external import importlib.machinery import marshal import os import io import sys LOAD_CONST = dis.opmap['LOAD_CONST'] IMPORT_NAME = dis.opmap['IMPORT_NAME'] STORE_NAME = dis.opmap['STORE_NAME'] STORE_GLOBAL = dis.opmap['STORE_GLOBAL'] STORE_OPS = STORE_NAME, STORE_GLOBAL EXTENDED_ARG = dis.EXTENDED_ARG # Old imp constants: _SEARCH_ERROR = 0 _PY_SOURCE = 1 _PY_COMPILED = 2 _C_EXTENSION = 3 _PKG_DIRECTORY = 5 _C_BUILTIN = 6 _PY_FROZEN = 7 # Modulefinder does a good job at simulating Python's, but it can not # handle __path__ modifications packages make at runtime. Therefore there # is a mechanism whereby you can register extra paths in this map for a # package, and it will be honored. # Note this is a mapping is lists of paths. packagePathMap = {} # A Public interface def AddPackagePath(packagename, path): packagePathMap.setdefault(packagename, []).append(path) replacePackageMap = {} # This ReplacePackage mechanism allows modulefinder to work around # situations in which a package injects itself under the name # of another package into sys.modules at runtime by calling # ReplacePackage("real_package_name", "faked_package_name") # before running ModuleFinder. def ReplacePackage(oldname, newname): replacePackageMap[oldname] = newname def _find_module(name, path=None): """An importlib reimplementation of imp.find_module (for our purposes).""" # It's necessary to clear the caches for our Finder first, in case any # modules are being added/deleted/modified at runtime. In particular, # test_modulefinder.py changes file tree contents in a cache-breaking way: importlib.machinery.PathFinder.invalidate_caches() spec = importlib.machinery.PathFinder.find_spec(name, path) if spec is None: raise ImportError("No module named {name!r}".format(name=name), name=name) # Some special cases: if spec.loader is importlib.machinery.BuiltinImporter: return None, None, ("", "", _C_BUILTIN) if spec.loader is importlib.machinery.FrozenImporter: return None, None, ("", "", _PY_FROZEN) file_path = spec.origin if spec.loader.is_package(name): return None, os.path.dirname(file_path), ("", "", _PKG_DIRECTORY) if isinstance(spec.loader, importlib.machinery.SourceFileLoader): kind = _PY_SOURCE elif isinstance(spec.loader, importlib.machinery.ExtensionFileLoader): kind = _C_EXTENSION elif isinstance(spec.loader, importlib.machinery.SourcelessFileLoader): kind = _PY_COMPILED else: # Should never happen. return None, None, ("", "", _SEARCH_ERROR) file = io.open_code(file_path) suffix = os.path.splitext(file_path)[-1] return file, file_path, (suffix, "rb", kind) class Module: def __init__(self, name, file=None, path=None): self.__name__ = name self.__file__ = file self.__path__ = path self.__code__ = None # The set of global names that are assigned to in the module. # This includes those names imported through starimports of # Python modules. self.globalnames = {} # The set of starimports this module did that could not be # resolved, ie. a starimport from a non-Python module. self.starimports = {} def __repr__(self): s = "Module(%r" % (self.__name__,) if self.__file__ is not None: s = s + ", %r" % (self.__file__,) if self.__path__ is not None: s = s + ", %r" % (self.__path__,) s = s + ")" return s class ModuleFinder: def __init__(self, path=None, debug=0, excludes=None, replace_paths=None): if path is None: path = sys.path self.path = path self.modules = {} self.badmodules = {} self.debug = debug self.indent = 0 self.excludes = excludes if excludes is not None else [] self.replace_paths = replace_paths if replace_paths is not None else [] self.processed_paths = [] # Used in debugging only def msg(self, level, str, *args): if level <= self.debug: for i in range(self.indent): print(" ", end=' ') print(str, end=' ') for arg in args: print(repr(arg), end=' ') print() def msgin(self, *args): level = args[0] if level <= self.debug: self.indent = self.indent + 1 self.msg(*args) def msgout(self, *args): level = args[0] if level <= self.debug: self.indent = self.indent - 1 self.msg(*args) def run_script(self, pathname): self.msg(2, "run_script", pathname) with io.open_code(pathname) as fp: stuff = ("", "rb", _PY_SOURCE) self.load_module('__main__', fp, pathname, stuff) def load_file(self, pathname): dir, name = os.path.split(pathname) name, ext = os.path.splitext(name) with io.open_code(pathname) as fp: stuff = (ext, "rb", _PY_SOURCE) self.load_module(name, fp, pathname, stuff) def import_hook(self, name, caller=None, fromlist=None, level=-1): self.msg(3, "import_hook", name, caller, fromlist, level) parent = self.determine_parent(caller, level=level) q, tail = self.find_head_package(parent, name) m = self.load_tail(q, tail) if not fromlist: return q if m.__path__: self.ensure_fromlist(m, fromlist) return None def determine_parent(self, caller, level=-1): self.msgin(4, "determine_parent", caller, level) if not caller or level == 0: self.msgout(4, "determine_parent -> None") return None pname = caller.__name__ if level >= 1: # relative import if caller.__path__: level -= 1 if level == 0: parent = self.modules[pname] assert parent is caller self.msgout(4, "determine_parent ->", parent) return parent if pname.count(".") < level: raise ImportError("relative importpath too deep") pname = ".".join(pname.split(".")[:-level]) parent = self.modules[pname] self.msgout(4, "determine_parent ->", parent) return parent if caller.__path__: parent = self.modules[pname] assert caller is parent self.msgout(4, "determine_parent ->", parent) return parent if '.' in pname: i = pname.rfind('.') pname = pname[:i] parent = self.modules[pname] assert parent.__name__ == pname self.msgout(4, "determine_parent ->", parent) return parent self.msgout(4, "determine_parent -> None") return None def find_head_package(self, parent, name): self.msgin(4, "find_head_package", parent, name) if '.' in name: i = name.find('.') head = name[:i] tail = name[i+1:] else: head = name tail = "" if parent: qname = "%s.%s" % (parent.__name__, head) else: qname = head q = self.import_module(head, qname, parent) if q: self.msgout(4, "find_head_package ->", (q, tail)) return q, tail if parent: qname = head parent = None q = self.import_module(head, qname, parent) if q: self.msgout(4, "find_head_package ->", (q, tail)) return q, tail self.msgout(4, "raise ImportError: No module named", qname) raise ImportError("No module named " + qname) def load_tail(self, q, tail): self.msgin(4, "load_tail", q, tail) m = q while tail: i = tail.find('.') if i < 0: i = len(tail) head, tail = tail[:i], tail[i+1:] mname = "%s.%s" % (m.__name__, head) m = self.import_module(head, mname, m) if not m: self.msgout(4, "raise ImportError: No module named", mname) raise ImportError("No module named " + mname) self.msgout(4, "load_tail ->", m) return m def ensure_fromlist(self, m, fromlist, recursive=0): self.msg(4, "ensure_fromlist", m, fromlist, recursive) for sub in fromlist: if sub == "*": if not recursive: all = self.find_all_submodules(m) if all: self.ensure_fromlist(m, all, 1) elif not hasattr(m, sub): subname = "%s.%s" % (m.__name__, sub) submod = self.import_module(sub, subname, m) if not submod: raise ImportError("No module named " + subname) def find_all_submodules(self, m): if not m.__path__: return modules = {} # 'suffixes' used to be a list hardcoded to [".py", ".pyc"]. # But we must also collect Python extension modules - although # we cannot separate normal dlls from Python extensions. suffixes = [] suffixes += importlib.machinery.EXTENSION_SUFFIXES[:] suffixes += importlib.machinery.SOURCE_SUFFIXES[:] suffixes += importlib.machinery.BYTECODE_SUFFIXES[:] for dir in m.__path__: try: names = os.listdir(dir) except OSError: self.msg(2, "can't list directory", dir) continue for name in names: mod = None for suff in suffixes: n = len(suff) if name[-n:] == suff: mod = name[:-n] break if mod and mod != "__init__": modules[mod] = mod return modules.keys() def import_module(self, partname, fqname, parent): self.msgin(3, "import_module", partname, fqname, parent) try: m = self.modules[fqname] except KeyError: pass else: self.msgout(3, "import_module ->", m) return m if fqname in self.badmodules: self.msgout(3, "import_module -> None") return None if parent and parent.__path__ is None: self.msgout(3, "import_module -> None") return None try: fp, pathname, stuff = self.find_module(partname, parent and parent.__path__, parent) except ImportError: self.msgout(3, "import_module ->", None) return None try: m = self.load_module(fqname, fp, pathname, stuff) finally: if fp: fp.close() if parent: setattr(parent, partname, m) self.msgout(3, "import_module ->", m) return m def load_module(self, fqname, fp, pathname, file_info): suffix, mode, type = file_info self.msgin(2, "load_module", fqname, fp and "fp", pathname) if type == _PKG_DIRECTORY: m = self.load_package(fqname, pathname) self.msgout(2, "load_module ->", m) return m if type == _PY_SOURCE: co = compile(fp.read(), pathname, 'exec') elif type == _PY_COMPILED: try: data = fp.read() importlib._bootstrap_external._classify_pyc(data, fqname, {}) except ImportError as exc: self.msgout(2, "raise ImportError: " + str(exc), pathname) raise co = marshal.loads(memoryview(data)[16:]) else: co = None m = self.add_module(fqname) m.__file__ = pathname if co: if self.replace_paths: co = self.replace_paths_in_code(co) m.__code__ = co self.scan_code(co, m) self.msgout(2, "load_module ->", m) return m def _add_badmodule(self, name, caller): if name not in self.badmodules: self.badmodules[name] = {} if caller: self.badmodules[name][caller.__name__] = 1 else: self.badmodules[name]["-"] = 1 def _safe_import_hook(self, name, caller, fromlist, level=-1): # wrapper for self.import_hook() that won't raise ImportError if name in self.badmodules: self._add_badmodule(name, caller) return try: self.import_hook(name, caller, level=level) except ImportError as msg: self.msg(2, "ImportError:", str(msg)) self._add_badmodule(name, caller) except SyntaxError as msg: self.msg(2, "SyntaxError:", str(msg)) self._add_badmodule(name, caller) else: if fromlist: for sub in fromlist: fullname = name + "." + sub if fullname in self.badmodules: self._add_badmodule(fullname, caller) continue try: self.import_hook(name, caller, [sub], level=level) except ImportError as msg: self.msg(2, "ImportError:", str(msg)) self._add_badmodule(fullname, caller) def scan_opcodes(self, co): # Scan the code, and yield 'interesting' opcode combinations code = co.co_code names = co.co_names consts = co.co_consts opargs = [(op, arg) for _, op, arg in dis._unpack_opargs(code) if op != EXTENDED_ARG] for i, (op, oparg) in enumerate(opargs): if op in STORE_OPS: yield "store", (names[oparg],) continue if (op == IMPORT_NAME and i >= 2 and opargs[i-1][0] == opargs[i-2][0] == LOAD_CONST): level = consts[opargs[i-2][1]] fromlist = consts[opargs[i-1][1]] if level == 0: # absolute import yield "absolute_import", (fromlist, names[oparg]) else: # relative import yield "relative_import", (level, fromlist, names[oparg]) continue def scan_code(self, co, m): code = co.co_code scanner = self.scan_opcodes for what, args in scanner(co): if what == "store": name, = args m.globalnames[name] = 1 elif what == "absolute_import": fromlist, name = args have_star = 0 if fromlist is not None: if "*" in fromlist: have_star = 1 fromlist = [f for f in fromlist if f != "*"] self._safe_import_hook(name, m, fromlist, level=0) if have_star: # We've encountered an "import *". If it is a Python module, # the code has already been parsed and we can suck out the # global names. mm = None if m.__path__: # At this point we don't know whether 'name' is a # submodule of 'm' or a global module. Let's just try # the full name first. mm = self.modules.get(m.__name__ + "." + name) if mm is None: mm = self.modules.get(name) if mm is not None: m.globalnames.update(mm.globalnames) m.starimports.update(mm.starimports) if mm.__code__ is None: m.starimports[name] = 1 else: m.starimports[name] = 1 elif what == "relative_import": level, fromlist, name = args if name: self._safe_import_hook(name, m, fromlist, level=level) else: parent = self.determine_parent(m, level=level) self._safe_import_hook(parent.__name__, None, fromlist, level=0) else: # We don't expect anything else from the generator. raise RuntimeError(what) for c in co.co_consts: if isinstance(c, type(co)): self.scan_code(c, m) def load_package(self, fqname, pathname): self.msgin(2, "load_package", fqname, pathname) newname = replacePackageMap.get(fqname) if newname: fqname = newname m = self.add_module(fqname) m.__file__ = pathname m.__path__ = [pathname] # As per comment at top of file, simulate runtime __path__ additions. m.__path__ = m.__path__ + packagePathMap.get(fqname, []) fp, buf, stuff = self.find_module("__init__", m.__path__) try: self.load_module(fqname, fp, buf, stuff) self.msgout(2, "load_package ->", m) return m finally: if fp: fp.close() def add_module(self, fqname): if fqname in self.modules: return self.modules[fqname] self.modules[fqname] = m = Module(fqname) return m def find_module(self, name, path, parent=None): if parent is not None: # assert path is not None fullname = parent.__name__+'.'+name else: fullname = name if fullname in self.excludes: self.msgout(3, "find_module -> Excluded", fullname) raise ImportError(name) if path is None: if name in sys.builtin_module_names: return (None, None, ("", "", _C_BUILTIN)) path = self.path return _find_module(name, path) def report(self): """Print a report to stdout, listing the found modules with their paths, as well as modules that are missing, or seem to be missing. """ print() print(" %-25s %s" % ("Name", "File")) print(" %-25s %s" % ("----", "----")) # Print modules found keys = sorted(self.modules.keys()) for key in keys: m = self.modules[key] if m.__path__: print("P", end=' ') else: print("m", end=' ') print("%-25s" % key, m.__file__ or "") # Print missing modules missing, maybe = self.any_missing_maybe() if missing: print() print("Missing modules:") for name in missing: mods = sorted(self.badmodules[name].keys()) print("?", name, "imported from", ', '.join(mods)) # Print modules that may be missing, but then again, maybe not... if maybe: print() print("Submodules that appear to be missing, but could also be", end=' ') print("global names in the parent package:") for name in maybe: mods = sorted(self.badmodules[name].keys()) print("?", name, "imported from", ', '.join(mods)) def any_missing(self): """Return a list of modules that appear to be missing. Use any_missing_maybe() if you want to know which modules are certain to be missing, and which *may* be missing. """ missing, maybe = self.any_missing_maybe() return missing + maybe def any_missing_maybe(self): """Return two lists, one with modules that are certainly missing and one with modules that *may* be missing. The latter names could either be submodules *or* just global names in the package. The reason it can't always be determined is that it's impossible to tell which names are imported when "from module import *" is done with an extension module, short of actually importing it. """ missing = [] maybe = [] for name in self.badmodules: if name in self.excludes: continue i = name.rfind(".") if i < 0: missing.append(name) continue subname = name[i+1:] pkgname = name[:i] pkg = self.modules.get(pkgname) if pkg is not None: if pkgname in self.badmodules[name]: # The package tried to import this module itself and # failed. It's definitely missing. missing.append(name) elif subname in pkg.globalnames: # It's a global in the package: definitely not missing. pass elif pkg.starimports: # It could be missing, but the package did an "import *" # from a non-Python module, so we simply can't be sure. maybe.append(name) else: # It's not a global in the package, the package didn't # do funny star imports, it's very likely to be missing. # The symbol could be inserted into the package from the # outside, but since that's not good style we simply list # it missing. missing.append(name) else: missing.append(name) missing.sort() maybe.sort() return missing, maybe def replace_paths_in_code(self, co): new_filename = original_filename = os.path.normpath(co.co_filename) for f, r in self.replace_paths: if original_filename.startswith(f): new_filename = r + original_filename[len(f):] break if self.debug and original_filename not in self.processed_paths: if new_filename != original_filename: self.msgout(2, "co_filename %r changed to %r" \ % (original_filename,new_filename,)) else: self.msgout(2, "co_filename %r remains unchanged" \ % (original_filename,)) self.processed_paths.append(original_filename) consts = list(co.co_consts) for i in range(len(consts)): if isinstance(consts[i], type(co)): consts[i] = self.replace_paths_in_code(consts[i]) return co.replace(co_consts=tuple(consts), co_filename=new_filename) def test(): # Parse command line import getopt try: opts, args = getopt.getopt(sys.argv[1:], "dmp:qx:") except getopt.error as msg: print(msg) return # Process options debug = 1 domods = 0 addpath = [] exclude = [] for o, a in opts: if o == '-d': debug = debug + 1 if o == '-m': domods = 1 if o == '-p': addpath = addpath + a.split(os.pathsep) if o == '-q': debug = 0 if o == '-x': exclude.append(a) # Provide default arguments if not args: script = "hello.py" else: script = args[0] # Set the path based on sys.path and the script directory path = sys.path[:] path[0] = os.path.dirname(script) path = addpath + path if debug > 1: print("path:") for item in path: print(" ", repr(item)) # Create the module finder and turn its crank mf = ModuleFinder(path, debug, exclude) for arg in args[1:]: if arg == '-m': domods = 1 continue if domods: if arg[-2:] == '.*': mf.import_hook(arg[:-2], None, ["*"]) else: mf.import_hook(arg) else: mf.load_file(arg) mf.run_script(script) mf.report() return mf # for -i debugging if __name__ == '__main__': try: mf = test() except KeyboardInterrupt: print("\n[interrupted]")
.
Edit
..
Edit
LICENSE.txt
Edit
__future__.py
Edit
__phello__.foo.py
Edit
__pycache__
Edit
_aix_support.py
Edit
_bootlocale.py
Edit
_bootsubprocess.py
Edit
_collections_abc.py
Edit
_compat_pickle.py
Edit
_compression.py
Edit
_markupbase.py
Edit
_osx_support.py
Edit
_py_abc.py
Edit
_pydecimal.py
Edit
_pyio.py
Edit
_sitebuiltins.py
Edit
_strptime.py
Edit
_sysconfigdata__linux_x86_64-linux-gnu.py
Edit
_sysconfigdata_d_linux_x86_64-linux-gnu.py
Edit
_threading_local.py
Edit
_weakrefset.py
Edit
abc.py
Edit
aifc.py
Edit
antigravity.py
Edit
argparse.py
Edit
ast.py
Edit
asynchat.py
Edit
asyncio
Edit
asyncore.py
Edit
base64.py
Edit
bdb.py
Edit
binhex.py
Edit
bisect.py
Edit
bz2.py
Edit
cProfile.py
Edit
calendar.py
Edit
cgi.py
Edit
cgitb.py
Edit
chunk.py
Edit
cmd.py
Edit
code.py
Edit
codecs.py
Edit
codeop.py
Edit
collections
Edit
colorsys.py
Edit
compileall.py
Edit
concurrent
Edit
config-3.9-x86_64-linux-gnu
Edit
configparser.py
Edit
contextlib.py
Edit
contextvars.py
Edit
copy.py
Edit
copyreg.py
Edit
crypt.py
Edit
csv.py
Edit
ctypes
Edit
curses
Edit
dataclasses.py
Edit
datetime.py
Edit
dbm
Edit
decimal.py
Edit
difflib.py
Edit
dis.py
Edit
distutils
Edit
doctest.py
Edit
email
Edit
encodings
Edit
ensurepip
Edit
enum.py
Edit
filecmp.py
Edit
fileinput.py
Edit
fnmatch.py
Edit
formatter.py
Edit
fractions.py
Edit
ftplib.py
Edit
functools.py
Edit
genericpath.py
Edit
getopt.py
Edit
getpass.py
Edit
gettext.py
Edit
glob.py
Edit
graphlib.py
Edit
gzip.py
Edit
hashlib.py
Edit
heapq.py
Edit
hmac.py
Edit
html
Edit
http
Edit
imaplib.py
Edit
imghdr.py
Edit
imp.py
Edit
importlib
Edit
inspect.py
Edit
io.py
Edit
ipaddress.py
Edit
json
Edit
keyword.py
Edit
lib-dynload
Edit
lib2to3
Edit
linecache.py
Edit
locale.py
Edit
logging
Edit
lzma.py
Edit
mailbox.py
Edit
mailcap.py
Edit
mimetypes.py
Edit
modulefinder.py
Edit
multiprocessing
Edit
netrc.py
Edit
nntplib.py
Edit
ntpath.py
Edit
nturl2path.py
Edit
numbers.py
Edit
opcode.py
Edit
operator.py
Edit
optparse.py
Edit
os.py
Edit
pathlib.py
Edit
pdb.py
Edit
pickle.py
Edit
pickletools.py
Edit
pipes.py
Edit
pkgutil.py
Edit
platform.py
Edit
plistlib.py
Edit
poplib.py
Edit
posixpath.py
Edit
pprint.py
Edit
profile.py
Edit
pstats.py
Edit
pty.py
Edit
py_compile.py
Edit
pyclbr.py
Edit
pydoc.py
Edit
pydoc_data
Edit
queue.py
Edit
quopri.py
Edit
random.py
Edit
re.py
Edit
reprlib.py
Edit
rlcompleter.py
Edit
runpy.py
Edit
sched.py
Edit
secrets.py
Edit
selectors.py
Edit
shelve.py
Edit
shlex.py
Edit
shutil.py
Edit
signal.py
Edit
site-packages
Edit
site.py
Edit
smtpd.py
Edit
smtplib.py
Edit
sndhdr.py
Edit
socket.py
Edit
socketserver.py
Edit
sqlite3
Edit
sre_compile.py
Edit
sre_constants.py
Edit
sre_parse.py
Edit
ssl.py
Edit
stat.py
Edit
statistics.py
Edit
string.py
Edit
stringprep.py
Edit
struct.py
Edit
subprocess.py
Edit
sunau.py
Edit
symbol.py
Edit
symtable.py
Edit
sysconfig.py
Edit
tabnanny.py
Edit
tarfile.py
Edit
telnetlib.py
Edit
tempfile.py
Edit
textwrap.py
Edit
this.py
Edit
threading.py
Edit
timeit.py
Edit
token.py
Edit
tokenize.py
Edit
trace.py
Edit
traceback.py
Edit
tracemalloc.py
Edit
tty.py
Edit
types.py
Edit
typing.py
Edit
unittest
Edit
urllib
Edit
uu.py
Edit
uuid.py
Edit
venv
Edit
warnings.py
Edit
wave.py
Edit
weakref.py
Edit
webbrowser.py
Edit
wsgiref
Edit
xdrlib.py
Edit
xml
Edit
xmlrpc
Edit
zipapp.py
Edit
zipfile.py
Edit
zipimport.py
Edit
zoneinfo
Edit