def test_json_encoder_numbers(self): self.encoder_test(0, '0') self.encoder_test(1, '1') self.encoder_test(-123456, '-123456') self.encoder_test(2.2, '2.2') self.encoder_test(1.2345678, '1.2345678') self.encoder_test(-1.23456e-123, '-1.23456e-123') # 9007199254740992 is the largest possible integer in JavaScript self.encoder_test(9007199254740992, '9007199254740992') # std module just returns a string representation for # all valid python numbers (even for non-jscript representable numbers) with assert_raises(ValueError, error_re='out of range'): self.encoder_test( 9007199254740992 + 1, None) with assert_raises(ValueError, error_re='out of range'): self.encoder_test(-9007199254740992 - 1, None) with assert_raises(ValueError, error_re='NaN is not supported'): self.encoder_test(float('NaN'), None) # std module does not support Decimals # note: Decimal(1.17) != Decimal("1.17"), for testing need initialization from a string self.encoder_test(Decimal("1.17"), '"1.17"', False, False) lst = [Decimal(str(random.random()*100000)) for _ in range(256)] out = '[{}]'.format(','.join('"{}"'.format(str(d)) for d in lst)) self.encoder_test(lst, out, False, False) # complex numbers are not JSON serializable with assert_raises(TypeError, error_re='not JSON serializable'): self.encoder_test(1+2j, None)
def test_json_default(self): class Foo: pass class Bar: pass class Spam: pass class Encoder(self.encoder): def default(self, obj): if isinstance(obj, Foo): return ['Foo', 'Foo'] if isinstance(obj, Spam): 1/0 return super().default(obj) assert Encoder().dumps([Foo(), Foo()]) == '[["Foo","Foo"],["Foo","Foo"]]' with assert_raises(TypeError, error_re='is not JSON seri'): Encoder().dumps(Bar()) # let's check that the exceptions propagate fine with assert_raises(ZeroDivisionError): Encoder().dumps([Spam()]) with assert_raises(ZeroDivisionError): Encoder().dumpb({Spam(): 1})
def test_json_encoder_dict(self): self.encoder_test({}, '{}') self.encoder_test({'foo':1, 'bar':2}, ('{"foo":1,"bar":2}', '{"bar":2,"foo":1}')) self.encoder_test({'foo':[1,2], 'bar':[3,4]}, ('{"foo":[1,2],"bar":[3,4]}', '{"bar":[3,4],"foo":[1,2]}')) # shold match std encoder, but conversion back converts to lists not tuples self.encoder_test({'foo':(1,2), 'bar':(3,4)}, ('{"foo":[1,2],"bar":[3,4]}', '{"bar":[3,4],"foo":[1,2]}'), False, True) # std encoder does not support sets self.encoder_test({'foo':{1,2}, 'bar':{3,4}}, ('{"foo":[1,2],"bar":[3,4]}', '{"bar":[3,4],"foo":[1,2]}'), False, False) # std encoder does nto support OrderedDicts d = {'banana': 3, 'apple':4, 'pear': 1, 'orange': 2} ordered_d = OrderedDict(sorted(d.items(), key=lambda t: t[0])) self.encoder_test( ordered_d, '{"apple":4,"banana":3,"orange":2,"pear":1}', False, False) # JSON spec does not support keys which are not a string or a number with assert_raises(TypeError, error_re='is not a valid dictionary key'): self.encoder_test({1:1}, '{1:1}') with assert_raises(TypeError, error_re='is not a valid dictionary key'): self.encoder_test({(1,2):'a'}, '{[1,2]:"a"}') class DerivedDict(dict): pass self.encoder_test(DerivedDict({'foo':1, 'bar':2}), ('{"foo":1,"bar":2}', '{"bar":2,"foo":1}'))
def test_json_default(self): class Foo: pass class Bar: pass class Spam: pass class Encoder(self.encoder): def default(self, obj): if isinstance(obj, Foo): return ['Foo', 'Foo'] if isinstance(obj, Spam): 1 / 0 return super().default(obj) assert Encoder().dumps([Foo(), Foo()]) == '[["Foo","Foo"],["Foo","Foo"]]' with assert_raises(TypeError, error_re='is not JSON seri'): Encoder().dumps(Bar()) # let's check that the exceptions propagate fine with assert_raises(ZeroDivisionError): Encoder().dumps([Spam()]) with assert_raises(ZeroDivisionError): Encoder().dumpb({Spam(): 1})
def test_json_encoder_custom_methods(self): class FooJson: def __mm_json__(self): return '{"foo":{"baz":"©","spam":"\x1b"}}' # none of these are suported by std encoder ex_result = '{"foo":{"baz":"\\u00a9","spam":"\\u001b"}}' self.encoder_test(FooJson(), ex_result, False, False) self.encoder_test({'bar': FooJson()}, '{"bar":' + ex_result + '}', False, False) class FooJsonb: def __mm_json__(self): return b'"foo"' # none of these are suported by std encoder self.encoder_test(FooJsonb(), '"foo"', False, False) self.encoder_test({'bar': FooJsonb()}, '{"bar":"foo"}', False, False) class FooJsonPlus: def __mm_json__(self): raise NotImplementedError def __mm_serialize__(self): return 'foo' # none of these are suported by std encoder self.encoder_test(FooJsonPlus(), '"foo"', False, False) self.encoder_test({'bar': FooJsonPlus()}, '{"bar":"foo"}', False, False) class Foo: def __mm_serialize__(self): return 'foo' # none of these are suported by std encoder self.encoder_test(Foo(), '"foo"', False, False) self.encoder_test({Foo(): 'bar'}, '{"foo":"bar"}', False, False) self.encoder_test({'bar': Foo()}, '{"bar":"foo"}', False, False) class FooNotImpl(str): def __mm_serialize__(self): raise NotImplementedError # none of these are suported by std encoder self.encoder_test(FooNotImpl('foo'), '"foo"', False, False) self.encoder_test({FooNotImpl('foo'): 'bar'}, '{"foo":"bar"}', False, False) self.encoder_test({'bar': FooNotImpl('foo')}, '{"bar":"foo"}', False, False) class Spam: def __mm_serialize__(self): 1 / 0 with assert_raises(ZeroDivisionError): self.dumps({'1': Spam()}) with assert_raises(ZeroDivisionError): self.dumps({Spam(): 1})
def test_json_encoder_numbers(self): self.encoder_test(0, '0') self.encoder_test(1, '1') self.encoder_test(-123456, '-123456') self.encoder_test(2.2, '2.2') self.encoder_test(1.2345678, '1.2345678') self.encoder_test(-1.23456e-123, '-1.23456e-123') # 9007199254740992 is the largest possible integer in JavaScript self.encoder_test(9007199254740992, '9007199254740992') # std module just returns a string representation for # all valid python numbers (even for non-jscript representable numbers) with assert_raises(ValueError, error_re='out of range'): self.encoder_test(9007199254740992 + 1, None) with assert_raises(ValueError, error_re='out of range'): self.encoder_test(-9007199254740992 - 1, None) with assert_raises(ValueError, error_re='NaN is not supported'): self.encoder_test(float('NaN'), None) # std module does not support Decimals # note: Decimal(1.17) != Decimal("1.17"), for testing need initialization from a string self.encoder_test(Decimal("1.17"), '"1.17"', False, False) lst = [Decimal(str(random.random() * 100000)) for _ in range(256)] out = '[{}]'.format(','.join('"{}"'.format(str(d)) for d in lst)) self.encoder_test(lst, out, False, False) # complex numbers are not JSON serializable with assert_raises(TypeError, error_re='not JSON serializable'): self.encoder_test(1 + 2j, None)
def test_json_encoder_custom_methods(self): class FooJson: def __mm_json__(self): return '{"foo":{"baz":"©","spam":"\x1b"}}' # none of these are suported by std encoder ex_result = '{"foo":{"baz":"\\u00a9","spam":"\\u001b"}}' self.encoder_test(FooJson(), ex_result, False, False) self.encoder_test({'bar':FooJson()}, '{"bar":' + ex_result + '}', False, False) class FooJsonb: def __mm_json__(self): return b'"foo"' # none of these are suported by std encoder self.encoder_test(FooJsonb(), '"foo"', False, False) self.encoder_test({'bar':FooJsonb()}, '{"bar":"foo"}', False, False) class FooJsonPlus: def __mm_json__(self): raise NotImplementedError def __mm_serialize__(self): return 'foo' # none of these are suported by std encoder self.encoder_test(FooJsonPlus(), '"foo"', False, False) self.encoder_test({'bar':FooJsonPlus()}, '{"bar":"foo"}', False, False) class Foo: def __mm_serialize__(self): return 'foo' # none of these are suported by std encoder self.encoder_test(Foo(), '"foo"', False, False) self.encoder_test({Foo():'bar'}, '{"foo":"bar"}', False, False) self.encoder_test({'bar':Foo()}, '{"bar":"foo"}', False, False) class FooNotImpl(str): def __mm_serialize__(self): raise NotImplementedError # none of these are suported by std encoder self.encoder_test(FooNotImpl('foo'), '"foo"', False, False) self.encoder_test({FooNotImpl('foo'):'bar'}, '{"foo":"bar"}', False, False) self.encoder_test({'bar':FooNotImpl('foo')}, '{"bar":"foo"}', False, False) class Spam: def __mm_serialize__(self): 1/0 with assert_raises(ZeroDivisionError): self.dumps({'1': Spam()}) with assert_raises(ZeroDivisionError): self.dumps({Spam(): 1})
def test_json_encoder_dict(self): self.encoder_test({}, '{}') self.encoder_test({ 'foo': 1, 'bar': 2 }, ('{"foo":1,"bar":2}', '{"bar":2,"foo":1}')) self.encoder_test({ 'foo': [1, 2], 'bar': [3, 4] }, ('{"foo":[1,2],"bar":[3,4]}', '{"bar":[3,4],"foo":[1,2]}')) # shold match std encoder, but conversion back converts to lists not tuples self.encoder_test({ 'foo': (1, 2), 'bar': (3, 4) }, ('{"foo":[1,2],"bar":[3,4]}', '{"bar":[3,4],"foo":[1,2]}'), False, True) # std encoder does not support sets self.encoder_test({ 'foo': {1, 2}, 'bar': {3, 4} }, ('{"foo":[1,2],"bar":[3,4]}', '{"bar":[3,4],"foo":[1,2]}'), False, False) # std encoder does nto support OrderedDicts d = {'banana': 3, 'apple': 4, 'pear': 1, 'orange': 2} ordered_d = OrderedDict(sorted(d.items(), key=lambda t: t[0])) self.encoder_test(ordered_d, '{"apple":4,"banana":3,"orange":2,"pear":1}', False, False) # JSON spec does not support keys which are not a string or a number with assert_raises(TypeError, error_re='is not a valid dictionary key'): self.encoder_test({1: 1}, '{1:1}') with assert_raises(TypeError, error_re='is not a valid dictionary key'): self.encoder_test({(1, 2): 'a'}, '{[1,2]:"a"}') class DerivedDict(dict): pass self.encoder_test(DerivedDict({ 'foo': 1, 'bar': 2 }), ('{"foo":1,"bar":2}', '{"bar":2,"foo":1}'))
def test_json_encoder_lists(self): self.encoder_test([], '[]') self.encoder_test(['abc', True], '["abc",true]') self.encoder_test(['abc', [], [True]], '["abc",[],[true]]') # a tuple is converted to the same string as a "similar" list, so no conversion back # (but should match the default encoder) self.encoder_test((), '[]', False, True) self.encoder_test((1, ), '[1]', False, True) self.encoder_test((1, 2, [1, 2, 3]), '[1,2,[1,2,3]]', False, True) self.encoder_test(('a', 'b', ('c', 1)), '["a","b",["c",1]]', False, True) self.encoder_test((99, 100, 999, 1000, 9999, 10000, 99999, 100000), '[99,100,999,1000,9999,10000,99999,100000]', False, True) # std module does not support sets self.encoder_test({1, 2, 3}, '[1,2,3]', False, False) # test max recursion level checks with assert_raises( ValueError, error_re='Exceeded maximum allowed recursion level'): self.dumps([[[1, 2], 3], 4], max_nested_level=1) with assert_raises( ValueError, error_re='Exceeded maximum allowed recursion level'): self.dumps([[[1, 2], 3], 4], max_nested_level=2) # this should work assert (self.dumps([[[1, 2], 3], 4], max_nested_level=3) == '[[[1,2],3],4]') # create infinite recursion a = [1] a[0] = a with assert_raises( ValueError, error_re='Exceeded maximum allowed recursion level'): self.dumps(a) # by design type "bytes" is not serializable and should raise a TypeError with assert_raises(TypeError, error_re='not JSON serializable'): self.encoder_test(bytes([1, 2, 3]), None) # by design type "bytearray" is not serializable and should raise a TypeError with assert_raises(TypeError, error_re='not JSON serializable'): self.encoder_test(bytearray([1, 2, 3]), '[1,2,3]', False, False)
def test_json_encoder_lists(self): self.encoder_test([], '[]') self.encoder_test(['abc',True], '["abc",true]') self.encoder_test(['abc',[],[True]], '["abc",[],[true]]') # a tuple is converted to the same string as a "similar" list, so no conversion back # (but should match the default encoder) self.encoder_test((), '[]', False, True) self.encoder_test((1,), '[1]', False, True) self.encoder_test((1,2,[1,2,3]), '[1,2,[1,2,3]]', False, True) self.encoder_test(('a','b',('c',1)), '["a","b",["c",1]]', False, True) self.encoder_test((99,100,999,1000,9999,10000,99999,100000), '[99,100,999,1000,9999,10000,99999,100000]', False, True) # std module does not support sets self.encoder_test({1,2,3}, '[1,2,3]', False, False) # test max recursion level checks with assert_raises(ValueError, error_re='Exceeded maximum allowed recursion level'): self.dumps([[[1,2],3],4], max_nested_level=1) with assert_raises(ValueError, error_re='Exceeded maximum allowed recursion level'): self.dumps([[[1,2],3],4], max_nested_level=2) # this should work assert(self.dumps([[[1,2],3],4], max_nested_level=3) == '[[[1,2],3],4]') # create infinite recursion a=[1] a[0]=a with assert_raises(ValueError, error_re='Exceeded maximum allowed recursion level'): self.dumps(a) # by design type "bytes" is not serializable and should raise a TypeError with assert_raises(TypeError, error_re='not JSON serializable'): self.encoder_test(bytes([1,2,3]), None) # by design type "bytearray" is not serializable and should raise a TypeError with assert_raises(TypeError, error_re='not JSON serializable'): self.encoder_test(bytearray([1,2,3]), '[1,2,3]', False, False)
def test_json_hook(self): class Spam: pass class Encoder(self.encoder): def encode_hook(self, obj): if isinstance(obj, list): return {str(i): el for i, el in enumerate(obj)} if isinstance(obj, int): return '*' + str(obj) if isinstance(obj, Spam): 1/0 return obj assert Encoder().dumps([1.11]) == '{"0":1.11}' assert Encoder().dumps([1]) == '{"0":"*1"}' with assert_raises(ZeroDivisionError): Encoder().dumps([Spam()])
def test_json_hook(self): class Spam: pass class Encoder(self.encoder): def encode_hook(self, obj): if isinstance(obj, list): return {str(i): el for i, el in enumerate(obj)} if isinstance(obj, int): return '*' + str(obj) if isinstance(obj, Spam): 1 / 0 return obj assert Encoder().dumps([1.11]) == '{"0":1.11}' assert Encoder().dumps([1]) == '{"0":"*1"}' with assert_raises(ZeroDivisionError): Encoder().dumps([Spam()])