From 78c9b635e9228a4ff16a520d86342f154299b48d Mon Sep 17 00:00:00 2001 From: Emil Simeonov Date: Sat, 20 Jun 2026 23:30:36 +0200 Subject: [PATCH] fix: catch TimeoutExpired in parallel runner wait loop The initial wait loop used proc.wait(timeout=0.5) which could raise subprocess.TimeoutExpired and crash the runner. Added a try/except and increased timeout to 5s so the runner polls correctly without crashing. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- scripts/run_molecule_parallel.py | 5 +++- tests/unit/test_run_molecule_parallel.py | 34 ++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/scripts/run_molecule_parallel.py b/scripts/run_molecule_parallel.py index 645530b..9cd3228 100644 --- a/scripts/run_molecule_parallel.py +++ b/scripts/run_molecule_parallel.py @@ -106,7 +106,10 @@ def cli(pairs: tuple[str, ...]) -> None: if not finished_indices: # No process finished yet, wait a bit for proc in processes: - proc.wait(timeout=0.5) + try: + proc.wait(timeout=5) + except subprocess.TimeoutExpired: + pass continue # Remove finished processes from the list diff --git a/tests/unit/test_run_molecule_parallel.py b/tests/unit/test_run_molecule_parallel.py index c9a2fc1..e88f298 100644 --- a/tests/unit/test_run_molecule_parallel.py +++ b/tests/unit/test_run_molecule_parallel.py @@ -147,6 +147,40 @@ class TestCli: assert result.exit_code == 1 assert "FAILURE" in result.output + def test_wait_loop_handles_timeout(self, tmp_path: Path) -> None: + from click.testing import CliRunner + + with ( + patch("scripts.run_molecule_parallel.run_pair") as mock_run_pair, + patch("os.killpg") as mock_killpg, + patch("os.getpgid") as mock_getpgid, + ): + mock_getpgid.return_value = 123 + + good_proc = MagicMock() + good_proc.poll.return_value = None + good_proc.wait.side_effect = [ + subprocess.TimeoutExpired("cmd", 5), + None, + None, + ] + bad_proc = MagicMock() + bad_proc.poll.side_effect = [None, 1, 1, 1] + bad_proc.wait.return_value = None + mock_run_pair.side_effect = [bad_proc, good_proc] + + runner = CliRunner() + result = runner.invoke( + cli, + [ + "default|ubuntu-2204|img:latest|", + "lifecycle|debian-12|img:deb|", + ], + ) + assert result.exit_code == 1 + assert "FAILURE" in result.output + mock_killpg.assert_called() + def test_main_module_block() -> None: import scripts.run_molecule_parallel as rm