diff --git a/IPython/html/widgets/interaction.py b/IPython/html/widgets/interaction.py
index a943894..3f30fd7 100644
--- a/IPython/html/widgets/interaction.py
+++ b/IPython/html/widgets/interaction.py
@@ -14,7 +14,7 @@ from inspect import getcallargs
from IPython.core.getipython import get_ipython
from IPython.html.widgets import (Widget, Text,
FloatSlider, IntSlider, Checkbox, Dropdown,
- Box, Button, DOMWidget)
+ Box, Button, DOMWidget, Output)
from IPython.display import display, clear_output
from IPython.utils.py3compat import string_types, unicode_type
from IPython.utils.traitlets import HasTraits, Any, Unicode
@@ -204,29 +204,34 @@ def interactive(__interact_f, **kwargs):
if manual:
manual_button = Button(description="Run %s" % f.__name__)
c.append(manual_button)
+
+ # Use an output widget to capture the output of interact.
+ output = Output()
+ c.append(output)
container.children = c
# Build the callback
def call_f(name=None, old=None, new=None):
- container.kwargs = {}
- for widget in kwargs_widgets:
- value = widget.value
- container.kwargs[widget._kwarg] = value
- if co:
- clear_output(wait=True)
- if manual:
- manual_button.disabled = True
- 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()
- finally:
+ with output:
+ container.kwargs = {}
+ for widget in kwargs_widgets:
+ value = widget.value
+ container.kwargs[widget._kwarg] = value
+ if co:
+ clear_output(wait=True)
if manual:
- manual_button.disabled = False
+ manual_button.disabled = True
+ 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()
+ finally:
+ if manual:
+ manual_button.disabled = False
# Wire up the widgets
# If we are doing manual running, the callback is only triggered by the button
diff --git a/IPython/html/widgets/tests/test_interaction.py b/IPython/html/widgets/tests/test_interaction.py
index 774b25c..9d2a9eb 100644
--- a/IPython/html/widgets/tests/test_interaction.py
+++ b/IPython/html/widgets/tests/test_interaction.py
@@ -80,7 +80,8 @@ def check_widgets(container, **to_check):
# build a widget dictionary, so it matches
widgets = {}
for w in container.children:
- widgets[w.description] = w
+ if hasattr(w, 'description'):
+ widgets[w.description] = w
for key, d in to_check.items():
nt.assert_in(key, widgets)
@@ -138,7 +139,7 @@ def test_single_value_float():
def test_single_value_int():
for a in (1, 5, -3):
c = interactive(f, a=a)
- nt.assert_equal(len(c.children), 1)
+ nt.assert_equal(len(c.children), 2)
w = c.children[0]
check_widget(w,
cls=widgets.IntSlider,
@@ -157,7 +158,7 @@ def test_list_tuple_2_int():
c = interactive(f, tup=(1,-1))
for min, max in [ (0,1), (1,10), (1,2), (-5,5), (-20,-19) ]:
c = interactive(f, tup=(min, max), lis=[min, max])
- nt.assert_equal(len(c.children), 2)
+ nt.assert_equal(len(c.children), 3)
d = dict(
cls=widgets.IntSlider,
min=min,
@@ -174,7 +175,7 @@ def test_list_tuple_3_int():
c = interactive(f, tup=(1,2,-1))
for min, max, step in [ (0,2,1), (1,10,2), (1,100,2), (-5,5,4), (-100,-20,4) ]:
c = interactive(f, tup=(min, max, step), lis=[min, max, step])
- nt.assert_equal(len(c.children), 2)
+ nt.assert_equal(len(c.children), 3)
d = dict(
cls=widgets.IntSlider,
min=min,
@@ -191,7 +192,7 @@ def test_list_tuple_2_float():
c = interactive(f, tup=(0.5,-0.5))
for min, max in [ (0.5, 1.5), (1.1,10.2), (1,2.2), (-5.,5), (-20,-19.) ]:
c = interactive(f, tup=(min, max), lis=[min, max])
- nt.assert_equal(len(c.children), 2)
+ nt.assert_equal(len(c.children), 3)
d = dict(
cls=widgets.FloatSlider,
min=min,
@@ -210,7 +211,7 @@ def test_list_tuple_3_float():
c = interactive(f, tup=(1,2.,-1.))
for min, max, step in [ (0.,2,1), (1,10.,2), (1,100,2.), (-5.,5.,4), (-100,-20.,4.) ]:
c = interactive(f, tup=(min, max, step), lis=[min, max, step])
- nt.assert_equal(len(c.children), 2)
+ nt.assert_equal(len(c.children), 3)
d = dict(
cls=widgets.FloatSlider,
min=min,
@@ -224,7 +225,7 @@ def test_list_tuple_str():
values = ['hello', 'there', 'guy']
first = values[0]
c = interactive(f, tup=tuple(values), lis=list(values))
- nt.assert_equal(len(c.children), 2)
+ nt.assert_equal(len(c.children), 3)
d = dict(
cls=widgets.Dropdown,
value=first,
@@ -471,7 +472,7 @@ def test_call_decorated_kwargs_on_trait_change():
def test_fixed():
c = interactive(f, a=widgets.fixed(5), b='text')
- nt.assert_equal(len(c.children), 1)
+ nt.assert_equal(len(c.children), 2)
w = c.children[0]
check_widget(w,
cls=widgets.Text,
diff --git a/IPython/html/widgets/widget_output.py b/IPython/html/widgets/widget_output.py
index 771ecb7..578df58 100644
--- a/IPython/html/widgets/widget_output.py
+++ b/IPython/html/widgets/widget_output.py
@@ -34,43 +34,53 @@ class Output(DOMWidget):
print('prints to output widget')"""
_view_name = Unicode('OutputView', sync=True)
+ def __init__(self, *args, **kwargs):
+ super(Output, self).__init__(*args, **kwargs)
+ from IPython import get_ipython
+ ip = get_ipython()
+ if ip is not None and hasattr(ip, 'kernel'):
+ self._kernel = ip.kernel
+ else:
+ self._kernel = None
+
def clear_output(self, *pargs, **kwargs):
with self:
clear_output(*pargs, **kwargs)
def __enter__(self):
"""Called upon entering output widget context manager."""
- self._flush()
- kernel = get_ipython().kernel
- session = kernel.session
- send = session.send
- self._original_send = send
- self._session = session
-
- def send_hook(stream, msg_or_type, content=None, parent=None, ident=None,
- buffers=None, track=False, header=None, metadata=None):
-
- # Handle both prebuild messages and unbuilt messages.
- if isinstance(msg_or_type, (Message, dict)):
- msg_type = msg_or_type['msg_type']
- msg = dict(msg_or_type)
- else:
- msg_type = msg_or_type
- msg = session.msg(msg_type, content=content, parent=parent,
- header=header, metadata=metadata)
-
- # If this is a message type that we want to forward, forward it.
- if stream is kernel.iopub_socket and msg_type in ['clear_output', 'stream', 'display_data']:
- self.send(msg)
- else:
- send(stream, msg, ident=ident, buffers=buffers, track=track)
-
- session.send = send_hook
+ if self._kernel is not None:
+ self._flush()
+ session = self._kernel.session
+ send = session.send
+ self._original_send = send
+ self._session = session
+
+ def send_hook(stream, msg_or_type, content=None, parent=None, ident=None,
+ buffers=None, track=False, header=None, metadata=None):
+
+ # Handle both prebuild messages and unbuilt messages.
+ if isinstance(msg_or_type, (Message, dict)):
+ msg_type = msg_or_type['msg_type']
+ msg = dict(msg_or_type)
+ else:
+ msg_type = msg_or_type
+ msg = session.msg(msg_type, content=content, parent=parent,
+ header=header, metadata=metadata)
+
+ # If this is a message type that we want to forward, forward it.
+ if stream is self._kernel.iopub_socket and msg_type in ['clear_output', 'stream', 'display_data']:
+ self.send(msg)
+ else:
+ send(stream, msg, ident=ident, buffers=buffers, track=track)
+
+ session.send = send_hook
def __exit__(self, exception_type, exception_value, traceback):
"""Called upon exiting output widget context manager."""
- self._flush()
- self._session.send = self._original_send
+ if self._kernel is not None:
+ self._flush()
+ self._session.send = self._original_send
def _flush(self):
"""Flush stdout and stderr buffers."""