Skip to content

Commit 123482e

Browse files
committed
Reapply "Fix NoConsoleScreenBufferError on Windows CI by robustly initializing PromptSession and ensuring consistent input/output usage"
This reverts commit f194db6.
1 parent f194db6 commit 123482e

File tree

2 files changed

+62
-18
lines changed

2 files changed

+62
-18
lines changed

cmd2/cmd2.py

Lines changed: 55 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,6 @@
6363
cast,
6464
)
6565

66-
import prompt_toolkit as pt
6766
import rich.box
6867
from rich.console import Group, RenderableType
6968
from rich.highlighter import ReprHighlighter
@@ -144,9 +143,26 @@
144143
from prompt_toolkit.completion import Completer, DummyCompleter
145144
from prompt_toolkit.formatted_text import ANSI
146145
from prompt_toolkit.history import InMemoryHistory
146+
from prompt_toolkit.input import DummyInput
147+
from prompt_toolkit.output import DummyOutput
147148
from prompt_toolkit.patch_stdout import patch_stdout
148149
from prompt_toolkit.shortcuts import PromptSession, set_title
149150

151+
try:
152+
if sys.platform == "win32":
153+
from prompt_toolkit.output.win32 import NoConsoleScreenBufferError # type: ignore[attr-defined]
154+
else:
155+
156+
class NoConsoleScreenBufferError(Exception): # type: ignore[no-redef]
157+
"""Dummy exception to use when prompt_toolkit.output.win32.NoConsoleScreenBufferError is not available."""
158+
159+
160+
except ImportError:
161+
162+
class NoConsoleScreenBufferError(Exception): # type: ignore[no-redef]
163+
"""Dummy exception to use when prompt_toolkit.output.win32.NoConsoleScreenBufferError is not available."""
164+
165+
150166
from .pt_utils import (
151167
Cmd2Completer,
152168
Cmd2History,
@@ -421,10 +437,22 @@ def __init__(
421437
# Initialize prompt-toolkit PromptSession
422438
self.history_adapter = Cmd2History(self)
423439
self.completer = Cmd2Completer(self)
424-
self.session: PromptSession[str] = PromptSession(
425-
history=self.history_adapter,
426-
completer=self.completer,
427-
)
440+
441+
try:
442+
self.session: PromptSession[str] = PromptSession(
443+
history=self.history_adapter,
444+
completer=self.completer,
445+
)
446+
except (NoConsoleScreenBufferError, AttributeError, ValueError):
447+
# Fallback to dummy input/output if PromptSession initialization fails.
448+
# This can happen in some CI environments (like GitHub Actions on Windows)
449+
# where isatty() is True but there is no real console.
450+
self.session = PromptSession(
451+
history=self.history_adapter,
452+
completer=self.completer,
453+
input=DummyInput(),
454+
output=DummyOutput(),
455+
)
428456

429457
# Commands to exclude from the history command
430458
self.exclude_from_history = ['eof', 'history']
@@ -3226,12 +3254,16 @@ def get_prompt() -> Any:
32263254
history_to_use = InMemoryHistory()
32273255
for item in history:
32283256
history_to_use.append_string(item)
3229-
from prompt_toolkit import prompt as pt_prompt
32303257

3231-
return pt_prompt(
3258+
temp_session1: PromptSession[str] = PromptSession(
3259+
history=history_to_use,
3260+
input=self.session.input,
3261+
output=self.session.output,
3262+
)
3263+
3264+
return temp_session1.prompt(
32323265
prompt_to_use,
32333266
completer=completer_to_use,
3234-
history=history_to_use,
32353267
bottom_toolbar=self._bottom_toolbar,
32363268
)
32373269

@@ -3244,13 +3276,26 @@ def get_prompt() -> Any:
32443276
# Otherwise read from self.stdin
32453277
elif self.stdin.isatty():
32463278
# on a tty, print the prompt first, then read the line
3247-
line = pt.prompt(prompt, bottom_toolbar=self._bottom_toolbar)
3279+
temp_session2: PromptSession[str] = PromptSession(
3280+
input=self.session.input,
3281+
output=self.session.output,
3282+
)
3283+
line = temp_session2.prompt(
3284+
prompt,
3285+
bottom_toolbar=self._bottom_toolbar,
3286+
)
32483287
if len(line) == 0:
32493288
raise EOFError
32503289
return line.rstrip('\n')
32513290
else:
32523291
# not a tty, just read the line
3253-
line = pt.prompt(bottom_toolbar=self._bottom_toolbar)
3292+
temp_session3: PromptSession[str] = PromptSession(
3293+
input=self.session.input,
3294+
output=self.session.output,
3295+
)
3296+
line = temp_session3.prompt(
3297+
bottom_toolbar=self._bottom_toolbar,
3298+
)
32543299
if len(line) == 0:
32553300
raise EOFError
32563301
line = line.rstrip('\n')

tests/test_cmd2.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1918,11 +1918,10 @@ def test_read_input_rawinput_true(capsys, monkeypatch) -> None:
19181918
app = cmd2.Cmd()
19191919
app.use_rawinput = True
19201920

1921-
# Mock prompt_toolkit.prompt (used when isatty=False)
1921+
# Mock PromptSession.prompt (used when isatty=False)
19221922
# and app.session.prompt (used when use_rawinput=True and isatty=True)
19231923
with (
1924-
mock.patch('prompt_toolkit.prompt', return_value=input_str),
1925-
mock.patch.object(app.session, 'prompt', return_value=input_str),
1924+
mock.patch('cmd2.cmd2.PromptSession.prompt', return_value=input_str),
19261925
):
19271926
# isatty is True
19281927
with mock.patch('sys.stdin.isatty', mock.MagicMock(name='isatty', return_value=True)):
@@ -2000,22 +1999,22 @@ def mock_pt_prompt(message='', **kwargs):
20001999

20012000
# isatty True
20022001
app = make_app(isatty=True)
2003-
with mock.patch('prompt_toolkit.prompt', side_effect=mock_pt_prompt):
2002+
with mock.patch('cmd2.cmd2.PromptSession.prompt', side_effect=mock_pt_prompt):
20042003
line = app.read_input(prompt_str)
20052004
out, _err = capsys.readouterr()
20062005
assert line == input_str
20072006
assert out == prompt_str
20082007

20092008
# isatty True, empty input
20102009
app = make_app(isatty=True, empty_input=True)
2011-
with mock.patch('prompt_toolkit.prompt', return_value=''), pytest.raises(EOFError):
2010+
with mock.patch('cmd2.cmd2.PromptSession.prompt', return_value=''), pytest.raises(EOFError):
20122011
app.read_input(prompt_str)
20132012
out, _err = capsys.readouterr()
20142013

20152014
# isatty is False, echo is True
20162015
app = make_app(isatty=False)
20172016
app.echo = True
2018-
with mock.patch('prompt_toolkit.prompt', return_value=input_str):
2017+
with mock.patch('cmd2.cmd2.PromptSession.prompt', return_value=input_str):
20192018
line = app.read_input(prompt_str)
20202019
out, _err = capsys.readouterr()
20212020
assert line == input_str
@@ -2024,15 +2023,15 @@ def mock_pt_prompt(message='', **kwargs):
20242023
# isatty is False, echo is False
20252024
app = make_app(isatty=False)
20262025
app.echo = False
2027-
with mock.patch('prompt_toolkit.prompt', return_value=input_str):
2026+
with mock.patch('cmd2.cmd2.PromptSession.prompt', return_value=input_str):
20282027
line = app.read_input(prompt_str)
20292028
out, _err = capsys.readouterr()
20302029
assert line == input_str
20312030
assert not out
20322031

20332032
# isatty is False, empty input
20342033
app = make_app(isatty=False, empty_input=True)
2035-
with mock.patch('prompt_toolkit.prompt', return_value=''), pytest.raises(EOFError):
2034+
with mock.patch('cmd2.cmd2.PromptSession.prompt', return_value=''), pytest.raises(EOFError):
20362035
app.read_input(prompt_str)
20372036
out, _err = capsys.readouterr()
20382037

0 commit comments

Comments
 (0)