Skip to content

Install the default SIGINT handler in the self-SIGINT interrupt test - #93

Merged
tae898 merged 1 commit into
mainfrom
test/sigint-default-handler
Oct 26, 2026
Merged

tae898 merged 1 commit into
mainfrom
test/sigint-default-handler

Conversation

@tae898

@tae898 tae898 commented Oct 26, 2026

Copy link
Copy Markdown
Collaborator

tests/test_python_function_interrupt.py::test_sigint_during_a_query_that_calls_python_is_a_keyboard_interrupt failed every time (assert 0 >= 1, 20 of 20) on a runner that starts pytest as a background job, on both the 26.10.2 and the 26.11.1-SNAPSHOT native engine, and passed in CI. The binding is fine; the test relied on a handler Python does not always install.

Cause: Python installs signal.default_int_handler at startup only when SIGINT is not ignored. A shell runs an asynchronous command (cmd &, no job control) with SIGINT set to SIG_IGN, and the setting survives exec, so the pytest process starts with getsignal(SIGINT) == SIG_IGN. os.kill(os.getpid(), SIGINT) is then dropped, no query is interrupted, and the test runs the full 22 to 25 s of eight uninterrupted queries before it fails.

Evidence, same wheel and test, one launch path changed at a time:

launch of the pytest process getsignal(SIGINT) in the test result
inherited from the agent shell, under quiet_build.sh (systemd scope, taskset, nice) default_int_handler passed in 2.3 s
bash -c "... & wait" SIG_IGN (SigIgn bit 0x2 set in /proc/self/status) failed in 24.7 s
same & launch, default_int_handler installed first default_int_handler passed in 2.2 s

So the scope, nice and taskset layers do not matter, and the native default-handler path (_dispatch entry, interrupt note, error reply, KeyboardInterrupt) works: the test passes there with the handler in place. test_sigint_loop_in_a_fresh_process_ends_cleanly and test_sigint.py already install the handler for their child processes for the same reason.

Fix: a default_sigint_handler fixture installs signal.default_int_handler for the test and restores the old handler. tests/README.md gets a short section on how to check a runner (grep SigIgn /proc/self/status, bit 0x2).

Checked on the laptop with the final file, pytest started as an & job (SIGINT ignored), tests/test_python_function_interrupt.py whole:

  • before: 0 of 20 runs passed the test on 26.11.1-SNAPSHOT and 0 of 20 on 26.10.2
  • after: 26 of 26 runs of the file pass (3 passed, 2 skipped each) on 26.11.1-SNAPSHOT and 26 of 26 on 26.10.2 (20 + 6 per wheel; the 6 are the final file after formatting)

scripts/check_no_embedded_name.py --root . is clean and black reports nothing on the changed lines.

🤖 Generated with Claude Code

test_sigint_during_a_query_that_calls_python_is_a_keyboard_interrupt sends SIGINT to its own process and needs Python's default_int_handler. Python installs that handler at startup only when SIGINT is not ignored, and a pytest started as a background job (a shell `cmd &` without job control, many launchers and agent shells) inherits SIGINT as ignored, so the signal was dropped, no query was interrupted and the test failed every time (20 of 20 on the laptop, on both the 26.10.2 and the 26.11.1-SNAPSHOT engine) while it passed in CI. The test now installs signal.default_int_handler for its duration and restores the old handler. tests/README.md says how to check a runner.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
@tae898
tae898 merged commit 179ac69 into main Oct 26, 2026
20 checks passed
@tae898
tae898 deleted the test/sigint-default-handler branch October 26, 2026 08:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant