Strip ANSI + accept `_create(...)` in devx tests
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-codecustom_log_levels
parent
3a45dbd503
commit
3854cf5ecb
|
|
@ -5,6 +5,7 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
import platform
|
import platform
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
import signal
|
import signal
|
||||||
import time
|
import time
|
||||||
from typing import (
|
from typing import (
|
||||||
|
|
@ -294,6 +295,26 @@ def expect(
|
||||||
PROMPT = r"\(Pdb\+\)"
|
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(
|
def in_prompt_msg(
|
||||||
child: SpawnBase,
|
child: SpawnBase,
|
||||||
parts: list[str],
|
parts: list[str],
|
||||||
|
|
@ -313,7 +334,7 @@ def in_prompt_msg(
|
||||||
'''
|
'''
|
||||||
__tracebackhide__: bool = False
|
__tracebackhide__: bool = False
|
||||||
|
|
||||||
before: str = str(child.before.decode())
|
before: str = ansi_strip(str(child.before.decode()))
|
||||||
for part in parts:
|
for part in parts:
|
||||||
if part not in before:
|
if part not in before:
|
||||||
if pause_on_false:
|
if pause_on_false:
|
||||||
|
|
@ -333,9 +354,9 @@ def in_prompt_msg(
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
# TODO: todo support terminal color-chars stripping so we can match
|
# NB: color-char stripping (so we can match against call-stack
|
||||||
# against call stack frame output from the the 'll' command the like!
|
# frame output from the `ll` command and the like) is handled by
|
||||||
# -[ ] SO answer for stipping ANSI codes: https://stackoverflow.com/a/14693789
|
# `ansi_strip()` applied inside `in_prompt_msg()` + below.
|
||||||
def assert_before(
|
def assert_before(
|
||||||
child: SpawnBase,
|
child: SpawnBase,
|
||||||
patts: list[str],
|
patts: list[str],
|
||||||
|
|
@ -356,7 +377,7 @@ def assert_before(
|
||||||
err_on_false=True,
|
err_on_false=True,
|
||||||
**kwargs
|
**kwargs
|
||||||
)
|
)
|
||||||
before: str = str(child.before.decode())
|
before: str = ansi_strip(str(child.before.decode()))
|
||||||
return before
|
return before
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1186,7 +1186,12 @@ def test_shield_pause(
|
||||||
"('cancelled_before_pause'", # actor name
|
"('cancelled_before_pause'", # actor name
|
||||||
_repl_fail_msg,
|
_repl_fail_msg,
|
||||||
"trio.Cancelled",
|
"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
|
# we should be handling a taskc inside
|
||||||
# the first `.port_mortem()` sin-shield!
|
# the first `.port_mortem()` sin-shield!
|
||||||
|
|
@ -1204,7 +1209,12 @@ def test_shield_pause(
|
||||||
"('root'", # actor name
|
"('root'", # actor name
|
||||||
_repl_fail_msg,
|
_repl_fail_msg,
|
||||||
"trio.Cancelled",
|
"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
|
# handling a taskc inside the first unshielded
|
||||||
# `.port_mortem()`.
|
# `.port_mortem()`.
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue