Skip to content

Commit 9c50259

Browse files
authored
Merge branch 'main' into pt-choice
2 parents 589914e + e5d2de7 commit 9c50259

File tree

2 files changed

+33
-9
lines changed

2 files changed

+33
-9
lines changed

cmd2/cmd2.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -680,8 +680,9 @@ def _should_continue_multiline(self) -> bool:
680680
def _create_main_session(self, auto_suggest: bool, completekey: str) -> PromptSession[str]:
681681
"""Create and return the main PromptSession for the application.
682682
683-
Builds an interactive session if stdin is a TTY. Otherwise, uses
684-
dummy drivers to support non-interactive streams like pipes or files.
683+
Builds an interactive session if self.stdin and self.stdout are TTYs.
684+
Otherwise, uses dummy drivers to support non-interactive streams like
685+
pipes or files.
685686
"""
686687
key_bindings = None
687688
if completekey != self.DEFAULT_COMPLETEKEY:
@@ -713,7 +714,7 @@ def _(event: Any) -> None: # pragma: no cover
713714
"rprompt": self.get_rprompt,
714715
}
715716

716-
if self.stdin.isatty():
717+
if self.stdin.isatty() and self.stdout.isatty():
717718
try:
718719
if self.stdin != sys.stdin:
719720
kwargs["input"] = create_input(stdin=self.stdin)
@@ -3245,7 +3246,8 @@ def _is_tty_session(session: PromptSession[str]) -> bool:
32453246
"""
32463247
# Validate against the session's assigned input driver rather than sys.stdin.
32473248
# This respects the fallback logic in _create_main_session() and allows unit
3248-
# tests to inject PipeInput for programmatic interaction.
3249+
# tests to inject PipeInput for programmatic interaction even if paired with
3250+
# a DummyOutput.
32493251
return not isinstance(session.input, DummyInput)
32503252

32513253
def _read_raw_input(

tests/test_cmd2.py

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3919,10 +3919,16 @@ def test_create_main_session_exception(monkeypatch):
39193919
mock_session = mock.MagicMock(side_effect=[ValueError, valid_session_mock])
39203920
monkeypatch.setattr("cmd2.cmd2.PromptSession", mock_session)
39213921

3922-
cmd2.Cmd()
3922+
# Mock isatty to ensure we enter the try block
3923+
with (
3924+
mock.patch('sys.stdin.isatty', return_value=True),
3925+
mock.patch('sys.stdout.isatty', return_value=True),
3926+
):
3927+
cmd2.Cmd()
39233928

39243929
# Check that fallback to DummyInput/Output happened
39253930
assert mock_session.call_count == 2
3931+
39263932
# Check args of second call
39273933
call_args = mock_session.call_args_list[1]
39283934
kwargs = call_args[1]
@@ -4008,7 +4014,12 @@ def test_create_main_session_no_console_error(monkeypatch):
40084014
mock_session = mock.MagicMock(side_effect=[NoConsoleScreenBufferError, valid_session_mock])
40094015
monkeypatch.setattr("cmd2.cmd2.PromptSession", mock_session)
40104016

4011-
cmd2.Cmd()
4017+
# Mock isatty to ensure we enter the try block
4018+
with (
4019+
mock.patch('sys.stdin.isatty', return_value=True),
4020+
mock.patch('sys.stdout.isatty', return_value=True),
4021+
):
4022+
cmd2.Cmd()
40124023

40134024
# Check that fallback to DummyInput/Output happened
40144025
assert mock_session.call_count == 2
@@ -4026,8 +4037,9 @@ def test_create_main_session_with_custom_tty() -> None:
40264037
custom_stdin.isatty.return_value = True
40274038
assert custom_stdin is not sys.stdin
40284039

4029-
# Create a mock stdout which is not sys.stdout
4040+
# Create a mock stdout with says it's a TTY
40304041
custom_stdout = mock.MagicMock(spec=io.TextIOWrapper)
4042+
custom_stdout.isatty.return_value = True
40314043
assert custom_stdout is not sys.stdout
40324044

40334045
# Check if the streams were wrapped
@@ -4044,8 +4056,8 @@ def test_create_main_session_with_custom_tty() -> None:
40444056
mock_create_output.assert_called_once_with(stdout=custom_stdout)
40454057

40464058

4047-
def test_create_main_session_non_interactive() -> None:
4048-
# Set up a mock for a non-TTY stream (like a pipe)
4059+
def test_create_main_session_stdin_non_tty() -> None:
4060+
# Set up a mock for a non-TTY stdin stream
40494061
mock_stdin = mock.MagicMock(spec=io.TextIOWrapper)
40504062
mock_stdin.isatty.return_value = False
40514063

@@ -4054,6 +4066,16 @@ def test_create_main_session_non_interactive() -> None:
40544066
assert isinstance(app.main_session.output, DummyOutput)
40554067

40564068

4069+
def test_create_main_session_stdout_non_tty() -> None:
4070+
# Set up a mock for a non-TTY stdout stream
4071+
mock_stdout = mock.MagicMock(spec=io.TextIOWrapper)
4072+
mock_stdout.isatty.return_value = False
4073+
4074+
app = cmd2.Cmd(stdout=mock_stdout)
4075+
assert isinstance(app.main_session.input, DummyInput)
4076+
assert isinstance(app.main_session.output, DummyOutput)
4077+
4078+
40574079
def test_no_console_screen_buffer_error_dummy():
40584080
from cmd2.cmd2 import NoConsoleScreenBufferError
40594081

0 commit comments

Comments
 (0)