diff --git a/tests/devx/conftest.py b/tests/devx/conftest.py index eb56d74c..232edd78 100644 --- a/tests/devx/conftest.py +++ b/tests/devx/conftest.py @@ -4,6 +4,7 @@ ''' from __future__ import annotations import platform +import re import signal import time from typing import ( @@ -216,6 +217,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], @@ -235,7 +256,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: @@ -255,9 +276,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], @@ -275,7 +296,8 @@ def assert_before( err_on_false=True, **kwargs ) - return str(child.before.decode()) + before: str = ansi_strip(str(child.before.decode())) + return before def do_ctlc( diff --git a/tests/devx/test_debugger.py b/tests/devx/test_debugger.py index d5fd759b..3c589bb2 100644 --- a/tests/devx/test_debugger.py +++ b/tests/devx/test_debugger.py @@ -1144,7 +1144,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! @@ -1162,7 +1167,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()`.