Repository: ipython/ipython

Issue:
Output widget issues. 
Widget outputs not stored in notebook format anymore. 

Example of beating frequency does not work the same for 2 reasons : 

1 - `<audio controls=\"controls\" >` not in ipynb anymore. 
2 - `f1, f2 = v.children`raise as `v` has 3 childrens: `f1`, `f2` and `out`.



Discussion:
@jdfreder did output widget outputs used to persist in the document, or am I crazy? If this is the expected behavior of an output widget, we should revert the change that puts interact in an output widget.

I'm pretty sure at some point in time they did get saved.  I feel like I've implemented the output widget a couple of times now.  At first I thought #7523 introduced the regression, but it looks like the implementation prior to that was also bugged.

@jdfreder what do you want to do with respect to 3.0? I don't think we should let interact output get lost. Would you rather revert the change that put interact in an OutputWidget, or make sure OutputWidget outputs aren't ethereal? I would be okay if OutputWidget outputs were treated like the rest of the widgets, but I don't think that should apply to `@interact` outputs. 


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
@@ -14,7 +14,7 @@
 from IPython.core.getipython import get_ipython
 from IPython.html.widgets import (Widget, Text,
     FloatSlider, IntSlider, Checkbox, Dropdown,
-    Box, Button, DOMWidget, Output)
+    Box, Button, DOMWidget)
 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,34 +204,29 @@ 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):
-        with output:
-            container.kwargs = {}
-            for widget in kwargs_widgets:
-                value = widget.value
-                container.kwargs[widget._kwarg] = value
-            if co:
-                clear_output(wait=True)
+        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:
             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:
-                if manual:
-                    manual_button.disabled = False
+                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/widget_output.py b/IPython/html/widgets/widget_output.py
--- a/IPython/html/widgets/widget_output.py
+++ b/IPython/html/widgets/widget_output.py
@@ -34,53 +34,43 @@ 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."""
-        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
+        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
 
     def __exit__(self, exception_type, exception_value, traceback):
         """Called upon exiting output widget context manager."""
-        if self._kernel is not None:
-            self._flush()
-            self._session.send = self._original_send
+        self._flush()
+        self._session.send = self._original_send
 
     def _flush(self):
         """Flush stdout and stderr buffers."""


Test patch:
diff --git a/IPython/html/widgets/tests/test_interaction.py b/IPython/html/widgets/tests/test_interaction.py
--- a/IPython/html/widgets/tests/test_interaction.py
+++ b/IPython/html/widgets/tests/test_interaction.py
@@ -80,8 +80,7 @@ def check_widgets(container, **to_check):
     # build a widget dictionary, so it matches
     widgets = {}
     for w in container.children:
-        if hasattr(w, 'description'):
-            widgets[w.description] = w
+        widgets[w.description] = w
     
     for key, d in to_check.items():
         nt.assert_in(key, widgets)
@@ -139,7 +138,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), 2)
+        nt.assert_equal(len(c.children), 1)
         w = c.children[0]
         check_widget(w,
             cls=widgets.IntSlider,
@@ -158,7 +157,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), 3)
+        nt.assert_equal(len(c.children), 2)
         d = dict(
             cls=widgets.IntSlider,
             min=min,
@@ -175,7 +174,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), 3)
+        nt.assert_equal(len(c.children), 2)
         d = dict(
             cls=widgets.IntSlider,
             min=min,
@@ -192,7 +191,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), 3)
+        nt.assert_equal(len(c.children), 2)
         d = dict(
             cls=widgets.FloatSlider,
             min=min,
@@ -211,7 +210,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), 3)
+        nt.assert_equal(len(c.children), 2)
         d = dict(
             cls=widgets.FloatSlider,
             min=min,
@@ -225,7 +224,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), 3)
+    nt.assert_equal(len(c.children), 2)
     d = dict(
         cls=widgets.Dropdown,
         value=first,
@@ -472,7 +471,7 @@ def foo(a='default'):
 
 def test_fixed():
     c = interactive(f, a=widgets.fixed(5), b='text')
-    nt.assert_equal(len(c.children), 2)
+    nt.assert_equal(len(c.children), 1)
     w = c.children[0]
     check_widget(w,
         cls=widgets.Text,