Repository: bokeh/bokeh

Issue:
Out of range float values are not JSON compliant with numpy arrays
#### ALL software version info (bokeh, python, notebook, OS, browser, any other relevant packages)

bokeh 0.12.14

#### Description of expected behavior and the observed behavior

I get an error on both the client and server like the following:

    ERROR:bokeh.server.protocol_handler:error handling message Message 'PULL-DOC-REQ' (revision 1): ValueError('Out of range float values are not JSON compliant',)

#### Complete, minimal, self-contained example code that reproduces the issue

```python
from bokeh.server.server import Server
from bokeh.plotting import figure, ColumnDataSource
import numpy as np

def handle_doc(doc):
    source = ColumnDataSource({'x': [], 'y': []})
    fig = figure()
    fig.line(x='x', y='y', source=source, line_width=1)

    data = {'x': np.array([  1.,   0.,  np.nan,   1.,   0.,  np.nan,
                             1.,   0.,  np.nan,   1.,   0.,  np.nan,
                             1.,   0.,  np.nan]),
            'y': np.array([  2.,   1.,  np.nan,   2.,   3.,  np.nan,
                             2.,   2.,  np.nan,   2.,   0.,  np.nan,
                             2.,   4.,  np.nan])}
    doc.add_root(fig)
    source.stream(data)
    # source.data.update(data)  # this works fine


apps = {'/': handle_doc}

server = Server(apps, port=5000)
server.start()

import tornado.ioloop
tornado.ioloop.IOLoop.current().start()
```

#### Stack traceback and/or browser JavaScript console output

```
[bokeh] setting log level to: 'info'
connection.js:194 [bokeh] Websocket connection 0 is now open
connection.js:188 Error: Error reply ValueError('Out of range float values are not JSON compliant',)
    at connection.js:131
    at es6-promise.js:281
    at f (es6-promise.js:290)
    at p (es6-promise.js:268)
    at MutationObserver.r (es6-promise.js:102)
(anonymous) @ connection.js:188
connection.js:189 [bokeh] Failed to repull session Error: Error reply ValueError('Out of range float values are not JSON compliant',)
(anonymous) @ connection.js:189
connection.js:221 [bokeh] Lost websocket 0 connection, 1006 ()
connection.js:105 [bokeh] Websocket connection 0 disconnected, will not attempt to reconnect
```


Discussion:
The problem with `nan` (and `inf`) is that it must be passed via a numpy array, which is the case here. This may indicate that `stream()` converts to a list, which later can't be serialized.
This will work if you change:
```
source = ColumnDataSource({'x': [], 'y': []})
```
to
```
source = ColumnDataSource({'x': np.array([]), 'y': np.array([])})
```
The reason is that the properties system tries to concatenate `[]` with a numpy array, and this results in a list, which later fails to serialize. This code is at fault:

https://github.com/bokeh/bokeh/blob/4f45e7480add13f6ab7dc9c781454107967dfbbe/bokeh/core/property/containers.py#L381-L391

A simple solution would be to add `or isinstance(new_data[k], np.ndarray)`.
That's quite helpful.  Thank you for the rapid response @mattpap !

Resolving patch:
diff --git a/bokeh/core/property/containers.py b/bokeh/core/property/containers.py
--- a/bokeh/core/property/containers.py
+++ b/bokeh/core/property/containers.py
@@ -379,7 +379,7 @@ def _stream(self, doc, source, new_data, rollover=None, setter=None):
         # is actually the already updated value. This is because the method
         # self._saved_copy() makes a shallow copy.
         for k, v in  new_data.items():
-            if isinstance(self[k], np.ndarray):
+            if isinstance(self[k], np.ndarray) or isinstance(new_data[k], np.ndarray):
                 data = np.append(self[k], new_data[k])
                 if rollover and len(data) > rollover:
                     data = data[-rollover:]


Test patch:
diff --git a/bokeh/core/property/tests/test_containers.py b/bokeh/core/property/tests/test_containers.py
--- a/bokeh/core/property/tests/test_containers.py
+++ b/bokeh/core/property/tests/test_containers.py
@@ -103,7 +103,7 @@ def test_PropertyValueColumnData_update(mock_notify):
     assert sorted(mock_notify.call_args[1]['hint'].cols) == ['bar', 'foo']
 
 @patch('bokeh.core.property.containers.PropertyValueContainer._notify_owners')
-def test_PropertyValueColumnData__stream_list(mock_notify):
+def test_PropertyValueColumnData__stream_list_to_list(mock_notify):
     from bokeh.document.events import ColumnsStreamedEvent
 
     source = ColumnDataSource(data=dict(foo=[10]))
@@ -112,12 +112,30 @@ def test_PropertyValueColumnData__stream_list(mock_notify):
     mock_notify.reset_mock()
     pvcd._stream("doc", source, dict(foo=[20]), setter="setter")
     assert mock_notify.call_count == 1
-    assert mock_notify.call_args[0] == ({'foo': [10, 20]},)
+    assert mock_notify.call_args[0] == ({'foo': [10, 20]},) # streaming to list, "old" is actually updated value
     assert 'hint' in mock_notify.call_args[1]
     assert isinstance(mock_notify.call_args[1]['hint'], ColumnsStreamedEvent)
     assert mock_notify.call_args[1]['hint'].setter == 'setter'
     assert mock_notify.call_args[1]['hint'].rollover == None
 
+@patch('bokeh.core.property.containers.PropertyValueContainer._notify_owners')
+def test_PropertyValueColumnData__stream_list_to_array(mock_notify):
+    from bokeh.document.events import ColumnsStreamedEvent
+    import numpy as np
+
+    source = ColumnDataSource(data=dict(foo=np.array([10])))
+    pvcd = pc.PropertyValueColumnData(source.data)
+
+    mock_notify.reset_mock()
+    pvcd._stream("doc", source, dict(foo=[20]), setter="setter")
+    assert mock_notify.call_count == 1
+    assert (mock_notify.call_args[0][0]['foo'] == np.array([10])).all()
+    assert 'hint' in mock_notify.call_args[1]
+    assert isinstance(mock_notify.call_args[1]['hint'], ColumnsStreamedEvent)
+    assert mock_notify.call_args[1]['hint'].setter == 'setter'
+    assert mock_notify.call_args[1]['hint'].rollover == None
+
+
 @patch('bokeh.core.property.containers.PropertyValueContainer._notify_owners')
 def test_PropertyValueColumnData__stream_list_with_rollover(mock_notify):
     from bokeh.document.events import ColumnsStreamedEvent
@@ -128,14 +146,14 @@ def test_PropertyValueColumnData__stream_list_with_rollover(mock_notify):
     mock_notify.reset_mock()
     pvcd._stream("doc", source, dict(foo=[40]), rollover=3, setter="setter")
     assert mock_notify.call_count == 1
-    assert mock_notify.call_args[0] == ({'foo': [20, 30, 40]},)
+    assert mock_notify.call_args[0] == ({'foo': [20, 30, 40]},) # streaming to list, "old" is actually updated value
     assert 'hint' in mock_notify.call_args[1]
     assert isinstance(mock_notify.call_args[1]['hint'], ColumnsStreamedEvent)
     assert mock_notify.call_args[1]['hint'].setter == 'setter'
     assert mock_notify.call_args[1]['hint'].rollover == 3
 
 @patch('bokeh.core.property.containers.PropertyValueContainer._notify_owners')
-def test_PropertyValueColumnData__stream_array(mock_notify):
+def test_PropertyValueColumnData__stream_array_to_array(mock_notify):
     from bokeh.document.events import ColumnsStreamedEvent
     import numpy as np
 
@@ -153,6 +171,24 @@ def test_PropertyValueColumnData__stream_array(mock_notify):
     assert mock_notify.call_args[1]['hint'].setter == 'setter'
     assert mock_notify.call_args[1]['hint'].rollover == None
 
+@patch('bokeh.core.property.containers.PropertyValueContainer._notify_owners')
+def test_PropertyValueColumnData__stream_array_to_list(mock_notify):
+    from bokeh.document.events import ColumnsStreamedEvent
+
+    source = ColumnDataSource(data=dict(foo=[10]))
+    pvcd = pc.PropertyValueColumnData(source.data)
+
+    mock_notify.reset_mock()
+    pvcd._stream("doc", source, dict(foo=[20]), setter="setter")
+    assert mock_notify.call_count == 1
+    assert len(mock_notify.call_args[0]) == 1
+    assert 'foo' in mock_notify.call_args[0][0]
+    assert mock_notify.call_args[0] == ({'foo': [10, 20]},) # streaming to list, "old" is actually updated value
+    assert 'hint' in mock_notify.call_args[1]
+    assert isinstance(mock_notify.call_args[1]['hint'], ColumnsStreamedEvent)
+    assert mock_notify.call_args[1]['hint'].setter == 'setter'
+    assert mock_notify.call_args[1]['hint'].rollover == None
+
 @patch('bokeh.core.property.containers.PropertyValueContainer._notify_owners')
 def test_PropertyValueColumnData__stream_array_with_rollover(mock_notify):
     from bokeh.document.events import ColumnsStreamedEvent