From 82b1edb362137b7415818effca32fd126e9fdf69 Mon Sep 17 00:00:00 2001 From: hborcher Date: Thu, 18 Jun 2026 08:32:25 -0500 Subject: [PATCH 1/2] fix: broken pipe when running speedwagon info no longer exits with an error --- speedwagon/startup.py | 11 ++++++++--- tests/test_startup.py | 17 +++++++++++++++++ 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/speedwagon/startup.py b/speedwagon/startup.py index 9d4425928..b0c31517b 100644 --- a/speedwagon/startup.py +++ b/speedwagon/startup.py @@ -288,13 +288,18 @@ def __init__(self, args: argparse.Namespace) -> None: report_format=self.args.report_format ) ) - self.exit_strategy: Callable[[], None] = lambda: sys.exit(0) + self.exit_strategy: Callable[[int], None] = sys.exit def run(self) -> None: """Build a system info report and write it to stdout.""" report = self.build_report() - logger.info(report) - self.exit_strategy() + try: + logger.info(report) + sys.stdout.flush() + except BrokenPipeError: + print("Broken pipe") + finally: + self.exit_strategy(0) def build_report(self) -> str: """Build a system info report as a string. diff --git a/tests/test_startup.py b/tests/test_startup.py index 854220e17..b0a8eca35 100644 --- a/tests/test_startup.py +++ b/tests/test_startup.py @@ -312,6 +312,23 @@ def test_build_report_runs_report_builder_strategy(self): command.build_report() mock_report_builder_strategy.assert_called_once() + def test_run_calls_exit_strategy(self): + command = speedwagon.startup.InfoCommand(Mock(spec=argparse.Namespace)) + command.exit_strategy = Mock(name="exit_strategy") + command.build_report = Mock(return_value="some logging") + command.run() + command.exit_strategy.assert_called_once_with(0) + + def test_run_calls_exit_strategy_with_broken_pipe(self, monkeypatch): + command = speedwagon.startup.InfoCommand(Mock(spec=argparse.Namespace)) + command.exit_strategy = Mock(name="exit_strategy") + command.build_report = Mock(return_value="some logging") + info = Mock(side_effect=BrokenPipeError) + monkeypatch.setattr(speedwagon.startup.logger, "info", info) + command.run() + info.assert_called_once() + command.exit_strategy.assert_called_once_with(0) + def test_get_global_options(): resolution_order = [ Mock(spec_set=speedwagon.config.config.AbsSetting, update=Mock()), From 4d40c3290d56a14c09f92be3f0974b65bec36993 Mon Sep 17 00:00:00 2001 From: hborcher Date: Thu, 18 Jun 2026 09:34:56 -0500 Subject: [PATCH 2/2] fix: fixed typehint for WindowsOpenSettings --- speedwagon/frontend/qtwidgets/dialog/settings.py | 16 +++++++++++++--- tests/frontend/test_qt_dialogs.py | 5 +---- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/speedwagon/frontend/qtwidgets/dialog/settings.py b/speedwagon/frontend/qtwidgets/dialog/settings.py index 05c0cdc95..f0ad52652 100644 --- a/speedwagon/frontend/qtwidgets/dialog/settings.py +++ b/speedwagon/frontend/qtwidgets/dialog/settings.py @@ -3,6 +3,7 @@ import abc import logging import os +import sys import platform # This is fine! It's only used to open a settings directory on a mac. @@ -216,12 +217,21 @@ def system_open_directory(self, settings_directory: str) -> None: class WindowsOpenSettings(AbsOpenSettings): + + def __init__(self, settings_directory: str) -> None: + super().__init__(settings_directory) + if sys.platform == "win32": + self.startfile = os.startfile + else: + self.startfile =\ + lambda _: logger.error( + "os.startfile is for the Windows platform only" + ) + def system_open_directory(self, settings_directory: str) -> None: self.validate_user_option(settings_directory) # pylint: disable=no-member - os.startfile( - settings_directory - ) # type: ignore[attr-defined] # nosec: B606 + self.startfile(settings_directory) DEFAULT_SETTINGS_DIR_STRATEGIES: Dict[str, Type[AbsOpenSettings]] = { diff --git a/tests/frontend/test_qt_dialogs.py b/tests/frontend/test_qt_dialogs.py index f09d94ef1..02aeaafac 100644 --- a/tests/frontend/test_qt_dialogs.py +++ b/tests/frontend/test_qt_dialogs.py @@ -45,10 +45,7 @@ def test_open_windows_settings(self, monkeypatch): opening_strategy.validate_user_option = lambda _: None import os startfile = Mock() - if platform.system() != "Windows": - setattr(os, "startfile", startfile) - else: - monkeypatch.setattr(os, "startfile", startfile) + opening_strategy.startfile = startfile opening_strategy.open() assert startfile.called is True