window.ENGINE_PY = "import io\nimport json\nimport sys\nimport threading\nimport time\nimport traceback\n\nDEFAULT_TIMEOUT = 8.0\nEXERCISE_TEST_TIMEOUT = 2.0\nMAX_VARS = 40\nMAX_REPR = 200\n# W3: cap captured stdout/stderr so a runaway print loop cannot blow up the\n# JSON payload or exhaust memory. 1 MiB is plenty for terminal output.\nMAX_OUTPUT = 1_048_576\n# W2: coarse byte-size guard applied *before* calling repr() on a user value,\n# so a gigantic object (or a repr that would allocate massively) is skipped.\nMAX_VAR_BYTES = 1_000_000\n\n\nclass SandboxTimeout(Exception):\n pass\n\n\nclass InputExhausted(Exception):\n pass\n\n\ndef _make_input(stdin_lines):\n queue = list(stdin_lines)\n\n def _input(prompt=\"\"):\n if prompt:\n sys.stdout.write(str(prompt))\n sys.stdout.flush()\n if not queue:\n raise InputExhausted(\n \"运行台不支持交互式输入:请把测试数据直接写在代码里,或到「神经接口」终端中使用 input()\"\n )\n return queue.pop(0)\n\n return _input\n\n\nclass _Watchdog:\n def __init__(self, deadline):\n self.deadline = deadline\n\n def __call__(self, frame, event, arg):\n if time.perf_counter() > self.deadline:\n raise SandboxTimeout()\n return self\n\n\ndef _error_payload(etype, evalue, tb):\n trimmed_tb = tb.tb_next if tb is not None else None\n text = \"\".join(traceback.format_exception(etype, evalue, trimmed_tb)).strip()\n return {\n \"type\": getattr(etype, \"__name__\", \"Error\"),\n \"message\": str(evalue),\n \"traceback\": text,\n }\n\n\ndef _truncate(text):\n \"\"\"W3: keep stdout/stderr bounded. When too long, drop the head and keep the\n tail so the most recent output (and the error) stays visible.\"\"\"\n if text and len(text) > MAX_OUTPUT:\n return \"[输出过长已截断]\\n\" + text[-MAX_OUTPUT:]\n return text\n\n\ndef _run_protected(code, namespace, stdin_lines, timeout):\n out_buf = io.StringIO()\n err_buf = io.StringIO()\n state = {\"finished\": False, \"error\": None}\n import builtins\n\n original_input = builtins.input\n old_stdout = sys.stdout\n old_stderr = sys.stderr\n\n def target():\n try:\n sys.stdout = out_buf\n sys.stderr = err_buf\n builtins.input = _make_input(stdin_lines)\n sys.settrace(_Watchdog(time.perf_counter() + timeout))\n try:\n exec(compile(code, \"<pyneon>\", \"exec\"), namespace)\n finally:\n sys.settrace(None)\n except SandboxTimeout:\n state[\"error\"] = {\n \"type\": \"Timeout\",\n \"message\": \"代码运行超过 %.1f 秒,已被强制中断(可能是死循环)\"\n % timeout,\n \"traceback\": \"\",\n }\n except InputExhausted as exc:\n state[\"error\"] = {\n \"type\": \"InputError\",\n \"message\": str(exc),\n \"traceback\": \"\",\n }\n except BaseException:\n etype, evalue, tb = sys.exc_info()\n state[\"error\"] = _error_payload(etype, evalue, tb)\n finally:\n builtins.input = original_input\n sys.stdout = old_stdout\n sys.stderr = old_stderr\n state[\"finished\"] = True\n\n started_at = time.perf_counter()\n worker = threading.Thread(target=target, daemon=True)\n worker.start()\n worker.join(timeout + 1.5)\n duration_ms = int((time.perf_counter() - started_at) * 1000)\n\n error = (\n None\n if state[\"finished\"] and state[\"error\"] is None\n else (\n state[\"error\"]\n or {\n \"type\": \"Timeout\",\n \"message\": \"代码运行超过 %.1f 秒,已被强制中断(可能是死循环)\"\n % timeout,\n \"traceback\": \"\",\n }\n )\n )\n return {\n \"stdout\": _truncate(out_buf.getvalue()),\n \"stderr\": _truncate(err_buf.getvalue()),\n \"error\": error,\n \"duration_ms\": duration_ms,\n }\n\n\ndef _snapshot(namespace):\n items = []\n for key, value in namespace.items():\n if key.startswith(\"__\"):\n continue\n if callable(value) and getattr(value, \"__module__\", None) == \"builtins\":\n continue\n try:\n if sys.getsizeof(value, 0) > MAX_VAR_BYTES:\n rendered = \"<对象过大>\"\n else:\n rendered = repr(value)\n except BaseException:\n # W2: a custom __repr__ that raises (or recurses into RecursionError)\n # must never crash the snapshot of the caller's session.\n rendered = \"<unrepresentable>\"\n if len(rendered) > MAX_REPR:\n rendered = rendered[:MAX_REPR] + \"…\"\n items.append({\"name\": key, \"type\": type(value).__name__, \"value\": rendered})\n if len(items) >= MAX_VARS:\n break\n return items\n\n\ndef run_code(code, stdin_lines=None, timeout=DEFAULT_TIMEOUT):\n namespace = {\"__name__\": \"__main__\", \"__doc__\": None}\n core = _run_protected(code, namespace, stdin_lines or [], float(timeout))\n result = dict(core)\n result[\"ok\"] = core[\"error\"] is None\n result[\"variables\"] = _snapshot(namespace)\n return result\n\n\ndef check_exercise(code, tests, stdin_lines=None):\n namespace = {\"__name__\": \"__main__\", \"__doc__\": None}\n core = _run_protected(code, namespace, stdin_lines or [], DEFAULT_TIMEOUT)\n if core[\"error\"] is not None:\n return {\n \"ok\": False,\n \"passed\": False,\n \"stdout\": core[\"stdout\"],\n \"stderr\": core[\"stderr\"],\n \"test_output\": \"\",\n \"error\": core[\"error\"],\n \"duration_ms\": core[\"duration_ms\"],\n \"variables\": [],\n }\n\n failures = []\n test_outputs = []\n for index, test in enumerate(tests, start=1):\n test_state = {\"finished\": False, \"error\": None}\n test_out = io.StringIO()\n\n def test_target(ns=namespace, source=test, st=test_state, buf=test_out):\n old_stdout = sys.stdout\n try:\n sys.stdout = buf\n sys.settrace(_Watchdog(time.perf_counter() + EXERCISE_TEST_TIMEOUT))\n try:\n exec(compile(source, \"<neon-test-%d>\" % index, \"exec\"), ns)\n finally:\n sys.settrace(None)\n except AssertionError as exc:\n st[\"error\"] = str(exc) or \"断言未通过\"\n except SandboxTimeout:\n st[\"error\"] = \"测试执行超时\"\n except BaseException:\n etype, evalue, _tb = sys.exc_info()\n st[\"error\"] = \"%s: %s\" % (getattr(etype, \"__name__\", \"Error\"), evalue)\n finally:\n sys.stdout = old_stdout\n st[\"finished\"] = True\n\n worker = threading.Thread(target=test_target, daemon=True)\n worker.start()\n worker.join(EXERCISE_TEST_TIMEOUT + 1.0)\n if not test_state[\"finished\"]:\n test_state[\"error\"] = \"测试执行超时\"\n # G3b: collect whatever the test printed (both for passing and failing\n # cases) so diagnostic print() calls inside a test are visible.\n test_outputs.append(test_out.getvalue())\n if test_state[\"error\"] is not None:\n failures.append(\n {\"index\": index, \"test\": test.strip(), \"reason\": test_state[\"error\"]}\n )\n\n passed = not failures\n message = (\n \"\"\n if passed\n else \"共 %d 个用例,%d 个未通过;首个失败:%s\"\n % (len(tests), len(failures), failures[0][\"reason\"])\n )\n return {\n \"ok\": True,\n \"passed\": passed,\n \"stdout\": core[\"stdout\"],\n \"stderr\": core[\"stderr\"],\n \"test_output\": _truncate(\"\".join(test_outputs)),\n \"error\": None\n if passed\n else {\"type\": \"TestFailed\", \"message\": message, \"traceback\": \"\"},\n \"duration_ms\": core[\"duration_ms\"],\n \"variables\": _snapshot(namespace),\n }\n\n\ndef info_json():\n return json.dumps(\n {\n \"python_version\": sys.version.split()[0],\n \"platform\": sys.platform,\n }\n )\n\n\ndef run_code_json(payload_json):\n payload = json.loads(payload_json)\n result = run_code(\n payload.get(\"code\", \"\"),\n payload.get(\"stdin\") or [],\n payload.get(\"timeout\", DEFAULT_TIMEOUT),\n )\n return json.dumps(result, ensure_ascii=False)\n\n\ndef check_exercise_json(payload_json):\n payload = json.loads(payload_json)\n result = check_exercise(\n payload.get(\"code\", \"\"),\n payload.get(\"tests\") or [],\n payload.get(\"stdin\") or [],\n )\n return json.dumps(result, ensure_ascii=False)\n";
window.REPL_HELPER_PY = "\nimport json as _json\n_REPL_NS = {\"__name__\": \"__main__\"}\n\ndef repl_exec_json(payload_json):\n \"\"\"在持久命名空间里执行一行/一段 REPL 代码,返回与 run_code 同构的结果。\"\"\"\n p = _json.loads(payload_json)\n code = p.get(\"code\", \"\")\n res = _run_protected(code, _REPL_NS, p.get(\"stdin\") or [], float(p.get(\"timeout\", DEFAULT_TIMEOUT)))\n r = dict(res)\n r[\"ok\"] = res[\"error\"] is None\n r[\"variables\"] = _snapshot(_REPL_NS)\n return _json.dumps(r, ensure_ascii=False)\n\ndef repl_reset_json(payload_json=\"\"):\n \"\"\"清空 REPL 会话命名空间(保留 __name__)。\"\"\"\n _REPL_NS.clear()\n _REPL_NS[\"__name__\"] = \"__main__\"\n return \"{}\"\n";