def test_endless_recursion(): # See #12051 def hook(o): """With ujson this will keep adding another list.""" return [o] with pytest.raises((RecursionError, OverflowError)): sd_ujson.dumps(5j, pre_encode_hook=hook)
def test_encode_dict_key_ref_counting(): import gc gc.collect() key = "key" data = {key: "abc"} ref_count = sys.getrefcount(key) sd_ujson.dumps(data) assert ref_count == sys.getrefcount(key)
def test_ints(): for num in [1, 1 << 32]: assert sd_ujson.dumps(num) == str(num) assert int(sd_ujson.dumps(num)) == num with pytest.raises(OverflowError): num = 1 << 64 assert sd_ujson.dumps(num) == str(num) assert int(sd_ujson.dumps(num)) == num
def test_encode_dict_values_ref_counting(): import gc gc.collect() value = ["abc"] data = {"1": value} ref_count = sys.getrefcount(value) sd_ujson.dumps(data) assert ref_count == sys.getrefcount(value)
def test_highly_nested_objects_encoding(): # See #12051 l, d = [], {} for x in range(100000): l, d = [l], {'k': d} with pytest.raises((RecursionError, OverflowError)): sd_ujson.dumps(l) with pytest.raises((RecursionError, OverflowError)): sd_ujson.dumps(d)
def test_allow_nan(): for val in (float('inf'), float('-inf'), float('nan')): out = sd_ujson.dumps([val]) if val == val: # inf assert sd_ujson.loads(out) == [val] else: # nan res = sd_ujson.loads(out) assert len(res) == 1 assert res[0] != res[0] with pytest.raises(ValueError): sd_ujson.dumps([val], allow_nan=False)
def test_encode_truefalse(): assert sd_ujson.dumps({ True: False, False: True }, sort_keys=True) == ('{"false": true, "true": false}') assert sd_ujson.dumps({ 2: 3.0, 4.0: 5, False: 1, 6: True }, sort_keys=True) == ('{"false": 1, "2": 3.0, "4.0": 5, "6": true}')
def test_multiple_files(): from .glossia import thorn from .glossia import talon # Test that we get the expected output when encoding a Decimal assert sd_ujson.dumps(Decimal(1)) == '"1"' # Test that we get the expected output when encoding a Fraction assert sd_ujson.dumps(Fraction(2, 3)) == '"2/3"' # Cleanup sd_ujson.encoders.unregister(Decimal) sd_ujson.encoders.unregister(Fraction)
def test_non_string_keys_dict(): import sys data = {'a': 1, (1, 2): 2} # TODO: if sys.version_info.major >= 3 and sys.version_info.minor > 6: match_string = "keys must be str, int, float, bool or None, not tuple" else: match_string = "keys must be a string" with pytest.raises(TypeError, match=match_string): sd_ujson.dumps(data)
def test_fraction_str(): # Create and register a custom encoder for Fraction that turns it into a str @sd_ujson.encoders.register(Fraction) def encode_fraction_str(obj): return str(obj) assert sd_ujson.dumps(Fraction(13, 10)) == '"13/10"' assert sd_ujson.dumps(Fraction(3, 4)) == '"3/4"' assert sd_ujson.dumps(Fraction(9, 11)) == '"9/11"' assert sd_ujson.dumps(Fraction(140, 144)) == '"35/36"' assert sd_ujson.dumps(Fraction(2, 7)) == '"2/7"' # Cleanup sd_ujson.encoders.unregister(Fraction)
def test_fraction_float(): # Create and register a custom encoder for Fraction that turns it into a float @sd_ujson.encoders.register(Fraction) def encode_fraction_float(obj): return float(obj) assert sd_ujson.dumps(Fraction(13, 10)) == "1.3" assert sd_ujson.dumps(Fraction(3, 4)) == "0.75" assert sd_ujson.dumps(Fraction(9, 11)) == "0.8181818181818182" assert sd_ujson.dumps(Fraction(140, 144)) == "0.9722222222222222" assert sd_ujson.dumps(Fraction(2, 7)) == "0.2857142857142857" # Cleanup sd_ujson.encoders.unregister(Fraction)
def test_dictrecursion(): x = {} x["test"] = x try: sd_ujson.dumps(x) except (ValueError, OverflowError): pass else: pytest.fail( "didn't raise ValueError or OverflowError on dict recursion") x = {} y = {"a": x, "b": x} # ensure that the marker is cleared sd_ujson.dumps(x)
def test_bytes_decode(): for encoding, bom in [ ('utf-8', codecs.BOM_UTF8), ('utf-16be', codecs.BOM_UTF16_BE), ('utf-16le', codecs.BOM_UTF16_LE), ('utf-32be', codecs.BOM_UTF32_BE), ('utf-32le', codecs.BOM_UTF32_LE), ]: data = ["a\xb5\u20ac\U0001d120"] encoded = sd_ujson.dumps(data).encode(encoding) print(data, encoded) assert sd_ujson.loads(bom + encoded) == data assert sd_ujson.loads(encoded) == data with pytest.raises(UnicodeDecodeError): sd_ujson.loads(b'["\x80"]') # RFC-7159 and ECMA-404 extend JSON to allow documents that # consist of only a string, which can present a special case # not covered by the encoding detection patterns specified in # RFC-4627 for utf-16-le (XX 00 XX 00). assert sd_ujson.loads('"\u2600"'.encode('utf-16-le')) == '\u2600' # Encoding detection for small (<4) bytes objects # is implemented as a special case. RFC-7159 and ECMA-404 # allow single codepoint JSON documents which are only two # bytes in utf-16 encodings w/o BOM. assert sd_ujson.loads(b'5\x00') == 5 assert sd_ujson.loads(b'\x007') == 7 assert sd_ujson.loads(b'57') == 57
def test_encode_mutated(): a = [object()] * 10 def crasher(obj): del a[-1] assert sd_ujson.dumps(a, default=crasher) == '[null, null, null, null, null]'
def test_encode_symbols(): s = "\u273f\u2661\u273f" # Рю┐РЎАРю┐ encoded = sd_ujson.dumps(s) encoded_json = sdjson.dumps(s) assert len(encoded) == len(s) * 6 + 2 # 6 characters + quotes assert encoded == encoded_json decoded = sd_ujson.loads(encoded) assert s == decoded # sd_ujson outputs an UTF-8 encoded str object encoded = sd_ujson.dumps(s, ensure_ascii=False) # json outputs an unicode object encoded_json = sdjson.dumps(s, ensure_ascii=False) assert len(encoded) == len(s) + 2 # original length + quotes assert encoded == encoded_json decoded = sd_ujson.loads(encoded) assert s == decoded
def test_time_float(): # Create and register a custom encoder for time that turns it into a float @sd_ujson.encoders.register(time) def encode_date_float(obj): return int(timedelta(hours=obj.hour, minutes=obj.minute, seconds=obj.second).total_seconds()) assert sd_ujson.dumps(time(9, 10, 11)) == "33011" # Cleanup sd_ujson.encoders.unregister(time)
def test_time_str(): # Create and register a custom encoder for time that turns it into a str @sd_ujson.encoders.register(time) def encode_time_str(obj): return f"{obj:%H:%M:%S}" assert sd_ujson.dumps(time(9, 10, 11)) == '"09:10:11"' # Cleanup sd_ujson.encoders.unregister(time)
def test_datetime_tuple(): # Create and register a custom encoder for datetime that turns it into a timetuple @sd_ujson.encoders.register(datetime) def encode_datetime_tuple(obj): return obj.timetuple() assert sd_ujson.dumps(datetime(1945, 5, 8, 19, 20)) == nospace('[1945, 5, 8, 19, 20, 0, 1, 128, -1]') # Cleanup sd_ujson.encoders.unregister(datetime)
def test_decimal_str(): # Create and register a custom encoder for Decimal that turns it into a str @sd_ujson.encoders.register(Decimal) def encode_decimal_str(obj): return str(obj) assert sd_ujson.dumps(Decimal(str(12.3456))) == '"12.3456"' # Cleanup sd_ujson.encoders.unregister(Decimal)
def test_time_tuple(): # Create and register a custom encoder for time that turns it into a timetuple @sd_ujson.encoders.register(time) def encode_time_tuple(obj): return obj.hour, obj.minute, obj.second assert sd_ujson.dumps(time(9, 10, 11)) == nospace('[9, 10, 11]') # Cleanup sd_ujson.encoders.unregister(time)
def test_datetime_float(): # Create and register a custom encoder for datetime that turns it into a float @sd_ujson.encoders.register(datetime) def encode_datetime_float(obj): return obj.timestamp() assert sd_ujson.dumps(datetime(1945, 5, 8, 19, 20, tzinfo=pytz.UTC)) == "-777876000.0" # Cleanup sd_ujson.encoders.unregister(datetime)
def test_parse_pass_2(): # from http://json.org/JSON_checker/test/pass2.json JSON = r''' [[[[[[[[[[[[[[[[[[["Not too deep"]]]]]]]]]]]]]]]]]]] ''' # test in/out equivalence and parsing res = sd_ujson.loads(JSON) out = sd_ujson.dumps(res) assert res == sd_ujson.loads(out)
def test_datetime_str(): # Create and register a custom encoder for datetime that turns it into a str @sd_ujson.encoders.register(datetime) def encode_datetime_str(obj): return f"{obj:%Y/%-m/%-d %H:%M}" assert sd_ujson.dumps(datetime(1945, 5, 8, 19, 20)) == '"1945/5/8 19:20"' # Cleanup sd_ujson.encoders.unregister(datetime)
def test_custom_class(): # Create and register the custom encoders # In this example we create three separate encoders even though all three classes # actually share a common subclass. In real usage they might not be. @sd_ujson.encoders.register(Character) def encode_character(obj): return dict(obj) @sd_ujson.encoders.register(Cheese) def encode_cheese(obj): return dict(obj) @sd_ujson.encoders.register(Shop) def encode_shop(obj): return dict(obj) # Create instances of classes runny_camembert = Cheese("Camembert", ["Very runny"]) shopkeeper = Character("Mr Wensleydale", "Michael Palin") customer = Character("The Customer", "John Cleese") cheese_shop = Shop( "The National Cheese Emporium", address="""12 Some Street Some Town England""", staff=[shopkeeper], customers=[customer], current_stock=[runny_camembert], music=False, dancing=False, ) expected_json = ( '{"name": "The National Cheese Emporium", "address": "12 Some Street\\n' 'Some Town\\nEngland", "open": true, "music": false, "dancing": false, ' '"staff": [{"name": "Mr Wensleydale", "actor": "Michael Palin", "armed": false}], ' '"customers": [{"name": "The Customer", "actor": "John Cleese", "armed": false}], ' '"current_stock": [{"name": "Camembert", "properties": ["Very runny"]}]}' ) with TemporaryDirectory() as tmpdir: tmpfile = pathlib.Path(tmpdir) / "output.json" with open(tmpfile, "w") as fp: sd_ujson.dump(cheese_shop, fp) with open(tmpfile, "r") as fp: assert fp.read() == nospace(expected_json) assert sd_ujson.dumps(cheese_shop) == nospace(expected_json) # Cleanup sd_ujson.unregister_encoder(Character) sd_ujson.unregister_encoder(Cheese) sd_ujson.unregister_encoder(Shop)
def test_timedelta_float(): # Create and register a custom encoder for timedelta that turns it into a float @sd_ujson.encoders.register(timedelta) def encode_timedelta_float(obj): return obj.total_seconds() start_date = datetime(1945, 5, 8, 19, 20).replace(tzinfo=pytz.utc) end_date = datetime(2020, 5, 8, 9, 0).replace(tzinfo=pytz.utc) delta = end_date - start_date assert sd_ujson.dumps(delta) == "2366804400.0" # Cleanup sd_ujson.encoders.unregister(timedelta)
def test_overloading(): # Create and register a custom encoder @sd_ujson.encoders.register(Decimal) def encoder_1(obj): return "Result from first registration" # Test that we get the expected output from the first encoder assert sd_ujson.dumps(Decimal(1)) == '"Result from first registration"' # Create and register a new custom encoder that overloads the previous one @sd_ujson.encoders.register(Decimal) def encoder_2(obj): return "Result from second registration" # Test that we get the expected output from the second encoder assert sd_ujson.dumps(Decimal(2)) == '"Result from second registration"' print(sd_ujson.encoders.registry.items()) # Cleanup sd_ujson.encoders.unregister(Decimal)
def test_separators(): h = [['blorpie'], ['whoops'], [], 'd-shtaeou', 'd-nthiouh', 'i-vhbjkhnth', { 'nifty': 87 }, { 'field': 'yes', 'morefield': False }] expect = textwrap.dedent("""\ [ [ "blorpie" ] , [ "whoops" ] , [] , "d-shtaeou" , "d-nthiouh" , "i-vhbjkhnth" , { "nifty" : 87 } , { "field" : "yes" , "morefield" : false } ]""") d1 = sd_ujson.dumps(h) d2 = sd_ujson.dumps(h, indent=2, sort_keys=True, separators=(' ,', ' : ')) h1 = sd_ujson.loads(d1) h2 = sd_ujson.loads(d2) assert h1 == h assert h2 == h assert d2 == expect
def test_illegal_separators(): h = {1: 2, 3: 4} with pytest.raises(TypeError): sd_ujson.dumps(h, separators=(b', ', ': ')) with pytest.raises(TypeError): sd_ujson.dumps(h, separators=(', ', b': ')) with pytest.raises(TypeError): sd_ujson.dumps(h, separators=(b', ', b': '))
def test_encode_unicode_bmp(): s = "\U0001f42e\U0001f42e\U0001F42D\U0001F42D" # Ъљ«Ъљ«ЪљГЪљГ encoded = sd_ujson.dumps(s) encoded_json = sdjson.dumps(s) if len(s) == 4: assert len(encoded) == len(s) * 12 + 2 else: assert len(encoded) == len(s) * 6 + 2 assert encoded == encoded_json decoded = sd_ujson.loads(encoded) assert s == decoded # sd_ujson outputs an UTF-8 encoded str object encoded = sd_ujson.dumps(s, ensure_ascii=False) # json outputs an unicode object encoded_json = sdjson.dumps(s, ensure_ascii=False) assert len(encoded) == len(s) + 2 # original length + quotes assert encoded == encoded_json decoded = sd_ujson.loads(encoded) assert s == decoded
def test_encode_null_character(): test_input = "31337 \x00 1337" output = sd_ujson.encode(test_input) assert test_input == sdjson.loads(output) assert output == sdjson.dumps(test_input) assert test_input == sd_ujson.decode(output) test_input = "\x00" output = sd_ujson.encode(test_input) assert test_input == sdjson.loads(output) assert output == sdjson.dumps(test_input) assert test_input == sd_ujson.decode(output) assert '" \\u0000\\r\\n "' == sd_ujson.dumps(" \u0000\r\n ")