Remove stale rich-markup escapes leaking backslashes into logged warnings

Merged
MA
2mo ago
+58-19

Type of change

  • Bug fix (non-breaking change which fixes an issue)

Changes To Core Features:

  • Have you added an explanation of what your changes do and why you'd like us to include them?
  • Have you written new tests for your core changes, as applicable?
  • Have you successfully ran tests with your changes locally?

Defect

FINDING-027 from 0.9.9a1 pre-release testing (cosmetic regression vs 0.9.8, verifier-confirmed): compile-time warnings print literal backslash-escaped brackets, e.g. the event-handler arg-mismatch warning renders as

Warning: Event handler on_submit expects (dict\[str, typing.Any]) -> () but got (dict\[str, str]) -> () ...

where 0.9.8 printed clean dict[str, ...].

Root cause: 0.9.8 built these messages with rich-markup escaping (rich.markup.escape() / .replace("[", "\\[")) and emitted them via the legacy console.* helpers, whose rich Console rendered with markup enabled and consumed the \[ escapes. The 0.9.9 migration to the stdlib logging pipeline kept the escaping, but RichConsoleHandler.emit renders records with markup=False unless a record opts in via extra={"rich": True} (these call sites do not), so the escapes now print literally — in the console sink, the JSON sink, and the full-logging file.

Fix

Drop the stale escaping at every call site that logs through the pipeline without the markup opt-in (all confirmed to use console.* + escaping on the v0.9.8 tag, i.e. all the same migration regression):

  • reflex_base/event/__init__.py — the arg-mismatch warning from the finding's repro (.replace("[", "\\[") on both type lists)
  • reflex_base/components/component.py — None-to-non-Optional Var deprecation warning
  • reflex_base/vars/base.py — computed var return-type error
  • reflex_base/event/processor/event_processor.py — event-queue processing error ([txid=...])
  • reflex/state.py — inline computed var type warning and state field type error

Bracketed text now renders verbatim. Markup injection from user-controlled values (type names, event payloads) stays impossible: the sink keeps markup=False, nothing opts into markup rendering. Call sites that do opt in via extra={"rich": True} (e.g. js_runtimes.py, prerequisites.py) correctly keep their escaping and are untouched, as is the deliberate escaping of client-supplied JS error text placed into Exception objects for user-defined frontend exception handlers.

Test plan

  • New regression test tests/units/test_event.py::test_arg_mismatch_warning_renders_brackets_verbatim: reproduces the finding's scenario (handler annotated dict[str, str] bound to the on_submit specs) through the managed pipeline and asserts the captured rendered output contains expects (dict[str, typing.Any]) -> () but got (dict[str, str]) -> () with no backslashes. Fails on unfixed main with dict\[str, typing.Any] in the output; passes with the fix.
  • New injection-safety test tests/units/reflex_base/utils/test_log.py::test_warning_with_markup_tags_stays_literal: a warning containing [red]x[/red] renders it literally — neither styled/stripped nor escaped.
  • uv run ruff check . and uv run ruff format . clean; uv run pyright reflex tests passes (0 errors); pyright over reflex_base shows only a pre-existing warning unrelated to this change.
  • Scoped suites pass: tests/units/test_event.py, tests/units/reflex_base/utils/test_log.py, tests/units/reflex_base/event/processor/test_event_processor.py, tests/units/components/test_component.py, tests/units/test_state.py, tests/units/test_var.py, tests/units/vars. (One pre-existing cross-file ordering failure, test_superseding_event_logs_debug_on_cancel when test_log.py runs before test_event_processor.py in the same invocation, reproduces identically on unmodified main and is unrelated.)

🤖 Generated with Claude Code

https://claude.ai/code/session_01EMjBXPozsNeQNSBZecNH8x


Generated by Claude Code