|
|
"""Test suite for our JSON utilities.
|
|
|
"""
|
|
|
#-----------------------------------------------------------------------------
|
|
|
# Copyright (C) 2010-2011 The IPython Development Team
|
|
|
#
|
|
|
# Distributed under the terms of the BSD License. The full license is in
|
|
|
# the file COPYING.txt, distributed as part of this software.
|
|
|
#-----------------------------------------------------------------------------
|
|
|
|
|
|
#-----------------------------------------------------------------------------
|
|
|
# Imports
|
|
|
#-----------------------------------------------------------------------------
|
|
|
# stdlib
|
|
|
import datetime
|
|
|
import json
|
|
|
from base64 import decodestring
|
|
|
|
|
|
# third party
|
|
|
import nose.tools as nt
|
|
|
|
|
|
# our own
|
|
|
from IPython.utils import jsonutil, tz
|
|
|
from ..jsonutil import json_clean, encode_images
|
|
|
from ..py3compat import unicode_to_str, str_to_bytes, iteritems
|
|
|
|
|
|
#-----------------------------------------------------------------------------
|
|
|
# Test functions
|
|
|
#-----------------------------------------------------------------------------
|
|
|
class Int(int):
|
|
|
def __str__(self):
|
|
|
return 'Int(%i)' % self
|
|
|
|
|
|
def test():
|
|
|
# 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.0, None),
|
|
|
('a', None),
|
|
|
(True, None),
|
|
|
(False, None),
|
|
|
(None, None),
|
|
|
# complex numbers for now just go to strings, as otherwise they
|
|
|
# are unserializable
|
|
|
(1j, '1j'),
|
|
|
# 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]),
|
|
|
(Int(5), 5),
|
|
|
]
|
|
|
|
|
|
for val, jval in pairs:
|
|
|
if jval is None:
|
|
|
jval = val
|
|
|
out = json_clean(val)
|
|
|
# validate our cleanup
|
|
|
nt.assert_equal(out, jval)
|
|
|
# and ensure that what we return, indeed encodes cleanly
|
|
|
json.loads(json.dumps(out))
|
|
|
|
|
|
|
|
|
|
|
|
def test_encode_images():
|
|
|
# invalid data, but the header and footer are from real files
|
|
|
pngdata = b'\x89PNG\r\n\x1a\nblahblahnotactuallyvalidIEND\xaeB`\x82'
|
|
|
jpegdata = b'\xff\xd8\xff\xe0\x00\x10JFIFblahblahjpeg(\xa0\x0f\xff\xd9'
|
|
|
|
|
|
fmt = {
|
|
|
'image/png' : pngdata,
|
|
|
'image/jpeg' : jpegdata,
|
|
|
}
|
|
|
encoded = encode_images(fmt)
|
|
|
for key, value in iteritems(fmt):
|
|
|
# encoded has unicode, want bytes
|
|
|
decoded = decodestring(encoded[key].encode('ascii'))
|
|
|
nt.assert_equal(decoded, value)
|
|
|
encoded2 = encode_images(encoded)
|
|
|
nt.assert_equal(encoded, encoded2)
|
|
|
|
|
|
b64_str = {}
|
|
|
for key, encoded in iteritems(encoded):
|
|
|
b64_str[key] = unicode_to_str(encoded)
|
|
|
encoded3 = encode_images(b64_str)
|
|
|
nt.assert_equal(encoded3, b64_str)
|
|
|
for key, value in iteritems(fmt):
|
|
|
# encoded3 has str, want bytes
|
|
|
decoded = decodestring(str_to_bytes(encoded3[key]))
|
|
|
nt.assert_equal(decoded, value)
|
|
|
|
|
|
def test_lambda():
|
|
|
jc = json_clean(lambda : 1)
|
|
|
assert isinstance(jc, str)
|
|
|
assert '<lambda>' in jc
|
|
|
json.dumps(jc)
|
|
|
|
|
|
def test_extract_dates():
|
|
|
timestamps = [
|
|
|
'2013-07-03T16:34:52.249482',
|
|
|
'2013-07-03T16:34:52.249482Z',
|
|
|
'2013-07-03T16:34:52.249482Z-0800',
|
|
|
'2013-07-03T16:34:52.249482Z+0800',
|
|
|
'2013-07-03T16:34:52.249482Z+08:00',
|
|
|
'2013-07-03T16:34:52.249482Z-08:00',
|
|
|
'2013-07-03T16:34:52.249482-0800',
|
|
|
'2013-07-03T16:34:52.249482+0800',
|
|
|
'2013-07-03T16:34:52.249482+08:00',
|
|
|
'2013-07-03T16:34:52.249482-08:00',
|
|
|
]
|
|
|
extracted = jsonutil.extract_dates(timestamps)
|
|
|
ref = extracted[0]
|
|
|
for dt in extracted:
|
|
|
nt.assert_true(isinstance(dt, datetime.datetime))
|
|
|
nt.assert_equal(dt, ref)
|
|
|
|
|
|
def test_parse_ms_precision():
|
|
|
base = '2013-07-03T16:34:52.'
|
|
|
digits = '1234567890'
|
|
|
|
|
|
for i in range(len(digits)):
|
|
|
ts = base + digits[:i]
|
|
|
parsed = jsonutil.parse_date(ts)
|
|
|
if i >= 1 and i <= 6:
|
|
|
assert isinstance(parsed, datetime.datetime)
|
|
|
else:
|
|
|
assert isinstance(parsed, str)
|
|
|
|
|
|
def test_date_default():
|
|
|
data = dict(today=datetime.datetime.now(), utcnow=tz.utcnow())
|
|
|
jsondata = json.dumps(data, default=jsonutil.date_default)
|
|
|
nt.assert_in("+00", jsondata)
|
|
|
nt.assert_equal(jsondata.count("+00"), 1)
|
|
|
extracted = jsonutil.extract_dates(json.loads(jsondata))
|
|
|
for dt in extracted.values():
|
|
|
nt.assert_true(isinstance(dt, datetime.datetime))
|
|
|
|
|
|
def test_exception():
|
|
|
bad_dicts = [{1:'number', '1':'string'},
|
|
|
{True:'bool', 'True':'string'},
|
|
|
]
|
|
|
for d in bad_dicts:
|
|
|
nt.assert_raises(ValueError, json_clean, d)
|
|
|
|
|
|
|