Repository: ipython/ipython

Issue:
Errors during interact display at the terminal, not anywhere in the notebook
If an error is raised by calling a function from an interact widget (or presumably from any widget update), the traceback appears in the terminal where the server was started, not anywhere in the notebook interface.



Discussion:
In general, this is because we allow widget errors to be completely unhandled, which results in logging. Probably an explicit call to ip.showtraceback() should be made somewhere. It seems like logging is the right thing to do in comms in general (by default), but not the right thing to do in widget callbacks, since they represent user action. But I'm not actually sure about that.

Maybe always send an error message back.  If comms don't choose to handle an error message, great.  Widgets should probably handle error messages, though.


Resolving patch:
diff --git a/IPython/html/widgets/interaction.py b/IPython/html/widgets/interaction.py
--- a/IPython/html/widgets/interaction.py
+++ b/IPython/html/widgets/interaction.py
@@ -20,6 +20,7 @@
     from IPython.utils.signatures import signature, Parameter
 from inspect import getcallargs
 
+from IPython.core.getipython import get_ipython
 from IPython.html.widgets import (Widget, TextWidget,
     FloatSliderWidget, IntSliderWidget, CheckboxWidget, DropdownWidget,
     ContainerWidget, DOMWidget)
@@ -205,7 +206,14 @@ def call_f(name, old, new):
             container.kwargs[widget.description] = value
         if co:
             clear_output(wait=True)
-        container.result = f(**container.kwargs)
+        try:
+            container.result = f(**container.kwargs)
+        except Exception as e:
+            ip = get_ipython()
+            if ip is None:
+                container.log.warn("Exception in interact callback: %s", e, exc_info=True)
+            else:
+                ip.showtraceback()
 
     # Wire up the widgets
     for widget in kwargs_widgets:
diff --git a/IPython/html/widgets/widget.py b/IPython/html/widgets/widget.py
--- a/IPython/html/widgets/widget.py
+++ b/IPython/html/widgets/widget.py
@@ -14,6 +14,7 @@
 #-----------------------------------------------------------------------------
 from contextlib import contextmanager
 
+from IPython.core.getipython import get_ipython
 from IPython.kernel.comm import Comm
 from IPython.config import LoggingConfigurable
 from IPython.utils.traitlets import Unicode, Dict, Instance, Bool, List, Tuple
@@ -33,7 +34,11 @@ def __call__(self, *args, **kwargs):
             try:
                 local_value = callback(*args, **kwargs)
             except Exception as e:
-                self.log.warn("Exception in callback %s: %s", callback, e)
+                ip = get_ipython()
+                if ip is None:
+                    self.log.warn("Exception in callback %s: %s", callback, e, exc_info=True)
+                else:
+                    ip.showtraceback()
             else:
                 value = local_value if local_value is not None else value
         return value
@@ -54,6 +59,18 @@ def register_callback(self, callback, remove=False):
         elif not remove and callback not in self.callbacks:
             self.callbacks.append(callback)
 
+def _show_traceback(method):
+    """decorator for showing tracebacks in IPython"""
+    def m(self, *args, **kwargs):
+        try:
+            return(method(self, *args, **kwargs))
+        except Exception as e:
+            ip = get_ipython()
+            if ip is None:
+                self.log.warn("Exception in widget method %s: %s", method, e, exc_info=True)
+            else:
+                ip.showtraceback()
+    return m
 
 class Widget(LoggingConfigurable):
     #-------------------------------------------------------------------------
@@ -241,6 +258,7 @@ def _should_send_property(self, key, value):
         value != self._property_lock[1]
     
     # Event handlers
+    @_show_traceback
     def _handle_msg(self, msg):
         """Called when a msg is received from the front-end"""
         data = msg['content']['data']


Test patch: