We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Qtconsole docstring pop-up fails on method containing defaulted enum argument
[We've found](http://trac.mantidproject.org/mantid/ticket/8422) that an error is generated in the (admittedly rare) situation where a C++ enum is exposed to Python via boost python and included in a python function as a default to an argument. This is seen in IPython 1.1 and the current tip of master.

Here's the simplest example I could come up with, though it does still require compiling C++ and linking to boost python. In a C++ file:

``` c++
#include <boost/python.hpp>

enum MyEnum
{
  Red,
  Yellow,
  Blue
};

BOOST_PYTHON_MODULE(enum_test)
{
  using namespace boost::python;

  enum_<MyEnum>("MyEnum")
    .value("Red", Red)
    .value("Yellow", Yellow)
    .value("Blue", Blue);
}
```

This should be compiled to a shared library with something like `gcc -fPIC -I /usr/include/python2.6 -shared -o enum_test.so enum_test.cpp -lboost_python`

Then, in the IPython qtconsole enter:

```
In [1]: import enum_test

In [2]: def MyFunc(color = enum_test.MyEnum.Red):
   ...:     pass
   ...: 

In [3]: MyFunc(
```

On typing the opening parenthesis a stack trace will appear that culminates in:

```
  File "/usr/lib/python2.6/site-packages/IPython/kernel/zmq/session.py", line 83, in <lambda>
    json_unpacker = lambda s: extract_dates(jsonapi.loads(s))
  File "/usr/lib64/python2.6/site-packages/zmq/utils/jsonapi.py", line 81, in loads
    return jsonmod.loads(s, **kwargs)
  File "/usr/lib64/python2.6/site-packages/simplejson/__init__.py", line 307, in loads
    return _default_decoder.decode(s)
  File "/usr/lib64/python2.6/site-packages/simplejson/decoder.py", line 335, in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
  File "/usr/lib64/python2.6/site-packages/simplejson/decoder.py", line 353, in raw_decode
    raise ValueError("No JSON object could be decoded")
```

The string that's going into the raw_decode function of decoder.py and leads to the exception is:

```
{"base_class":"<type 'function'>","init_definition":null,"type_name":"function","name":"MyFunc","isclass":null,"namespace":"Interactive","isalias":false,"init_docstring":null,"argspec":{"args":["color"],"varkw":null,"defaults":[Red],"varargs":null},"source":null,"length":null,"call_def":null,"call_docstring":null,"file":"/home/enumproblem/<ipython-input-2-b6e10dea3e06>","string_form":"<function MyFunc at 0x16a8de8>","found":true,"class_docstring":null,"definition":"\u001b[0mMyFunc\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mcolor\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0menum_test\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mMyEnum\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mRed\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n","docstring":"<no docstring>","ismagic":false}
```


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `IPython/utils/jsonutil.py`
Content:
```
1 """Utilities to manipulate JSON objects.
2 """
3 #-----------------------------------------------------------------------------
4 #  Copyright (C) 2010-2011  The IPython Development Team
5 #
6 #  Distributed under the terms of the BSD License.  The full license is in
7 #  the file COPYING.txt, distributed as part of this software.
8 #-----------------------------------------------------------------------------
9 
10 #-----------------------------------------------------------------------------
11 # Imports
12 #-----------------------------------------------------------------------------
13 # stdlib
14 import math
15 import re
16 import types
17 from datetime import datetime
18 
19 try:
20     # base64.encodestring is deprecated in Python 3.x
21     from base64 import encodebytes
22 except ImportError:
23     # Python 2.x
24     from base64 import encodestring as encodebytes
25 
26 from IPython.utils import py3compat
27 from IPython.utils.py3compat import string_types, unicode_type, iteritems
28 from IPython.utils.encoding import DEFAULT_ENCODING
29 next_attr_name = '__next__' if py3compat.PY3 else 'next'
30 
31 #-----------------------------------------------------------------------------
32 # Globals and constants
33 #-----------------------------------------------------------------------------
34 
35 # timestamp formats
36 ISO8601 = "%Y-%m-%dT%H:%M:%S.%f"
37 ISO8601_PAT=re.compile(r"^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{1,6})Z?([\+\-]\d{2}:?\d{2})?$")
38 
39 #-----------------------------------------------------------------------------
40 # Classes and functions
41 #-----------------------------------------------------------------------------
42 
43 def rekey(dikt):
44     """Rekey a dict that has been forced to use str keys where there should be
45     ints by json."""
46     for k in dikt:
47         if isinstance(k, string_types):
48             ik=fk=None
49             try:
50                 ik = int(k)
51             except ValueError:
52                 try:
53                     fk = float(k)
54                 except ValueError:
55                     continue
56             if ik is not None:
57                 nk = ik
58             else:
59                 nk = fk
60             if nk in dikt:
61                 raise KeyError("already have key %r"%nk)
62             dikt[nk] = dikt.pop(k)
63     return dikt
64 
65 def parse_date(s):
66     """parse an ISO8601 date string
67     
68     If it is None or not a valid ISO8601 timestamp,
69     it will be returned unmodified.
70     Otherwise, it will return a datetime object.
71     """
72     if s is None:
73         return s
74     m = ISO8601_PAT.match(s)
75     if m:
76         # FIXME: add actual timezone support
77         # this just drops the timezone info
78         notz = m.groups()[0]
79         return datetime.strptime(notz, ISO8601)
80     return s
81 
82 def extract_dates(obj):
83     """extract ISO8601 dates from unpacked JSON"""
84     if isinstance(obj, dict):
85         new_obj = {} # don't clobber
86         for k,v in iteritems(obj):
87             new_obj[k] = extract_dates(v)
88         obj = new_obj
89     elif isinstance(obj, (list, tuple)):
90         obj = [ extract_dates(o) for o in obj ]
91     elif isinstance(obj, string_types):
92         obj = parse_date(obj)
93     return obj
94 
95 def squash_dates(obj):
96     """squash datetime objects into ISO8601 strings"""
97     if isinstance(obj, dict):
98         obj = dict(obj) # don't clobber
99         for k,v in iteritems(obj):
100             obj[k] = squash_dates(v)
101     elif isinstance(obj, (list, tuple)):
102         obj = [ squash_dates(o) for o in obj ]
103     elif isinstance(obj, datetime):
104         obj = obj.isoformat()
105     return obj
106 
107 def date_default(obj):
108     """default function for packing datetime objects in JSON."""
109     if isinstance(obj, datetime):
110         return obj.isoformat()
111     else:
112         raise TypeError("%r is not JSON serializable"%obj)
113 
114 
115 # constants for identifying png/jpeg data
116 PNG = b'\x89PNG\r\n\x1a\n'
117 # front of PNG base64-encoded
118 PNG64 = b'iVBORw0KG'
119 JPEG = b'\xff\xd8'
120 # front of JPEG base64-encoded
121 JPEG64 = b'/9'
122 
123 def encode_images(format_dict):
124     """b64-encodes images in a displaypub format dict
125 
126     Perhaps this should be handled in json_clean itself?
127 
128     Parameters
129     ----------
130 
131     format_dict : dict
132         A dictionary of display data keyed by mime-type
133 
134     Returns
135     -------
136 
137     format_dict : dict
138         A copy of the same dictionary,
139         but binary image data ('image/png' or 'image/jpeg')
140         is base64-encoded.
141 
142     """
143     encoded = format_dict.copy()
144 
145     pngdata = format_dict.get('image/png')
146     if isinstance(pngdata, bytes):
147         # make sure we don't double-encode
148         if not pngdata.startswith(PNG64):
149             pngdata = encodebytes(pngdata)
150         encoded['image/png'] = pngdata.decode('ascii')
151 
152     jpegdata = format_dict.get('image/jpeg')
153     if isinstance(jpegdata, bytes):
154         # make sure we don't double-encode
155         if not jpegdata.startswith(JPEG64):
156             jpegdata = encodebytes(jpegdata)
157         encoded['image/jpeg'] = jpegdata.decode('ascii')
158 
159     return encoded
160 
161 
162 def json_clean(obj):
163     """Clean an object to ensure it's safe to encode in JSON.
164 
165     Atomic, immutable objects are returned unmodified.  Sets and tuples are
166     converted to lists, lists are copied and dicts are also copied.
167 
168     Note: dicts whose keys could cause collisions upon encoding (such as a dict
169     with both the number 1 and the string '1' as keys) will cause a ValueError
170     to be raised.
171 
172     Parameters
173     ----------
174     obj : any python object
175 
176     Returns
177     -------
178     out : object
179 
180       A version of the input which will not cause an encoding error when
181       encoded as JSON.  Note that this function does not *encode* its inputs,
182       it simply sanitizes it so that there will be no encoding errors later.
183 
184     Examples
185     --------
186     >>> json_clean(4)
187     4
188     >>> json_clean(list(range(10)))
189     [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
190     >>> sorted(json_clean(dict(x=1, y=2)).items())
191     [('x', 1), ('y', 2)]
192     >>> sorted(json_clean(dict(x=1, y=2, z=[1,2,3])).items())
193     [('x', 1), ('y', 2), ('z', [1, 2, 3])]
194     >>> json_clean(True)
195     True
196     """
197     # types that are 'atomic' and ok in json as-is.  bool doesn't need to be
198     # listed explicitly because bools pass as int instances
199     atomic_ok = (unicode_type, int, type(None))
200 
201     # containers that we need to convert into lists
202     container_to_list = (tuple, set, types.GeneratorType)
203 
204     if isinstance(obj, float):
205         # cast out-of-range floats to their reprs
206         if math.isnan(obj) or math.isinf(obj):
207             return repr(obj)
208         return obj
209 
210     if isinstance(obj, atomic_ok):
211         return obj
212 
213     if isinstance(obj, bytes):
214         return obj.decode(DEFAULT_ENCODING, 'replace')
215 
216     if isinstance(obj, container_to_list) or (
217         hasattr(obj, '__iter__') and hasattr(obj, next_attr_name)):
218         obj = list(obj)
219 
220     if isinstance(obj, list):
221         return [json_clean(x) for x in obj]
222 
223     if isinstance(obj, dict):
224         # First, validate that the dict won't lose data in conversion due to
225         # key collisions after stringification.  This can happen with keys like
226         # True and 'true' or 1 and '1', which collide in JSON.
227         nkeys = len(obj)
228         nkeys_collapsed = len(set(map(str, obj)))
229         if nkeys != nkeys_collapsed:
230             raise ValueError('dict can not be safely converted to JSON: '
231                              'key collision would lead to dropped values')
232         # If all OK, proceed by making the new dict that will be json-safe
233         out = {}
234         for k,v in iteritems(obj):
235             out[str(k)] = json_clean(v)
236         return out
237 
238     # If we get here, we don't know how to handle the object, so we just get
239     # its repr and return that.  This will catch lambdas, open sockets, class
240     # objects, and any other complicated contraption that json can't encode
241     return repr(obj)
242
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/IPython/utils/jsonutil.py b/IPython/utils/jsonutil.py
--- a/IPython/utils/jsonutil.py
+++ b/IPython/utils/jsonutil.py
@@ -194,9 +194,8 @@
     >>> json_clean(True)
     True
     """
-    # types that are 'atomic' and ok in json as-is.  bool doesn't need to be
-    # listed explicitly because bools pass as int instances
-    atomic_ok = (unicode_type, int, type(None))
+    # types that are 'atomic' and ok in json as-is.
+    atomic_ok = (unicode_type, type(None))
 
     # containers that we need to convert into lists
     container_to_list = (tuple, set, types.GeneratorType)
@@ -205,7 +204,14 @@
         # cast out-of-range floats to their reprs
         if math.isnan(obj) or math.isinf(obj):
             return repr(obj)
-        return obj
+        return float(obj)
+    
+    if isinstance(obj, int):
+        # cast int to int, in case subclasses override __str__ (e.g. boost enum, #4598)
+        if isinstance(obj, bool):
+            # bools are ints, but we don't want to cast them to 0,1
+            return obj
+        return int(obj)
 
     if isinstance(obj, atomic_ok):
         return obj

{"golden_diff": "diff --git a/IPython/utils/jsonutil.py b/IPython/utils/jsonutil.py\n--- a/IPython/utils/jsonutil.py\n+++ b/IPython/utils/jsonutil.py\n@@ -194,9 +194,8 @@\n     >>> json_clean(True)\n     True\n     \"\"\"\n-    # types that are 'atomic' and ok in json as-is.  bool doesn't need to be\n-    # listed explicitly because bools pass as int instances\n-    atomic_ok = (unicode_type, int, type(None))\n+    # types that are 'atomic' and ok in json as-is.\n+    atomic_ok = (unicode_type, type(None))\n \n     # containers that we need to convert into lists\n     container_to_list = (tuple, set, types.GeneratorType)\n@@ -205,7 +204,14 @@\n         # cast out-of-range floats to their reprs\n         if math.isnan(obj) or math.isinf(obj):\n             return repr(obj)\n-        return obj\n+        return float(obj)\n+    \n+    if isinstance(obj, int):\n+        # cast int to int, in case subclasses override __str__ (e.g. boost enum, #4598)\n+        if isinstance(obj, bool):\n+            # bools are ints, but we don't want to cast them to 0,1\n+            return obj\n+        return int(obj)\n \n     if isinstance(obj, atomic_ok):\n         return obj\n", "issue": "Qtconsole docstring pop-up fails on method containing defaulted enum argument\n[We've found](http://trac.mantidproject.org/mantid/ticket/8422) that an error is generated in the (admittedly rare) situation where a C++ enum is exposed to Python via boost python and included in a python function as a default to an argument. This is seen in IPython 1.1 and the current tip of master.\n\nHere's the simplest example I could come up with, though it does still require compiling C++ and linking to boost python. In a C++ file:\n\n``` c++\n#include <boost/python.hpp>\n\nenum MyEnum\n{\n  Red,\n  Yellow,\n  Blue\n};\n\nBOOST_PYTHON_MODULE(enum_test)\n{\n  using namespace boost::python;\n\n  enum_<MyEnum>(\"MyEnum\")\n    .value(\"Red\", Red)\n    .value(\"Yellow\", Yellow)\n    .value(\"Blue\", Blue);\n}\n```\n\nThis should be compiled to a shared library with something like `gcc -fPIC -I /usr/include/python2.6 -shared -o enum_test.so enum_test.cpp -lboost_python`\n\nThen, in the IPython qtconsole enter:\n\n```\nIn [1]: import enum_test\n\nIn [2]: def MyFunc(color = enum_test.MyEnum.Red):\n   ...:     pass\n   ...: \n\nIn [3]: MyFunc(\n```\n\nOn typing the opening parenthesis a stack trace will appear that culminates in:\n\n```\n  File \"/usr/lib/python2.6/site-packages/IPython/kernel/zmq/session.py\", line 83, in <lambda>\n    json_unpacker = lambda s: extract_dates(jsonapi.loads(s))\n  File \"/usr/lib64/python2.6/site-packages/zmq/utils/jsonapi.py\", line 81, in loads\n    return jsonmod.loads(s, **kwargs)\n  File \"/usr/lib64/python2.6/site-packages/simplejson/__init__.py\", line 307, in loads\n    return _default_decoder.decode(s)\n  File \"/usr/lib64/python2.6/site-packages/simplejson/decoder.py\", line 335, in decode\n    obj, end = self.raw_decode(s, idx=_w(s, 0).end())\n  File \"/usr/lib64/python2.6/site-packages/simplejson/decoder.py\", line 353, in raw_decode\n    raise ValueError(\"No JSON object could be decoded\")\n```\n\nThe string that's going into the raw_decode function of decoder.py and leads to the exception is:\n\n```\n{\"base_class\":\"<type 'function'>\",\"init_definition\":null,\"type_name\":\"function\",\"name\":\"MyFunc\",\"isclass\":null,\"namespace\":\"Interactive\",\"isalias\":false,\"init_docstring\":null,\"argspec\":{\"args\":[\"color\"],\"varkw\":null,\"defaults\":[Red],\"varargs\":null},\"source\":null,\"length\":null,\"call_def\":null,\"call_docstring\":null,\"file\":\"/home/enumproblem/<ipython-input-2-b6e10dea3e06>\",\"string_form\":\"<function MyFunc at 0x16a8de8>\",\"found\":true,\"class_docstring\":null,\"definition\":\"\\u001b[0mMyFunc\\u001b[0m\\u001b[1;33m(\\u001b[0m\\u001b[0mcolor\\u001b[0m\\u001b[1;33m=\\u001b[0m\\u001b[0menum_test\\u001b[0m\\u001b[1;33m.\\u001b[0m\\u001b[0mMyEnum\\u001b[0m\\u001b[1;33m.\\u001b[0m\\u001b[0mRed\\u001b[0m\\u001b[1;33m)\\u001b[0m\\u001b[1;33m\\u001b[0m\\u001b[0m\\n\",\"docstring\":\"<no docstring>\",\"ismagic\":false}\n```\n\n", "before_files": [{"content": "\"\"\"Utilities to manipulate JSON objects.\n\"\"\"\n#-----------------------------------------------------------------------------\n#  Copyright (C) 2010-2011  The IPython Development Team\n#\n#  Distributed under the terms of the BSD License.  The full license is in\n#  the file COPYING.txt, distributed as part of this software.\n#-----------------------------------------------------------------------------\n\n#-----------------------------------------------------------------------------\n# Imports\n#-----------------------------------------------------------------------------\n# stdlib\nimport math\nimport re\nimport types\nfrom datetime import datetime\n\ntry:\n    # base64.encodestring is deprecated in Python 3.x\n    from base64 import encodebytes\nexcept ImportError:\n    # Python 2.x\n    from base64 import encodestring as encodebytes\n\nfrom IPython.utils import py3compat\nfrom IPython.utils.py3compat import string_types, unicode_type, iteritems\nfrom IPython.utils.encoding import DEFAULT_ENCODING\nnext_attr_name = '__next__' if py3compat.PY3 else 'next'\n\n#-----------------------------------------------------------------------------\n# Globals and constants\n#-----------------------------------------------------------------------------\n\n# timestamp formats\nISO8601 = \"%Y-%m-%dT%H:%M:%S.%f\"\nISO8601_PAT=re.compile(r\"^(\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{1,6})Z?([\\+\\-]\\d{2}:?\\d{2})?$\")\n\n#-----------------------------------------------------------------------------\n# Classes and functions\n#-----------------------------------------------------------------------------\n\ndef rekey(dikt):\n    \"\"\"Rekey a dict that has been forced to use str keys where there should be\n    ints by json.\"\"\"\n    for k in dikt:\n        if isinstance(k, string_types):\n            ik=fk=None\n            try:\n                ik = int(k)\n            except ValueError:\n                try:\n                    fk = float(k)\n                except ValueError:\n                    continue\n            if ik is not None:\n                nk = ik\n            else:\n                nk = fk\n            if nk in dikt:\n                raise KeyError(\"already have key %r\"%nk)\n            dikt[nk] = dikt.pop(k)\n    return dikt\n\ndef parse_date(s):\n    \"\"\"parse an ISO8601 date string\n    \n    If it is None or not a valid ISO8601 timestamp,\n    it will be returned unmodified.\n    Otherwise, it will return a datetime object.\n    \"\"\"\n    if s is None:\n        return s\n    m = ISO8601_PAT.match(s)\n    if m:\n        # FIXME: add actual timezone support\n        # this just drops the timezone info\n        notz = m.groups()[0]\n        return datetime.strptime(notz, ISO8601)\n    return s\n\ndef extract_dates(obj):\n    \"\"\"extract ISO8601 dates from unpacked JSON\"\"\"\n    if isinstance(obj, dict):\n        new_obj = {} # don't clobber\n        for k,v in iteritems(obj):\n            new_obj[k] = extract_dates(v)\n        obj = new_obj\n    elif isinstance(obj, (list, tuple)):\n        obj = [ extract_dates(o) for o in obj ]\n    elif isinstance(obj, string_types):\n        obj = parse_date(obj)\n    return obj\n\ndef squash_dates(obj):\n    \"\"\"squash datetime objects into ISO8601 strings\"\"\"\n    if isinstance(obj, dict):\n        obj = dict(obj) # don't clobber\n        for k,v in iteritems(obj):\n            obj[k] = squash_dates(v)\n    elif isinstance(obj, (list, tuple)):\n        obj = [ squash_dates(o) for o in obj ]\n    elif isinstance(obj, datetime):\n        obj = obj.isoformat()\n    return obj\n\ndef date_default(obj):\n    \"\"\"default function for packing datetime objects in JSON.\"\"\"\n    if isinstance(obj, datetime):\n        return obj.isoformat()\n    else:\n        raise TypeError(\"%r is not JSON serializable\"%obj)\n\n\n# constants for identifying png/jpeg data\nPNG = b'\\x89PNG\\r\\n\\x1a\\n'\n# front of PNG base64-encoded\nPNG64 = b'iVBORw0KG'\nJPEG = b'\\xff\\xd8'\n# front of JPEG base64-encoded\nJPEG64 = b'/9'\n\ndef encode_images(format_dict):\n    \"\"\"b64-encodes images in a displaypub format dict\n\n    Perhaps this should be handled in json_clean itself?\n\n    Parameters\n    ----------\n\n    format_dict : dict\n        A dictionary of display data keyed by mime-type\n\n    Returns\n    -------\n\n    format_dict : dict\n        A copy of the same dictionary,\n        but binary image data ('image/png' or 'image/jpeg')\n        is base64-encoded.\n\n    \"\"\"\n    encoded = format_dict.copy()\n\n    pngdata = format_dict.get('image/png')\n    if isinstance(pngdata, bytes):\n        # make sure we don't double-encode\n        if not pngdata.startswith(PNG64):\n            pngdata = encodebytes(pngdata)\n        encoded['image/png'] = pngdata.decode('ascii')\n\n    jpegdata = format_dict.get('image/jpeg')\n    if isinstance(jpegdata, bytes):\n        # make sure we don't double-encode\n        if not jpegdata.startswith(JPEG64):\n            jpegdata = encodebytes(jpegdata)\n        encoded['image/jpeg'] = jpegdata.decode('ascii')\n\n    return encoded\n\n\ndef json_clean(obj):\n    \"\"\"Clean an object to ensure it's safe to encode in JSON.\n\n    Atomic, immutable objects are returned unmodified.  Sets and tuples are\n    converted to lists, lists are copied and dicts are also copied.\n\n    Note: dicts whose keys could cause collisions upon encoding (such as a dict\n    with both the number 1 and the string '1' as keys) will cause a ValueError\n    to be raised.\n\n    Parameters\n    ----------\n    obj : any python object\n\n    Returns\n    -------\n    out : object\n\n      A version of the input which will not cause an encoding error when\n      encoded as JSON.  Note that this function does not *encode* its inputs,\n      it simply sanitizes it so that there will be no encoding errors later.\n\n    Examples\n    --------\n    >>> json_clean(4)\n    4\n    >>> json_clean(list(range(10)))\n    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\n    >>> sorted(json_clean(dict(x=1, y=2)).items())\n    [('x', 1), ('y', 2)]\n    >>> sorted(json_clean(dict(x=1, y=2, z=[1,2,3])).items())\n    [('x', 1), ('y', 2), ('z', [1, 2, 3])]\n    >>> json_clean(True)\n    True\n    \"\"\"\n    # types that are 'atomic' and ok in json as-is.  bool doesn't need to be\n    # listed explicitly because bools pass as int instances\n    atomic_ok = (unicode_type, int, type(None))\n\n    # containers that we need to convert into lists\n    container_to_list = (tuple, set, types.GeneratorType)\n\n    if isinstance(obj, float):\n        # cast out-of-range floats to their reprs\n        if math.isnan(obj) or math.isinf(obj):\n            return repr(obj)\n        return obj\n\n    if isinstance(obj, atomic_ok):\n        return obj\n\n    if isinstance(obj, bytes):\n        return obj.decode(DEFAULT_ENCODING, 'replace')\n\n    if isinstance(obj, container_to_list) or (\n        hasattr(obj, '__iter__') and hasattr(obj, next_attr_name)):\n        obj = list(obj)\n\n    if isinstance(obj, list):\n        return [json_clean(x) for x in obj]\n\n    if isinstance(obj, dict):\n        # First, validate that the dict won't lose data in conversion due to\n        # key collisions after stringification.  This can happen with keys like\n        # True and 'true' or 1 and '1', which collide in JSON.\n        nkeys = len(obj)\n        nkeys_collapsed = len(set(map(str, obj)))\n        if nkeys != nkeys_collapsed:\n            raise ValueError('dict can not be safely converted to JSON: '\n                             'key collision would lead to dropped values')\n        # If all OK, proceed by making the new dict that will be json-safe\n        out = {}\n        for k,v in iteritems(obj):\n            out[str(k)] = json_clean(v)\n        return out\n\n    # If we get here, we don't know how to handle the object, so we just get\n    # its repr and return that.  This will catch lambdas, open sockets, class\n    # objects, and any other complicated contraption that json can't encode\n    return repr(obj)\n", "path": "IPython/utils/jsonutil.py"}], "after_files": [{"content": "\"\"\"Utilities to manipulate JSON objects.\n\"\"\"\n#-----------------------------------------------------------------------------\n#  Copyright (C) 2010-2011  The IPython Development Team\n#\n#  Distributed under the terms of the BSD License.  The full license is in\n#  the file COPYING.txt, distributed as part of this software.\n#-----------------------------------------------------------------------------\n\n#-----------------------------------------------------------------------------\n# Imports\n#-----------------------------------------------------------------------------\n# stdlib\nimport math\nimport re\nimport types\nfrom datetime import datetime\n\ntry:\n    # base64.encodestring is deprecated in Python 3.x\n    from base64 import encodebytes\nexcept ImportError:\n    # Python 2.x\n    from base64 import encodestring as encodebytes\n\nfrom IPython.utils import py3compat\nfrom IPython.utils.py3compat import string_types, unicode_type, iteritems\nfrom IPython.utils.encoding import DEFAULT_ENCODING\nnext_attr_name = '__next__' if py3compat.PY3 else 'next'\n\n#-----------------------------------------------------------------------------\n# Globals and constants\n#-----------------------------------------------------------------------------\n\n# timestamp formats\nISO8601 = \"%Y-%m-%dT%H:%M:%S.%f\"\nISO8601_PAT=re.compile(r\"^(\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{1,6})Z?([\\+\\-]\\d{2}:?\\d{2})?$\")\n\n#-----------------------------------------------------------------------------\n# Classes and functions\n#-----------------------------------------------------------------------------\n\ndef rekey(dikt):\n    \"\"\"Rekey a dict that has been forced to use str keys where there should be\n    ints by json.\"\"\"\n    for k in dikt:\n        if isinstance(k, string_types):\n            ik=fk=None\n            try:\n                ik = int(k)\n            except ValueError:\n                try:\n                    fk = float(k)\n                except ValueError:\n                    continue\n            if ik is not None:\n                nk = ik\n            else:\n                nk = fk\n            if nk in dikt:\n                raise KeyError(\"already have key %r\"%nk)\n            dikt[nk] = dikt.pop(k)\n    return dikt\n\ndef parse_date(s):\n    \"\"\"parse an ISO8601 date string\n    \n    If it is None or not a valid ISO8601 timestamp,\n    it will be returned unmodified.\n    Otherwise, it will return a datetime object.\n    \"\"\"\n    if s is None:\n        return s\n    m = ISO8601_PAT.match(s)\n    if m:\n        # FIXME: add actual timezone support\n        # this just drops the timezone info\n        notz = m.groups()[0]\n        return datetime.strptime(notz, ISO8601)\n    return s\n\ndef extract_dates(obj):\n    \"\"\"extract ISO8601 dates from unpacked JSON\"\"\"\n    if isinstance(obj, dict):\n        new_obj = {} # don't clobber\n        for k,v in iteritems(obj):\n            new_obj[k] = extract_dates(v)\n        obj = new_obj\n    elif isinstance(obj, (list, tuple)):\n        obj = [ extract_dates(o) for o in obj ]\n    elif isinstance(obj, string_types):\n        obj = parse_date(obj)\n    return obj\n\ndef squash_dates(obj):\n    \"\"\"squash datetime objects into ISO8601 strings\"\"\"\n    if isinstance(obj, dict):\n        obj = dict(obj) # don't clobber\n        for k,v in iteritems(obj):\n            obj[k] = squash_dates(v)\n    elif isinstance(obj, (list, tuple)):\n        obj = [ squash_dates(o) for o in obj ]\n    elif isinstance(obj, datetime):\n        obj = obj.isoformat()\n    return obj\n\ndef date_default(obj):\n    \"\"\"default function for packing datetime objects in JSON.\"\"\"\n    if isinstance(obj, datetime):\n        return obj.isoformat()\n    else:\n        raise TypeError(\"%r is not JSON serializable\"%obj)\n\n\n# constants for identifying png/jpeg data\nPNG = b'\\x89PNG\\r\\n\\x1a\\n'\n# front of PNG base64-encoded\nPNG64 = b'iVBORw0KG'\nJPEG = b'\\xff\\xd8'\n# front of JPEG base64-encoded\nJPEG64 = b'/9'\n\ndef encode_images(format_dict):\n    \"\"\"b64-encodes images in a displaypub format dict\n\n    Perhaps this should be handled in json_clean itself?\n\n    Parameters\n    ----------\n\n    format_dict : dict\n        A dictionary of display data keyed by mime-type\n\n    Returns\n    -------\n\n    format_dict : dict\n        A copy of the same dictionary,\n        but binary image data ('image/png' or 'image/jpeg')\n        is base64-encoded.\n\n    \"\"\"\n    encoded = format_dict.copy()\n\n    pngdata = format_dict.get('image/png')\n    if isinstance(pngdata, bytes):\n        # make sure we don't double-encode\n        if not pngdata.startswith(PNG64):\n            pngdata = encodebytes(pngdata)\n        encoded['image/png'] = pngdata.decode('ascii')\n\n    jpegdata = format_dict.get('image/jpeg')\n    if isinstance(jpegdata, bytes):\n        # make sure we don't double-encode\n        if not jpegdata.startswith(JPEG64):\n            jpegdata = encodebytes(jpegdata)\n        encoded['image/jpeg'] = jpegdata.decode('ascii')\n\n    return encoded\n\n\ndef json_clean(obj):\n    \"\"\"Clean an object to ensure it's safe to encode in JSON.\n\n    Atomic, immutable objects are returned unmodified.  Sets and tuples are\n    converted to lists, lists are copied and dicts are also copied.\n\n    Note: dicts whose keys could cause collisions upon encoding (such as a dict\n    with both the number 1 and the string '1' as keys) will cause a ValueError\n    to be raised.\n\n    Parameters\n    ----------\n    obj : any python object\n\n    Returns\n    -------\n    out : object\n\n      A version of the input which will not cause an encoding error when\n      encoded as JSON.  Note that this function does not *encode* its inputs,\n      it simply sanitizes it so that there will be no encoding errors later.\n\n    Examples\n    --------\n    >>> json_clean(4)\n    4\n    >>> json_clean(list(range(10)))\n    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\n    >>> sorted(json_clean(dict(x=1, y=2)).items())\n    [('x', 1), ('y', 2)]\n    >>> sorted(json_clean(dict(x=1, y=2, z=[1,2,3])).items())\n    [('x', 1), ('y', 2), ('z', [1, 2, 3])]\n    >>> json_clean(True)\n    True\n    \"\"\"\n    # types that are 'atomic' and ok in json as-is.\n    atomic_ok = (unicode_type, type(None))\n\n    # containers that we need to convert into lists\n    container_to_list = (tuple, set, types.GeneratorType)\n\n    if isinstance(obj, float):\n        # cast out-of-range floats to their reprs\n        if math.isnan(obj) or math.isinf(obj):\n            return repr(obj)\n        return float(obj)\n    \n    if isinstance(obj, int):\n        # cast int to int, in case subclasses override __str__ (e.g. boost enum, #4598)\n        if isinstance(obj, bool):\n            # bools are ints, but we don't want to cast them to 0,1\n            return obj\n        return int(obj)\n\n    if isinstance(obj, atomic_ok):\n        return obj\n\n    if isinstance(obj, bytes):\n        return obj.decode(DEFAULT_ENCODING, 'replace')\n\n    if isinstance(obj, container_to_list) or (\n        hasattr(obj, '__iter__') and hasattr(obj, next_attr_name)):\n        obj = list(obj)\n\n    if isinstance(obj, list):\n        return [json_clean(x) for x in obj]\n\n    if isinstance(obj, dict):\n        # First, validate that the dict won't lose data in conversion due to\n        # key collisions after stringification.  This can happen with keys like\n        # True and 'true' or 1 and '1', which collide in JSON.\n        nkeys = len(obj)\n        nkeys_collapsed = len(set(map(str, obj)))\n        if nkeys != nkeys_collapsed:\n            raise ValueError('dict can not be safely converted to JSON: '\n                             'key collision would lead to dropped values')\n        # If all OK, proceed by making the new dict that will be json-safe\n        out = {}\n        for k,v in iteritems(obj):\n            out[str(k)] = json_clean(v)\n        return out\n\n    # If we get here, we don't know how to handle the object, so we just get\n    # its repr and return that.  This will catch lambdas, open sockets, class\n    # objects, and any other complicated contraption that json can't encode\n    return repr(obj)\n", "path": "IPython/utils/jsonutil.py"}]}