Skip to content

Commit 32dcb3d

Browse files
authored
Fix async_alert to respect allow_style setting for 3.x branch (#1582)
* Fix async_alert to respect allow_style setting * Update CHANGELOG
1 parent e228018 commit 32dcb3d

File tree

3 files changed

+60
-0
lines changed

3 files changed

+60
-0
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
## 3.2.1 (February TBD, 2026)
2+
3+
- Bug Fixes
4+
- The `async_alert` and `async_prompt_update` methods of `cmd2.Cmd` now respect the current
5+
value of the `allow_style` settable
6+
- If `allow_style` is `NEVER`, all ANSI escape codes will be stripped to ensure plain text
7+
output
8+
19
## 3.2.0 (February 5, 2026)
210

311
- Bug Fixes

cmd2/cmd2.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5744,6 +5744,8 @@ def async_alert(self, alert_msg: str, new_prompt: str | None = None) -> None: #
57445744
update_terminal = False
57455745

57465746
if alert_msg:
5747+
if self.allow_style == ru.AllowStyle.NEVER:
5748+
alert_msg = su.strip_style(alert_msg)
57475749
alert_msg += '\n'
57485750
update_terminal = True
57495751

tests/test_async_alert.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import unittest
2+
from unittest.mock import MagicMock, patch
3+
4+
import cmd2
5+
import cmd2.cmd2 # to patch vt100_support
6+
from cmd2 import rich_utils as ru
7+
8+
9+
class TestAsyncAlert(unittest.TestCase):
10+
def test_async_alert_strips_ansi_when_allow_style_is_never(self):
11+
app = cmd2.Cmd()
12+
13+
# Patch vt100_support to True
14+
with patch('cmd2.cmd2.vt100_support', True):
15+
# Patch threading functions
16+
mock_current_thread = MagicMock()
17+
mock_current_thread.name = "NotMainThread"
18+
19+
with (
20+
patch('threading.current_thread', return_value=mock_current_thread),
21+
patch('threading.main_thread', return_value=MagicMock()),
22+
patch('cmd2.cmd2.rl_get_display_prompt', return_value='(Cmd) '),
23+
patch('cmd2.cmd2.readline.get_line_buffer', return_value=''),
24+
patch('cmd2.cmd2.rl_get_point', return_value=0),
25+
patch('cmd2.cmd2.rl_force_redisplay'),
26+
patch('sys.stdout', new_callable=MagicMock) as mock_stdout,
27+
):
28+
# Set allow_style to NEVER
29+
app.allow_style = ru.AllowStyle.NEVER
30+
31+
# Styled message
32+
msg = "\033[31mError\033[0m"
33+
34+
# Call async_alert
35+
app.async_alert(msg)
36+
37+
# Capture calls to write
38+
# mock_stdout.write.call_args_list -> [call(str), call(str)...]
39+
# We look at all written strings
40+
written_content = "".join([call.args[0] for call in mock_stdout.write.call_args_list])
41+
42+
# Check that ANSI codes for color are NOT present
43+
if "\033[31m" in written_content:
44+
raise AssertionError(f"Found ANSI color code in output: {written_content!r}")
45+
if "Error" not in written_content:
46+
raise AssertionError(f"Message 'Error' not found in output: {written_content!r}")
47+
48+
49+
if __name__ == '__main__':
50+
unittest.main()

0 commit comments

Comments
 (0)