Skip to content

Commit 9843f3b

Browse files
rustyconoverclaude
andcommitted
Handle KeyboardInterrupt cleanly in workers
Add top-level KeyboardInterrupt handler to Worker.run() that: - Logs "worker_interrupted" at debug level - Exits with code 130 (128 + SIGINT signal number) - Avoids printing a traceback This provides a cleaner user experience when pressing Ctrl+C instead of showing a full Python traceback. Also fix line-length lint errors in table_function.py and table_in_out_function.py for protocol state metadata construction. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 42872fa commit 9843f3b

3 files changed

Lines changed: 18 additions & 10 deletions

File tree

vgi/table_function.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -149,9 +149,7 @@ class ProtocolOutput:
149149
batch: pa.RecordBatch | None
150150
log_message: vgi.log.Message | None = None
151151

152-
def metadata(
153-
self, invocation: vgi.invocation.Invocation
154-
) -> pa.KeyValueMetadata:
152+
def metadata(self, invocation: vgi.invocation.Invocation) -> pa.KeyValueMetadata:
155153
"""Create metadata for this output based on the status.
156154
157155
Args:
@@ -164,9 +162,9 @@ def metadata(
164162
165163
"""
166164
# Start with protocol state (required for VGI protocol)
167-
metadata_dict: dict[bytes, bytes] = {
168-
vgi.ipc_utils.PROTOCOL_STATE_KEY: vgi.ipc_utils.ProtocolState.OUTPUT.encode()
169-
}
165+
protocol_key = vgi.ipc_utils.PROTOCOL_STATE_KEY
166+
protocol_state = vgi.ipc_utils.ProtocolState.OUTPUT.encode()
167+
metadata_dict: dict[bytes, bytes] = {protocol_key: protocol_state}
170168

171169
# Add log message metadata if present
172170
if self.log_message is not None:

vgi/table_in_out_function.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -159,9 +159,7 @@ class ProtocolOutput:
159159
status: _OutputStatus
160160
log_message: vgi.log.Message | None = None
161161

162-
def metadata(
163-
self, invocation: vgi.invocation.Invocation
164-
) -> pa.KeyValueMetadata:
162+
def metadata(self, invocation: vgi.invocation.Invocation) -> pa.KeyValueMetadata:
165163
"""Create metadata for this output based on the status.
166164
167165
Args:
@@ -174,8 +172,10 @@ def metadata(
174172
175173
"""
176174
# Start with protocol state (required for VGI protocol) and status
175+
protocol_key = vgi.ipc_utils.PROTOCOL_STATE_KEY
176+
protocol_state = vgi.ipc_utils.ProtocolState.OUTPUT.encode()
177177
metadata_dict: dict[bytes, bytes] = {
178-
vgi.ipc_utils.PROTOCOL_STATE_KEY: vgi.ipc_utils.ProtocolState.OUTPUT.encode(),
178+
protocol_key: protocol_state,
179179
b"vgi.status": self.status.value.encode(),
180180
}
181181

vgi/worker.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1432,6 +1432,16 @@ def run(self) -> None:
14321432

14331433
tracer = tracing.get_tracer("vgi.worker")
14341434

1435+
try:
1436+
self._run_loop(tracer)
1437+
except KeyboardInterrupt:
1438+
# Exit cleanly on Ctrl+C without traceback
1439+
# Exit code 130 = 128 + SIGINT(2), conventional for interrupted processes
1440+
self.log.debug("worker_interrupted")
1441+
sys.exit(130)
1442+
1443+
def _run_loop(self, tracer: Any) -> None:
1444+
"""Run the main worker loop for processing invocations."""
14351445
try:
14361446
while True:
14371447
self.log.info("waiting_for_invocation")

0 commit comments

Comments
 (0)