Repository: ipython/ipython

Issue:
JSON serialization should allow NaNs. 
In IPython's messaging machinery, the function `JSON.dumps` is called with the default value for the parameter `allow_nan`, that is `True`, causing the generated JSON to be invalid, and therefore causing parsing errors on the JavaScript side. 
(However, `JSON.loads` parses that invalid JSON, and therefore it is still fine for Python to Python communication.)

The following code, to be run in the notebook triggers the error:

``` Python
from IPython.kernel.comm import Comm
from IPython.utils.traitlets import HasTraits, Instance
class nanmsg(HasTraits):
    comm = Instance(Comm, kw=dict(target_name='nanmsg'))
    def __init__(self):
        self.comm.send([float('nan')])
nanmsg()
```

``` JavaScript
%%javascript
var nanmsg = function (comm) {
    this.comm = comm;
    comm.on_msg($.proxy(this.handler, this));
};
nanmsg.prototype.handler = function (msg) {
    alert(msg);
};
IPython.notebook.kernel.comm_manager.register_target('nanmsg', function (comm) { var f = new nanmsg(comm); console.log(f)});
```

However, replacing `float('nan')` by `None` will not cause any JSON parsing error. 

All the calls to `JSON.dumps` should probably be done with `allow_nan=False`, which raises a ValueError when it encounters nans. 



Discussion:

Resolving patch:
diff --git a/IPython/kernel/comm/comm.py b/IPython/kernel/comm/comm.py
--- a/IPython/kernel/comm/comm.py
+++ b/IPython/kernel/comm/comm.py
@@ -1,26 +1,16 @@
 """Base class for a Comm"""
 
-#-----------------------------------------------------------------------------
-#  Copyright (C) 2013  The IPython Development Team
-#
-#  Distributed under the terms of the BSD License.  The full license is in
-#  the file COPYING, distributed as part of this software.
-#-----------------------------------------------------------------------------
-
-#-----------------------------------------------------------------------------
-# Imports
-#-----------------------------------------------------------------------------
+# Copyright (c) IPython Development Team.
+# Distributed under the terms of the Modified BSD License.
 
 import uuid
 
 from IPython.config import LoggingConfigurable
 from IPython.core.getipython import get_ipython
 
+from IPython.utils.jsonutil import json_clean
 from IPython.utils.traitlets import Instance, Unicode, Bytes, Bool, Dict, Any
 
-#-----------------------------------------------------------------------------
-# Code
-#-----------------------------------------------------------------------------
 
 class Comm(LoggingConfigurable):
     
@@ -69,9 +59,10 @@ def _publish_msg(self, msg_type, data=None, metadata=None, **keys):
         """Helper for sending a comm message on IOPub"""
         data = {} if data is None else data
         metadata = {} if metadata is None else metadata
+        content = json_clean(dict(data=data, comm_id=self.comm_id, **keys))
         self.session.send(self.iopub_socket, msg_type,
-            dict(data=data, comm_id=self.comm_id, **keys),
-            metadata=metadata,
+            content,
+            metadata=json_clean(metadata),
             parent=self.shell.get_parent(),
             ident=self.topic,
         )
diff --git a/IPython/kernel/zmq/session.py b/IPython/kernel/zmq/session.py
--- a/IPython/kernel/zmq/session.py
+++ b/IPython/kernel/zmq/session.py
@@ -67,7 +67,11 @@ def squash_unicode(obj):
 #-----------------------------------------------------------------------------
 
 # ISO8601-ify datetime objects
-json_packer = lambda obj: jsonapi.dumps(obj, default=date_default, ensure_ascii=False)
+# allow unicode
+# disallow nan, because it's not actually valid JSON
+json_packer = lambda obj: jsonapi.dumps(obj, default=date_default,
+    ensure_ascii=False, allow_nan=False,
+)
 json_unpacker = lambda s: jsonapi.loads(s)
 
 pickle_packer = lambda o: pickle.dumps(squash_dates(o),-1)


Test patch: