From bc40fbc8263aeba64fb94e63ddcadf89bfb478a3 Mon Sep 17 00:00:00 2001 From: Tom Jakubowski Date: Thu, 17 Jul 2025 22:52:09 -0700 Subject: [PATCH] WIP, publish error tracebacks on iopub This ensures that when an Output ipywidget is used as a context manager, errors raised inside the context are rendered in the Output widget. ``` out = Output() out with out: raise RuntimeError("oh no! an error occurred") ``` However, it does not suppress the error in the original cell, which is to say the error is rendered both in the original cell and in the Output widget. This behavior is in contrast to Jupyter Lab and Notebook, which do suppress the error in the original cell. Fixing this is a TODO. This patch also handles the case where an ipywidgets handler, without the `@out.capture()` decorator, raises an exception. Such exceptions are now seen in the log console. TODOs: - [ ] Suppress error in the original cell --- .../py/pyodide-kernel/pyodide_kernel/display.py | 1 + .../py/pyodide-kernel/pyodide_kernel/interpreter.py | 6 ++++++ packages/pyodide-kernel/src/worker.ts | 3 ++- 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/pyodide-kernel/py/pyodide-kernel/pyodide_kernel/display.py b/packages/pyodide-kernel/py/pyodide-kernel/pyodide_kernel/display.py index bac096b8..790c0fbc 100644 --- a/packages/pyodide-kernel/py/pyodide-kernel/pyodide_kernel/display.py +++ b/packages/pyodide-kernel/py/pyodide-kernel/pyodide_kernel/display.py @@ -58,6 +58,7 @@ class LiteDisplayHook(DisplayHook): def __init__(self, *args, **kwargs): super(LiteDisplayHook, self).__init__(*args, **kwargs) self.publish_execution_result = None + self.publish_execution_error = None def start_displayhook(self): self.data = {} diff --git a/packages/pyodide-kernel/py/pyodide-kernel/pyodide_kernel/interpreter.py b/packages/pyodide-kernel/py/pyodide-kernel/pyodide_kernel/interpreter.py index aa844c36..426c371e 100644 --- a/packages/pyodide-kernel/py/pyodide-kernel/pyodide_kernel/interpreter.py +++ b/packages/pyodide-kernel/py/pyodide-kernel/pyodide_kernel/interpreter.py @@ -56,6 +56,12 @@ def enable_gui(self, gui=None): pass def _showtraceback(self, etype, evalue, stb): + sys.stdout.flush() + sys.stderr.flush() + dh = self.displayhook + if dh.publish_execution_error: + dh.publish_execution_error(str(etype), str(evalue), stb) + self._last_traceback = { "ename": str(etype), "evalue": str(evalue), diff --git a/packages/pyodide-kernel/src/worker.ts b/packages/pyodide-kernel/src/worker.ts index 675d6bcb..7e75b0e3 100644 --- a/packages/pyodide-kernel/src/worker.ts +++ b/packages/pyodide-kernel/src/worker.ts @@ -289,7 +289,7 @@ ${e.stack}`; const bundle = { ename: ename, evalue: evalue, - traceback: traceback, + traceback: this.formatResult(traceback), }; this._sendWorkerMessage({ @@ -363,6 +363,7 @@ ${e.stack}`; this._interpreter.display_pub.update_display_data_callback = updateDisplayDataCallback; this._interpreter.displayhook.publish_execution_result = publishExecutionResult; + this._interpreter.displayhook.publish_execution_error = publishExecutionError; this._interpreter.input = this.input.bind(this); this._interpreter.getpass = this.getpass.bind(this);