Skip to content

Commit 5b2a485

Browse files
Merge pull request #698 from martinRenou/fix_json_default
Fix json_default so that it's closer to what ipykernel had before
2 parents 7ef616e + 1131e31 commit 5b2a485

File tree

2 files changed

+65
-4
lines changed

2 files changed

+65
-4
lines changed

jupyter_client/jsonutil.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
"""Utilities to manipulate JSON objects."""
22
# Copyright (c) Jupyter Development Team.
33
# Distributed under the terms of the Modified BSD License.
4+
import numbers
45
import re
56
import warnings
67
from binascii import b2a_base64
8+
from collections.abc import Iterable
79
from datetime import datetime
810
from typing import Optional
911
from typing import Union
@@ -106,7 +108,17 @@ def json_default(obj):
106108
if isinstance(obj, datetime):
107109
obj = _ensure_tzinfo(obj)
108110
return obj.isoformat().replace('+00:00', 'Z')
109-
elif isinstance(obj, bytes):
111+
112+
if isinstance(obj, bytes):
110113
return b2a_base64(obj).decode('ascii')
111-
else:
112-
raise TypeError("%r is not JSON serializable" % obj)
114+
115+
if isinstance(obj, Iterable):
116+
return list(obj)
117+
118+
if isinstance(obj, numbers.Integral):
119+
return int(obj)
120+
121+
if isinstance(obj, numbers.Real):
122+
return float(obj)
123+
124+
raise TypeError("%r is not JSON serializable" % obj)

jupyter_client/tests/test_jsonutil.py

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
# Distributed under the terms of the Modified BSD License.
44
import datetime
55
import json
6+
import numbers
67
from datetime import timedelta
78
from unittest import mock
89

@@ -16,6 +17,22 @@
1617
REFERENCE_DATETIME = datetime.datetime(2013, 7, 3, 16, 34, 52, 249482, tzlocal())
1718

1819

20+
class MyInt(object):
21+
def __int__(self):
22+
return 389
23+
24+
25+
numbers.Integral.register(MyInt)
26+
27+
28+
class MyFloat(object):
29+
def __float__(self):
30+
return 3.14
31+
32+
33+
numbers.Real.register(MyFloat)
34+
35+
1936
def test_extract_date_from_naive():
2037
ref = REFERENCE_DATETIME
2138
timestamp = "2013-07-03T16:34:52.249482"
@@ -65,7 +82,7 @@ def test_parse_ms_precision():
6582
assert isinstance(parsed, str)
6683

6784

68-
def test_json_default():
85+
def test_json_default_date():
6986
naive = datetime.datetime.now()
7087
local = tzoffset("Local", -8 * 3600)
7188
other = tzoffset("Other", 2 * 3600)
@@ -79,3 +96,35 @@ def test_json_default():
7996
for dt in extracted.values():
8097
assert isinstance(dt, datetime.datetime)
8198
assert dt.tzinfo is not None
99+
100+
101+
def test_json_default():
102+
# list of input/expected output. Use None for the expected output if it
103+
# can be the same as the input.
104+
pairs = [
105+
(1, None), # start with scalars
106+
(1.123, None),
107+
(1.0, None),
108+
('a', None),
109+
(True, None),
110+
(False, None),
111+
(None, None),
112+
# Containers
113+
([1, 2], None),
114+
((1, 2), [1, 2]),
115+
(set([1, 2]), [1, 2]),
116+
(dict(x=1), None),
117+
({'x': 1, 'y': [1, 2, 3], '1': 'int'}, None),
118+
# More exotic objects
119+
((x for x in range(3)), [0, 1, 2]),
120+
(iter([1, 2]), [1, 2]),
121+
(MyFloat(), 3.14),
122+
(MyInt(), 389),
123+
]
124+
125+
for val, jval in pairs:
126+
if jval is None:
127+
jval = val
128+
out = json.loads(json.dumps(val, default=jsonutil.json_default))
129+
# validate our cleanup
130+
assert out == jval

0 commit comments

Comments
 (0)