From e23996f944dcf1383526511c0e6b42fb69321366 Mon Sep 17 00:00:00 2001 From: Pigbibi <20649888+Pigbibi@users.noreply.github.com> Date: Wed, 9 Sep 2026 00:00:19 +0800 Subject: [PATCH] fix: sanitize and shorten cycle failure notifications Co-Authored-By: Codex --- main.py | 9 ++++----- tests/test_runtime_notification_i18n.py | 24 ++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/main.py b/main.py index 637c604..1616607 100644 --- a/main.py +++ b/main.py @@ -7,7 +7,6 @@ import re import threading import time -import traceback from datetime import datetime from zoneinfo import ZoneInfo @@ -1714,19 +1713,19 @@ def _handle_request( append_runtime_report_error( report, stage="strategy_cycle", - message=str(exc), + message="strategy_cycle_failed", error_type=type(exc).__name__, ) finalize_runtime_report(report, status="error") log_runtime_event( log_context, "strategy_cycle_failed", - message="Strategy execution failed", + message=t("runtime_failure_log"), severity="ERROR", error_type=type(exc).__name__, - error_message=str(exc), + error_message="strategy_cycle_failed", ) - error_msg = f"{t('error_title')}\n{traceback.format_exc()}" + error_msg = _runtime_error_notification_message(exc) _publish_runtime_failure_notification( detailed_text=error_msg, compact_text=error_msg, diff --git a/tests/test_runtime_notification_i18n.py b/tests/test_runtime_notification_i18n.py index 7002715..9076a77 100644 --- a/tests/test_runtime_notification_i18n.py +++ b/tests/test_runtime_notification_i18n.py @@ -40,3 +40,27 @@ def test_startup_alert_is_compact_localized_and_does_not_embed_provider_text(loc assert ("未正常结束" in message) is locale.startswith("zh") assert ("did not finish successfully" in message) is (locale == "en") assert "test_strategy" in message or "Test strategy" in message + + +@pytest.mark.parametrize("handler_name", ['_handle_request']) +def test_actual_cycle_exception_handler_emits_no_provider_text(handler_name): + source = (Path(__file__).resolve().parents[1] / "main.py").read_text() + function = next(n for n in ast.parse(source).body if isinstance(n, ast.FunctionDef) and n.name == handler_name) + handler = next(n for n in ast.walk(function) if isinstance(n, ast.ExceptHandler) and isinstance(n.type, ast.Name) and n.type.id == "Exception" and n.name == "exc" and any(isinstance(c, ast.Call) and isinstance(c.func, ast.Name) and c.func.id == "append_runtime_report_error" for c in ast.walk(n))) + emitted = [] + def record(*args, **kwargs): + # Exception objects themselves remain private inputs, never rendered outputs. + emitted.append((args, {k: v for k, v in kwargs.items() if k != "exc"})) + namespace = dict(exc=RuntimeError("PRIVATE_CYCLE_SENTINEL"), report={}, log_context=object(), + composer=SimpleNamespace(build_notification_adapters=lambda: SimpleNamespace(publish_cycle_notification=record)), + append_runtime_report_error=record, finalize_runtime_report=record, log_runtime_event=record, + _publish_runtime_failure_notification=record, + _runtime_error_notification_message=runtime_function("_runtime_error_notification_message"), + t=build_translator("zh-CN"), traceback=SimpleNamespace(format_exc=lambda: "Traceback PRIVATE_CYCLE_SENTINEL")) + body = ast.parse("def exercise():\n pass").body[0] + body.body = handler.body + exec(compile(ast.fix_missing_locations(ast.Module(body=[body], type_ignores=[])), "main.py", "exec"), namespace) + assert namespace["exercise"]() == ("Error", 500) + assert "PRIVATE_CYCLE_SENTINEL" not in repr(emitted) + assert "Traceback" not in repr(emitted) + assert "RuntimeError" in repr(emitted)