1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
|
# stdlib
import decimal
import re
from collections import OrderedDict
from io import StringIO
from typing import Callable
# 3rd party
import pytest
from coincidence.selectors import not_pypy
from domdf_python_tools.compat import PYPY
# this package
import sdjson
def test_decimal() -> None:
rval = sdjson.loads("1.1", parse_float=decimal.Decimal)
assert isinstance(rval, decimal.Decimal)
assert rval == decimal.Decimal("1.1")
def test_float() -> None:
rval = sdjson.loads('1', parse_int=float)
assert isinstance(rval, float)
assert rval == 1.0
def test_empty_objects() -> None:
assert sdjson.loads("{}") == {}
assert sdjson.loads("[]") == []
assert sdjson.loads('""') == ''
def test_object_pairs_hook() -> None:
s = '{"xkd":1, "kcw":2, "art":3, "hxm":4, "qrt":5, "pad":6, "hoy":7}'
p = [("xkd", 1), ("kcw", 2), ("art", 3), ("hxm", 4), ("qrt", 5), ("pad", 6), ("hoy", 7)]
assert sdjson.loads(s, object_pairs_hook=lambda x: x) == p
assert sdjson.load(StringIO(s), object_pairs_hook=lambda x: x) == p
od = sdjson.loads(s, object_pairs_hook=OrderedDict)
assert od == OrderedDict(p)
assert type(od) == OrderedDict
# the object_pairs_hook takes priority over the object_hook
assert sdjson.loads(s, object_pairs_hook=OrderedDict, object_hook=lambda x: None) == OrderedDict(p)
# check that empty object literals work (see #17368)
assert sdjson.loads("{}", object_pairs_hook=OrderedDict) == OrderedDict()
assert sdjson.loads('{"empty": {}}', object_pairs_hook=OrderedDict) == OrderedDict([("empty", OrderedDict())])
def test_decoder_optimizations() -> None:
# Several optimizations were made that skip over calls to
# the whitespace regex, so this test is designed to try and
# exercise the uncommon cases. The array cases are already covered.
rval = sdjson.loads('{ "key" : "value" , "k":"v" }')
assert rval == {"key": "value", 'k': 'v'}
def check_keys_reuse(source: str, loads: Callable) -> None:
rval = loads(source)
(a, b), (c, d) = sorted(rval[0]), sorted(rval[1])
assert a == c
assert b == d
@not_pypy(reason="Strange behaviour with PyPy")
def test_keys_reuse():
s = '[{"a_key": 1, "b_é": 2}, {"a_key": 3, "b_é": 4}]'
check_keys_reuse(s, sdjson.loads)
decoder = sdjson.JSONDecoder()
check_keys_reuse(s, decoder.decode)
assert not decoder.memo
def test_extra_data():
s = "[1, 2, 3]5"
msg = "Extra data"
with pytest.raises(sdjson.JSONDecodeError, match=msg):
sdjson.loads(s)
def test_invalid_escape() -> None:
s = '["abc\\y"]'
msg = "escape"
with pytest.raises(sdjson.JSONDecodeError, match=msg):
sdjson.loads(s)
def test_invalid_input_type() -> None:
msg = "the JSON object must be str"
for value in [1, 3.14, [], {}, None]:
with pytest.raises(TypeError, match=msg):
sdjson.loads(value) # type: ignore[arg-type]
def test_string_with_utf8_bom() -> None:
# stdlib
import sys
# see #18958
bom_json = "[1,2,3]".encode("utf-8-sig").decode("utf-8")
with pytest.raises(sdjson.JSONDecodeError) as e:
sdjson.loads(bom_json)
# TODO:
if sys.version_info.major >= 3 and sys.version_info.minor == 7:
assert "BOM" in str(e)
with pytest.raises(sdjson.JSONDecodeError) as e:
sdjson.load(StringIO(bom_json))
# TODO:
if sys.version_info.major >= 3 and sys.version_info.minor == 7:
assert "BOM" in str(e)
# make sure that the BOM is not detected in the middle of a string
bom_in_str = '"{}"'.format(''.encode("utf-8-sig").decode("utf-8"))
assert sdjson.loads(bom_in_str) == '\ufeff'
assert sdjson.load(StringIO(bom_in_str)) == '\ufeff'
def test_negative_index() -> None:
d = sdjson.JSONDecoder()
if PYPY:
match = re.escape("Expecting value: line 1 column -49999 (char -50000)")
else:
match = "idx cannot be negative"
with pytest.raises(ValueError, match=match):
d.raw_decode('a' * 42, -50000)
|