Skip to content

Commit

Permalink
Fix json_default so that it's closer to what ipykernel had
Browse files Browse the repository at this point in the history
  • Loading branch information
martinRenou committed Sep 27, 2021
1 parent 1fcf65a commit a3c2293
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 4 deletions.
19 changes: 16 additions & 3 deletions jupyter_client/jsonutil.py
@@ -1,9 +1,12 @@
"""Utilities to manipulate JSON objects."""
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
import numbers
import re
import types
import warnings
from binascii import b2a_base64
from collections.abc import Iterable
from datetime import datetime
from typing import Optional
from typing import Union
Expand Down Expand Up @@ -106,7 +109,17 @@ def json_default(obj):
if isinstance(obj, datetime):
obj = _ensure_tzinfo(obj)
return obj.isoformat().replace('+00:00', 'Z')
elif isinstance(obj, bytes):

if isinstance(obj, bytes):
return b2a_base64(obj).decode('ascii')
else:
raise TypeError("%r is not JSON serializable" % obj)

if isinstance(obj, (set, types.GeneratorType, Iterable)):
return list(obj)

if isinstance(obj, numbers.Integral):
return int(obj)

if isinstance(obj, numbers.Real):
return float(obj)

raise TypeError("%r is not JSON serializable" % obj)
51 changes: 50 additions & 1 deletion jupyter_client/tests/test_jsonutil.py
Expand Up @@ -3,6 +3,7 @@
# Distributed under the terms of the Modified BSD License.
import datetime
import json
import numbers
from datetime import timedelta
from unittest import mock

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


class MyInt(object):
def __int__(self):
return 389


numbers.Integral.register(MyInt)


class MyFloat(object):
def __float__(self):
return 3.14


numbers.Real.register(MyFloat)


def test_extract_date_from_naive():
ref = REFERENCE_DATETIME
timestamp = "2013-07-03T16:34:52.249482"
Expand Down Expand Up @@ -65,7 +82,7 @@ def test_parse_ms_precision():
assert isinstance(parsed, str)


def test_json_default():
def test_json_default_date():
naive = datetime.datetime.now()
local = tzoffset("Local", -8 * 3600)
other = tzoffset("Other", 2 * 3600)
Expand All @@ -79,3 +96,35 @@ def test_json_default():
for dt in extracted.values():
assert isinstance(dt, datetime.datetime)
assert dt.tzinfo is not None


def test_json_default():
# list of input/expected output. Use None for the expected output if it
# can be the same as the input.
pairs = [
(1, None), # start with scalars
(1.123, None),
(1.0, None),
('a', None),
(True, None),
(False, None),
(None, None),
# Containers
([1, 2], None),
((1, 2), [1, 2]),
(set([1, 2]), [1, 2]),
(dict(x=1), None),
({'x': 1, 'y': [1, 2, 3], '1': 'int'}, None),
# More exotic objects
((x for x in range(3)), [0, 1, 2]),
(iter([1, 2]), [1, 2]),
(MyFloat(), 3.14),
(MyInt(), 389),
]

for val, jval in pairs:
if jval is None:
jval = val
out = json.loads(json.dumps(val, default=jsonutil.json_default))
# validate our cleanup
assert out == jval

0 comments on commit a3c2293

Please sign in to comment.