#!/usr/bin/env python3 """Shim context harness (PV-4, issue #5 phase 1). Runs ONE import scenario in a fresh interpreter and prints a JSON report of which tree every relevant module resolved to. Kept out of the test module on purpose: each scenario needs a virgin ``sys.modules``, so it must be a subprocess. Usage: shim_contexts.py Tree arguments are skills-directory roots, each containing ``lib_py/`` and ``multi-agent-mux-delegate-job/scripts/``. The scenarios mirror the four real execution contexts: pytest in-process (A/A_pe/B/B2/D/E), standalone with no ambient tree (C1/C1_pe), and a frozen copy invoked while PYTHONPATH still points at the live tree (C2/C2_pe) -- the shape every delegated agent job actually runs. """ import json import os import sys SCENARIO = sys.argv[1] TREES = { "real": os.path.abspath(sys.argv[2]), "sandbox": os.path.abspath(sys.argv[3]), "sandbox2": os.path.abspath(sys.argv[4]), "freeze": os.path.abspath(sys.argv[5]), } def scripts(tree): return os.path.join(TREES[tree], "multi-agent-mux-delegate-job", "scripts") def which_tree(path): if not path: return None path = os.path.abspath(path) for name, root in TREES.items(): if path == root or path.startswith(root + os.sep): return name return "OUTSIDE:" + path def report(): out = {"scenario": SCENARIO} mods = sys.modules def note(key, mod, attr="__file__"): m = mods.get(mod) if m is None: out[key] = None return if attr == "__path__": out[key] = which_tree(list(getattr(m, "__path__", [None]))[0]) else: out[key] = which_tree(getattr(m, attr, None)) note("alias_mqtt_common", "mqtt_common") note("alias_publish_event", "publish_event") note("pkg_delegation", "lib_py.delegation", "__path__") note("pkg_mqtt_common", "lib_py.delegation.mqtt_common") note("pkg_publish_event", "lib_py.delegation.publish_event") note("lib_py", "lib_py", "__path__") note("lib_py_agents", "lib_py.agents", "__path__") # identity: the alias and the package attribute must be one object ident = {} for name in ("mqtt_common", "publish_event"): alias = mods.get(name) pkg_mod = mods.get("lib_py.delegation." + name) ident[name] = None if alias is None else (alias is pkg_mod) out["identity"] = ident # PV-3 shape: sys.path entries that would contribute a lib_py package out["lib_py_path_entries"] = [ which_tree(e) for e in sys.path if e and os.path.isdir(os.path.join(e, "lib_py")) ] return out if SCENARIO in ("A", "A_pe", "B", "B2", "D", "E"): sys.path.insert(0, TREES["real"]) # models conftest.py inserting the real tree if SCENARIO == "A": sys.path.insert(0, scripts("sandbox")) import mqtt_common, publish_event # noqa: F401 elif SCENARIO == "A_pe": sys.path.insert(0, scripts("sandbox")) import publish_event # noqa: F401 elif SCENARIO == "B": import lib_py.agents # noqa: F401 sys.path.insert(0, scripts("sandbox")) import mqtt_common, publish_event # noqa: F401 elif SCENARIO == "B2": import lib_py.delegation.mqtt_common # noqa: F401 sys.path.insert(0, scripts("sandbox")) import mqtt_common, publish_event # noqa: F401 elif SCENARIO == "D": sys.path.insert(0, scripts("sandbox")) import mqtt_common # noqa: F401 sys.path.insert(0, scripts("sandbox2")) import publish_event # noqa: F401 elif SCENARIO == "E": sys.path.insert(0, scripts("sandbox")) import mqtt_common # noqa: F401 from lib_py.delegation import publish_event # noqa: F401 elif SCENARIO == "C1": sys.path.insert(0, scripts("freeze")) import mqtt_common, publish_event # noqa: F401 elif SCENARIO == "C1_pe": sys.path.insert(0, scripts("freeze")) import publish_event # noqa: F401 elif SCENARIO == "C2": sys.path.insert(0, scripts("freeze")) # PYTHONPATH=real set by caller import mqtt_common, publish_event # noqa: F401 elif SCENARIO == "C2_pe": sys.path.insert(0, scripts("freeze")) # PYTHONPATH=real set by caller import publish_event # noqa: F401 elif SCENARIO == "K3": sys.path.insert(0, scripts("real")) import mqtt_common, publish_event # noqa: F401 else: raise SystemExit("unknown scenario: " + SCENARIO) print(json.dumps(report()))