from contextlib import contextmanager, redirect_stdout, redirect_stderr from concurrent.futures import ThreadPoolExecutor from time import time, sleep import html, io, json, os, re, subprocess, sys, tempfile, threading, traceback, urllib.request, webbrowser from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer __all__ = ["plan", "phase", "parallel", "mapchain"] _ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) _PORT = int(os.environ.get("GA_ULTRAPLAN_PORT", "47831")) _T0 = time(); _phases = []; _phase_stack = []; _tasks = []; _current = "idle"; _events = []; _srv = None; _last = time(); _lock = threading.Lock(); _exec_lock = threading.Lock() _TASK_SLUG = "task"; _FUNC_SEQ = 0; _PLANNED = False; _SESSION = None; _sessions = {} _RUN_DIR = os.path.abspath(os.environ.get("GA_ULTRAPLAN_RUNDIR", os.path.join(_ROOT, "temp", "ultraplan_default"))) os.makedirs(_RUN_DIR, exist_ok=True) def _bind(rundir): global _SESSION, _RUN_DIR, _phases, _phase_stack, _tasks, _current, _events, _FUNC_SEQ, _TASK_SLUG key = os.path.abspath(rundir); os.makedirs(key, exist_ok=True) s = _sessions.setdefault(key, {"rundir": key, "phases": [], "phase_stack": [], "tasks": [], "current": "idle", "events": [], "func_seq": 0, "task_slug": "task"}) _SESSION = key; _RUN_DIR = key; _phases = s["phases"]; _phase_stack = s["phase_stack"]; _tasks = s["tasks"]; _current = s["current"]; _events = s["events"]; _FUNC_SEQ = s["func_seq"]; _TASK_SLUG = s["task_slug"] return s def _save_session(): if _SESSION in _sessions: _sessions[_SESSION].update(current=_current, func_seq=_FUNC_SEQ, task_slug=_TASK_SLUG) def _need_plan(): if not _PLANNED: raise RuntimeError("call plan(rundir) as the first UltraPlan statement") def _slug(s): s = re.sub(r"[^a-zA-Z0-9]+", "_", str(s)).strip("_").lower() return s[:80] or "task" def _task_slug(path): stem = os.path.splitext(os.path.basename(path or "task"))[0] parts = [_slug(x) for x in re.split(r"[_\-]+", stem)] stop = {"ultra", "ultraplan", "script", "boot", "build", "test", "debug", "verify", "explore", "reduce", "phase"} parts = [p for p in parts if p and not p.isdigit() and p not in stop] return "_".join(parts) or _slug(stem) def _note(s): global _last with _lock: _last = time(); _events.append(f"{_last-_T0:7.1f}s {s}"); del _events[:-60] def _phase_lines(nodes, depth=0): out = [] for p in nodes: pre = " " * depth; mark = ">>" if p["on"] else " " out.append(f"{pre}{mark} {p['status']:<7} {p['name']}" + (f" - {p['desc']}" if p['desc'] else "")) out += [f"{pre} | {op}" for op in p.get("ops", [])[-8:]] out += [f"{pre} - {t['status']:<5} {t['desc']}" for t in p.get("tasks", [])[-20:]] out += _phase_lines(p.get("children", []), depth + 1) return out def _page(): with _lock: lines = ["GA UltraPlan"] for key, s in _sessions.items(): lines += ["", f"== {os.path.basename(key) or key} ==", f"rundir: {key}", f"current: {s['current']}", "", "phases:"] lines += _phase_lines(s["phases"]) or ["(none)"] lines += ["", "recent tasks:"] lines += [f"{t['status']:<7} {t['desc']}" for t in s["tasks"][-12:]] or ["(none)"] lines += ["", "events:", *s["events"][-30:]] if not _sessions: lines += ["", "(no sessions)"] return "
" + html.escape("\n".join(lines)) + ""
class _H(BaseHTTPRequestHandler):
def do_GET(self):
b = _page().encode("utf-8"); self.send_response(200); self.send_header("Content-Type", "text/html; charset=utf-8"); self.end_headers(); self.wfile.write(b)
def do_POST(self):
global _TASK_SLUG, _PLANNED
if self.path != "/exec": self.send_response(404); self.end_headers(); return
n = int(self.headers.get("Content-Length", "0")); req = json.loads(self.rfile.read(n).decode("utf-8"))
out = io.StringIO(); err = io.StringIO(); rc = 0
with _exec_lock, redirect_stdout(out), redirect_stderr(err):
_bind(req["rundir"]); _note("exec: " + req.get("path", "