From 3854cf5ecb9cc90f93b5cd829a42e162061f3ffb Mon Sep 17 00:00:00 2001 From: goodboy Date: Tue, 2 Jun 2026 00:41:06 -0400 Subject: [PATCH] Strip ANSI + accept `_create(...)` in devx tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two version-compat fixes for the `devx` debugger test-suite, both about matching upstream output that got more verbose w/ recent lib releases. ANSI stripping (`tests/devx/conftest.py`), - Add `ansi_strip(text)` helper + `_ansi_re` pattern (regex per https://stackoverflow.com/a/14693789). - Apply inside `in_prompt_msg()` + `assert_before()` so substring matches against REPL/traceback output stay robust to color leakage. - Motivated by py3.13's colored tracebacks + `pdbp`/pygments highlighting leaking ANSI even when `PYTHON_COLORS=0` is set in the `spawn` fixture (not every renderer in the spawned subproc honors it). - Replaces the longstanding inline TODO that linked the SO answer w/o impl'ing. trio 0.30+ `Cancelled._create(` match (`test_debugger`), - In `test_shield_pause` swap the two `"raise Cancelled._create()"` assertion patterns → `"raise Cancelled._create("` (open-paren form, no closing). - trio >=0.30 raises a multi-line `raise Cancelled._create(source=.., reason=.., source_task=..)` w/ cancel-reason metadata, so the legacy bare-`()` form no longer matches. Inline comment documents the trio-version pivot. (this commit msg was generated in some part by [`claude-code`][claude-code-gh]) [claude-code-gh]: https://github.com/anthropics/claude-code --- tests/devx/conftest.py | 31 ++++++++++++++++++++++++++----- tests/devx/test_debugger.py | 14 ++++++++++++-- 2 files changed, 38 insertions(+), 7 deletions(-) diff --git a/tests/devx/conftest.py b/tests/devx/conftest.py index 7b0d96bb..e64d38dc 100644 --- a/tests/devx/conftest.py +++ b/tests/devx/conftest.py @@ -5,6 +5,7 @@ from __future__ import annotations import platform import os +import re import signal import time from typing import ( @@ -294,6 +295,26 @@ def expect( PROMPT = r"\(Pdb\+\)" +# Strip terminal color / ANSI-VT100 escape sequences so +# substring matching against REPL + traceback output stays +# robust to color leakage — Python 3.13's colored tracebacks, +# `pdbp`'s pygments highlighting, etc. — even when +# `PYTHON_COLORS=0` (set in the `spawn` fixture) isn't honored +# by every renderer in the spawned subproc. +# Regex per https://stackoverflow.com/a/14693789 +_ansi_re: re.Pattern = re.compile( + r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])' +) + + +def ansi_strip(text: str) -> str: + ''' + Remove ANSI/VT100 escape sequences from `text`. + + ''' + return _ansi_re.sub('', text) + + def in_prompt_msg( child: SpawnBase, parts: list[str], @@ -313,7 +334,7 @@ def in_prompt_msg( ''' __tracebackhide__: bool = False - before: str = str(child.before.decode()) + before: str = ansi_strip(str(child.before.decode())) for part in parts: if part not in before: if pause_on_false: @@ -333,9 +354,9 @@ def in_prompt_msg( return True -# TODO: todo support terminal color-chars stripping so we can match -# against call stack frame output from the the 'll' command the like! -# -[ ] SO answer for stipping ANSI codes: https://stackoverflow.com/a/14693789 +# NB: color-char stripping (so we can match against call-stack +# frame output from the `ll` command and the like) is handled by +# `ansi_strip()` applied inside `in_prompt_msg()` + below. def assert_before( child: SpawnBase, patts: list[str], @@ -356,7 +377,7 @@ def assert_before( err_on_false=True, **kwargs ) - before: str = str(child.before.decode()) + before: str = ansi_strip(str(child.before.decode())) return before diff --git a/tests/devx/test_debugger.py b/tests/devx/test_debugger.py index 94515aa4..1c3338c5 100644 --- a/tests/devx/test_debugger.py +++ b/tests/devx/test_debugger.py @@ -1186,7 +1186,12 @@ def test_shield_pause( "('cancelled_before_pause'", # actor name _repl_fail_msg, "trio.Cancelled", - "raise Cancelled._create()", + # trio >=0.30 raises via a multi-line + # `raise Cancelled._create(source=.., reason=.., + # source_task=..)` (cancel-reason metadata), so + # match the open-paren form only, NOT the legacy + # bare `()`. + "raise Cancelled._create(", # we should be handling a taskc inside # the first `.port_mortem()` sin-shield! @@ -1204,7 +1209,12 @@ def test_shield_pause( "('root'", # actor name _repl_fail_msg, "trio.Cancelled", - "raise Cancelled._create()", + # trio >=0.30 raises via a multi-line + # `raise Cancelled._create(source=.., reason=.., + # source_task=..)` (cancel-reason metadata), so + # match the open-paren form only, NOT the legacy + # bare `()`. + "raise Cancelled._create(", # handling a taskc inside the first unshielded # `.port_mortem()`.