Skip to content

Commit 7828d37

Browse files
committed
fix(test): cancel after child prints ready, not at proc registration
The old wait loop polled _live_procs, which is populated the moment Popen() returns — before the child interpreter finishes site init. On CI the SIGINT then landed during interpreter startup and the child died with a KeyboardInterrupt traceback (exit 1) instead of the handler's exit 130. Cancel from the on_line callback at the child's own "ready" marker instead: the handler is guaranteed installed by then.
1 parent 971d88a commit 7828d37

1 file changed

Lines changed: 11 additions & 9 deletions

File tree

‎tests/test_runner.py‎

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ def test_budget_flags_forwarded(self, tmp_path) -> None:
131131

132132
class TestCancel:
133133
def test_cancel_delivered_to_live_process(self, tmp_path) -> None:
134-
"""SIGINT reaches the harness; the process actually exits."""
134+
"""SIGINT reaches the harness once it is up; the process exits."""
135135
script = tmp_path / "sigint_harness.py"
136136
script.write_text(
137137
textwrap.dedent(
@@ -147,23 +147,25 @@ def test_cancel_delivered_to_live_process(self, tmp_path) -> None:
147147
runner = LocalRunner()
148148
runner._harness.cmd = f"{sys.executable} {script}"
149149
sandbox = runner.create("u", "c")
150-
started = threading.Event()
151150
result_box: dict = {}
152151

153152
def _run() -> None:
154-
started.set() # wait until child prints "ready" instead
155-
result = runner.exec_run(sandbox, "p", "r", timeout=15)
153+
result = runner.exec_run(sandbox, "p", "r", on_line=_on_line, timeout=15)
156154
result_box["result"] = result
157155

158-
# start exec on a thread, cancel once the child is up
156+
def _on_line(line: str) -> None:
157+
# The child's own SIGINT handler is installed before it prints
158+
# "ready", so cancelling here cannot race interpreter startup
159+
# (an early SIGINT would kill the raw interpreter with a
160+
# KeyboardInterrupt traceback instead of exit code 130).
161+
if line == "ready":
162+
result_box["cancel_ok"] = runner.cancel(sandbox, "r")
163+
159164
thread = threading.Thread(target=_run, daemon=True)
160165
thread.start()
161-
deadline = time.time() + 10
162-
while time.time() < deadline and "r" not in runner._live_procs:
163-
time.sleep(0.02)
164-
assert runner.cancel(sandbox, "r") is True
165166
thread.join(timeout=15)
166167
result = result_box["result"]
168+
assert result_box.get("cancel_ok") is True
167169
assert result.exit_code == 130
168170
assert "cancelled" in result.stdout
169171

0 commit comments

Comments
 (0)