#!/usr/bin/env python3 """Replace hardcoded `localhost` URLs in the imported XVI checkers with self.url(). Why: the imported checkers connect to `http://localhost:{self.port}`. The receiver does run on the same host as the published team ports, so this happens to work, but it is fragile (breaks the moment a receiver runs in a container or the port is bound to a specific interface). Challenge.url() builds the URL from self.host (default 127.0.0.1, overridable with RECEIVER_HOST) plus self.port. Idempotent; rewrites only the f-string form, leaving class-level constants that pin a *fixed* port (GemasNotes/Pasta/S3) alone — those are handled separately. """ import re import sys from pathlib import Path BASE = Path("/opt/gemastik18-final/receiver/challenges/xvi") # f"http://localhost:{self.port}/path/{expr}" -> self.url(f"/path/{expr}") # The leading f is part of the literal being matched and must be consumed. PAT = re.compile( r"""f?(?P["'])http://localhost:\{self\.port\}(?P/[^"']*)?(?P=q)""" ) def repl(m: "re.Match[str]") -> str: path = m.group("path") or "" if not path: return "self.url()" # the path may itself contain {expr} placeholders — keep them as an f-string return f"self.url(f{path!r})" def main() -> int: changed = [] for p in sorted(BASE.glob("*.py")): if p.name in ("Challenge.py", "config.py", "__init__.py"): continue src = p.read_text() if "localhost:{self.port}" not in src: continue new = PAT.sub(repl, src) if new != src: p.write_text(new) changed.append(p.name) print("rewritten:", ", ".join(changed) if changed else "(none)") return 0 if __name__ == "__main__": raise SystemExit(main())