Skip to content

fix(pybind): let Ctrl-C interrupt a running query - #66

Open
zahariash wants to merge 1 commit into
LadybugDB:mainfrom
zahariash:fix/keyboard-interrupt-stops-query
Open

zahariash wants to merge 1 commit into
LadybugDB:mainfrom
zahariash:fix/keyboard-interrupt-stops-query

Conversation

@zahariash

Copy link
Copy Markdown
Contributor

This work was produced with the help of language models.

Python runs its SIGINT handler only between bytecodes, so while the main thread waited in the engine for query(), execute() or query_as_arrow(), Ctrl-C took effect only after the query finished: a 15 s query ran to the end before KeyboardInterrupt appeared.

Fix. During such a wait on the main thread, a scoped SIGINT handler interrupts the connection's query and calls Python's own handler; once the call returns, the pending signal becomes KeyboardInterrupt instead of the engine's Interrupted. error. It is installed only while Python's default handler is active, so custom handlers are untouched. A Ctrl-C during compile is dropped by the engine; a second one stops the query.

Known limitation: the engine interrupts a connection, not a query. If the main thread's call waits behind another thread's query on the same connection, Ctrl-C stops that query instead, and the main thread's query runs before KeyboardInterrupt is raised. Pybind backend on POSIX only; the C-API backend (ctypes) is not covered.

Tests. Three tests send SIGINT one second into a long query (query(), parameterized execute(), query_as_arrow()) and expect KeyboardInterrupt within seconds and a usable connection afterwards; they fail on main, pass here on pybind and are xfailed on C-API. A custom-handler test checks the query still finishes and the handler runs. Full suites: no new failures.

Fixes #62

Python runs its SIGINT handler only between bytecodes. While the main thread
waited in the engine for query(), execute() or query_as_arrow(), Ctrl-C
therefore took effect only after the query had finished: a 15 s query ran to
the end before KeyboardInterrupt appeared.

During such a wait on the main thread, a scoped SIGINT handler now
interrupts the connection's query and then calls Python's own handler. Once
the call returns, the pending signal is raised as KeyboardInterrupt instead
of the engine's "Interrupted." error, and the connection stays usable. The
handler is installed only while Python's default handler (KeyboardInterrupt)
is active, so a custom SIGINT handler keeps its behaviour, and only on POSIX
systems. That check goes through the driver's import cache and never fails
a query, e.g. one run during interpreter shutdown. A Ctrl-C that lands while the query is still compiling is dropped
by the engine, as any interrupt is; a second Ctrl-C stops the query. The
engine interrupts a connection, not a query: if the main thread's call is
waiting behind another thread's query on the same connection, Ctrl-C stops
that query instead, and the main thread's query runs before
KeyboardInterrupt is raised.

The new tests send SIGINT one second into a long query through query(),
execute() with parameters and query_as_arrow(), and expect KeyboardInterrupt
within seconds; they fail on main. A custom handler test checks that the
query then still finishes and the handler runs. The C-API backend calls the
engine through ctypes and is not covered, so the interrupt tests are listed
in capi_xfails.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

Ctrl-C does not stop a running query

1 participant