diff --git a/craft_cli/messages.py b/craft_cli/messages.py index 6725234..414cfc6 100644 --- a/craft_cli/messages.py +++ b/craft_cli/messages.py @@ -306,7 +306,7 @@ class _StreamContextManager: def __init__( # noqa: PLR0913 (too many arguments) self, printer: Printer, - text: str, + text: str | None, stream: TextIO | None, use_timestamp: bool, # noqa: FBT001 (boolean positional arg) ephemeral_mode: bool, # noqa: FBT001 (boolean positional arg) @@ -319,9 +319,10 @@ def __init__( # noqa: PLR0913 (too many arguments) "end_line": not ephemeral_mode, } - # show the intended text (explicitly asking for a complete line) before passing the - # output command to the pip-reading thread - printer.show(stream, text, **printer_flags) + if text is not None: + # show the intended text (explicitly asking for a complete line) before + # passing the output command to the pipe-reading thread + printer.show(stream, text, **printer_flags) # enable the thread to read and show what comes through the provided pipe self.pipe_reader = _PipeReaderThread(printer, stream, printer_flags) @@ -640,7 +641,7 @@ def progress_bar( return _Progresser(self._printer, total, text, stream, delta, use_timestamp, ephemeral) @_active_guard() - def open_stream(self, text: str) -> _StreamContextManager: + def open_stream(self, text: str | None = None) -> _StreamContextManager: """Open a stream context manager to get messages from subprocesses.""" if self._mode == EmitterMode.QUIET: # no third party stream diff --git a/tests/integration/test_messages_integration.py b/tests/integration/test_messages_integration.py index 982fa22..bcadda8 100644 --- a/tests/integration/test_messages_integration.py +++ b/tests/integration/test_messages_integration.py @@ -1512,3 +1512,37 @@ def test_secrets_integrated(capsys, logger, monkeypatch, init_emitter): expected_err=expected_err, expected_log=expected_log, ) + + +@pytest.mark.parametrize("output_is_terminal", [True]) +def test_open_stream_no_text(capsys, logger, monkeypatch, init_emitter): + """Test emitter output when open_stream() has no `text` parameter""" + monkeypatch.setattr(printer, "_get_terminal_width", lambda: 200) + + emit = Emitter() + emit.init(EmitterMode.VERBOSE, "testapp", GREETING) + + emit.progress("Begin stage", permanent=False) + + with emit.open_stream() as write_pipe: + os.write(write_pipe, b"Info message 1\n") + os.write(write_pipe, b"Info message 2\n") + + emit.progress("End stage", permanent=False) + emit.ended_ok() + + expected_err = [ + Line("Begin stage"), + Line(":: Info message 1"), + Line(":: Info message 2"), + Line("End stage"), + ] + expected_log = expected_err + + assert_outputs( + capsys, + emit, + expected_out=None, + expected_err=expected_err, + expected_log=expected_log, + ) diff --git a/tests/unit/test_messages_emitter.py b/tests/unit/test_messages_emitter.py index 7cf0093..0eb5019 100644 --- a/tests/unit/test_messages_emitter.py +++ b/tests/unit/test_messages_emitter.py @@ -579,6 +579,28 @@ def test_openstream_in_developer_modes(get_initiated_emitter, mode): ] +def test_openstream_no_text(get_initiated_emitter): + """Test open_stream() with no text parameter.""" + emitter = get_initiated_emitter(EmitterMode.VERBOSE) + + with patch("craft_cli.messages._StreamContextManager") as stream_context_manager_mock: + instantiated_cm = object() + stream_context_manager_mock.return_value = instantiated_cm + context_manager = emitter.open_stream() + + assert emitter.printer_calls == [] + assert context_manager is instantiated_cm + assert stream_context_manager_mock.mock_calls == [ + call( + emitter._printer, + None, + stream=sys.stderr, + use_timestamp=False, + ephemeral_mode=False, + ), + ] + + @pytest.mark.parametrize( "mode", [