#!/usr/bin/env python2.7 """Embedded-Python feasibility spike (Python 2.7 only; see docs/PYTHON-EMBED-EVAL.md). Emulates CPythonLauncher::Create + system.py bootstrap: stub native modules (every call returns 0), a directory-backed `pack` module over the 40250 Eternexus tree, then imports every root module through system.py's own `__pack_import`. Reports loaded/failed modules to --out (default py_embed_spike.txt); system.py redirects stdout, so nothing useful is printed. python2.7 py_embed_spike.py [--out FILE] (reference root: MT_40250_SOURCE or audit manifest) """ import sys, types, os, glob, traceback if os.environ.get("MT_ETERNEXUS"): # on-device run: Eternexus tree pushed next to the binary E = os.environ["MT_ETERNEXUS"] else: HERE = os.path.dirname(os.path.abspath(__file__)) REPO = os.path.abspath(os.path.join(HERE, "../../../..")) import json _ref = os.environ.get("MT_40250_SOURCE") or json.load(open(os.path.join(REPO, "audit/manifest.json")))["reference_root"] E = str(os.path.normpath(os.path.join(REPO, _ref, "../../Client/Eternexus"))) # json gives unicode OUT = sys.argv[sys.argv.index("--out") + 1] if "--out" in sys.argv else "py_embed_spike.txt" FILES = {} for p in glob.glob(E + "/root/*"): FILES[os.path.basename(p).lower()] = p for base, pre in ((E + "/locale_en/locale/en", "locale/en/"), (E + "/uiscript/uiscript", "uiscript/")): for d, _, fs in os.walk(base): for f in fs: full = os.path.join(d, f) FILES[(pre + os.path.relpath(full, base)).lower()] = full LOG = open(OUT, "w") MISSING = {} class Stub(types.ModuleType): def __getattr__(self, name): if name.startswith("__"): raise AttributeError(name) key = "%s.%s" % (self.__name__, name) def f(*a, **k): MISSING[key] = MISSING.get(key, 0) + 1 return 0 setattr(self, name, f); return f NATIVE = "app net player chr chrmgr wndMgr grp grpImage grpText grpThing item skill snd chat background miniMap ime systemSetting guild safebox shop exchange quest messenger textTail nonplayer effect event eventMgr fly dbg profiler ServerStateChecker udp".split() for n in NATIVE: sys.modules[n] = Stub(n) pack = types.ModuleType("pack") def norm(n): return n.replace("\\", "/").lower().lstrip("/") pack.Exist = lambda n: norm(n) in FILES pack.Get = lambda n: open(FILES[norm(n)], "rb").read() sys.modules["pack"] = pack sys.modules["dbg"].Trace = sys.modules["dbg"].TraceError = lambda *a: None sys.modules["dbg"].LogBox = lambda m: LOG.write("LOGBOX: %s\n" % m) sys.modules["app"].Loop = lambda: LOG.write("app.Loop reached\n") STR = {"app.GetLocalePath": "locale/en", "app.GetLocaleName": "en", "app.GetLocaleServiceName": "en", "app.GetLocalePathCommon": "locale/common"} for k, v in STR.items(): m, f = k.split(".") setattr(sys.modules[m], f, (lambda v: lambda *a: v)(v)) import __builtin__ __builtin__.TRUE = 1; __builtin__.FALSE = 0 # CPythonLauncher::Create # A fresh __main__ like CPythonLauncher::Create (whose __builtins__ is the module, not a dict); # system.py and prototype.py exec into it, so they must not share this script's globals. __main__ = types.ModuleType("__main__") __main__.__builtins__ = __builtin__ sys.modules["__main__"] = __main__ __main__.__DEBUG__ = 0 __main__.__COMMAND_LINE__ = "" src = pack.Get("system.py") try: exec compile(src, "system.py", "exec") in __main__.__dict__ except BaseException: LOG.write("system.py raised:\n" + traceback.format_exc()) sys.stdout, sys.stderr = sys.__stdout__, sys.__stderr__ # system.py redirects both to dbg ok, bad = [], [] _pimp = __main__.__dict__["__pack_import"] for name in sorted(FILES): if not name.endswith(".py") or "/" in name: continue mod = name[:-3] real = [m for m in sys.modules if m.lower() == mod] if real: ok.append(mod); continue try: _pimp(mod); ok.append(mod) except BaseException as e: bad.append((mod, "%s: %s" % (type(e).__name__, repr(e.args)[:100]))) try: LOG.write("modules loaded %d, failed %d\n" % (len(ok), len(bad))) for m, e in bad: LOG.write(" FAIL %s %s\n" % (m, e)) LOG.write("native functions called during bootstrap: %d\n" % len(MISSING)) LOG.write(" " + " ".join(sorted(MISSING)) + "\n") std = [] for name, m in sorted(sys.modules.items()): if m is None or name in NATIVE or name in ("pack", "__main__"): continue try: f = m.__dict__.get("__file__") except Exception: continue if not isinstance(f, str) and f is not None: continue if f is None: std.append("builtin:" + name) elif f.endswith(".so"): std.append("ext:" + name) elif not f.startswith(E) and not f.startswith("<"): std.append("py:" + name) LOG.write("stdlib modules in use: " + " ".join(std) + "\n") except BaseException: LOG.write("report failed:\n" + traceback.format_exc()) LOG.close()