Skip to content

Commit 39b2c1a

Browse files
committed
Fix interrupt handling for release
1 parent 63d529a commit 39b2c1a

2 files changed

Lines changed: 54 additions & 11 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828
- Reduced false positives in document dependency analysis for nested scopes and comprehension variables.
2929
- Improved engine/runtime behavior around worker management, variables, and process lifecycle.
30+
- Stabilized soft interrupt handling so worker resets do not surface as connection-reset crashes in CI.
3031
- Cleaned frontend warning sources in dialog, toast, mode switcher, hint, guide, and widget surfaces.
3132
- Corrected the PyPI workflow to validate the adapter-static editor output in `src/codaro/webBuild` and run tests with the `dev` extra installed.
3233

src/codaro/runtime/localWorker.py

Lines changed: 53 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -65,16 +65,35 @@ def emitEvent(eventType: str, payload: Any = None) -> None:
6565
},
6666
)
6767

68-
response = _executeCommand(
69-
code=command["code"],
70-
blockId=blockId,
71-
injectedVars=command.get("injectedVars"),
72-
registry=registry,
73-
cellDefinitions=cellDefinitions,
74-
executionCount=executionCount,
75-
targetCwd=targetCwd,
76-
emitEvent=emitEvent,
77-
)
68+
try:
69+
response = _executeCommand(
70+
code=command["code"],
71+
blockId=blockId,
72+
injectedVars=command.get("injectedVars"),
73+
registry=registry,
74+
cellDefinitions=cellDefinitions,
75+
executionCount=executionCount,
76+
targetCwd=targetCwd,
77+
emitEvent=emitEvent,
78+
)
79+
except KeyboardInterrupt:
80+
if interruptFlag is not None:
81+
interruptFlag.clear()
82+
response = _buildInterruptedResponse(registry, cellDefinitions, executionCount)
83+
emitEvent(
84+
"display",
85+
{
86+
"outputType": "error",
87+
"data": response["data"],
88+
},
89+
)
90+
emitEvent(
91+
"finished",
92+
{
93+
"status": response["status"],
94+
"outputType": response["type"],
95+
},
96+
)
7897
_workerSend(connection, {"kind": "response", "response": response})
7998
continue
8099

@@ -239,6 +258,29 @@ def _buildStateResponse(
239258
}
240259

241260

261+
def _buildInterruptedResponse(
262+
registry: dict[str, object],
263+
cellDefinitions: dict[str, set[str]],
264+
executionCount: int,
265+
) -> dict[str, Any]:
266+
response = _buildStateResponse(registry, cellDefinitions, executionCount)
267+
response["stateDelta"] = {
268+
"added": [],
269+
"updated": [],
270+
"removed": [],
271+
}
272+
response.update(
273+
{
274+
"type": "error",
275+
"data": "Execution interrupted.",
276+
"stdout": "",
277+
"stderr": "",
278+
"status": "error",
279+
}
280+
)
281+
return response
282+
283+
242284
def _buildRegistryMirror(registry: dict[str, object]) -> dict[str, object]:
243285
snapshot: dict[str, object] = {}
244286
for name, value in registry.items():
@@ -517,7 +559,7 @@ def _sanitizeDescriptor(value: object) -> object:
517559

518560
def _installInterruptTrace(interruptFlag) -> None:
519561
def traceCallback(frame, event, arg):
520-
if interruptFlag.is_set():
562+
if interruptFlag.is_set() and frame.f_code.co_filename == "<codaro>":
521563
raise KeyboardInterrupt("Soft interrupt requested")
522564
return traceCallback
523565
sys.settrace(traceCallback)

0 commit comments

Comments
 (0)