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 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
|
"""Tests for the ijson.basic_parse method"""
import itertools
import threading
from decimal import Decimal
import pytest
from ijson import common
from .test_base import ARRAY_JSON, ARRAY_JSON_EVENTS, INCOMPLETE_JSONS, INCOMPLETE_JSON_TOKENS, INVALID_JSONS, JSON, JSON_EVENTS, SCALAR_JSON, SURROGATE_PAIRS_JSON, STRINGS_JSON
def _raises_json_error(adaptor, json, **kwargs):
with pytest.raises(common.JSONError):
adaptor.basic_parse(json, **kwargs)
def _raises_incomplete_json_error(adaptor, json):
with pytest.raises(common.IncompleteJSONError):
adaptor.basic_parse(json)
def test_basic_parse(adaptor):
assert JSON_EVENTS == adaptor.basic_parse(JSON)
def test_basic_parse_threaded(adaptor):
thread = threading.Thread(target=test_basic_parse, args=(adaptor,))
thread.start()
thread.join()
def test_basic_parse_array(adaptor):
assert ARRAY_JSON_EVENTS == adaptor.basic_parse(ARRAY_JSON)
def test_basic_parse_array_threaded(adaptor):
thread = threading.Thread(target=test_basic_parse_array, args=(adaptor,))
thread.start()
thread.join()
def test_scalar(adaptor):
assert [('number', 0)] == adaptor.basic_parse(SCALAR_JSON)
def test_strings(adaptor):
events = adaptor.basic_parse(STRINGS_JSON)
strings = [value for event, value in events if event == 'string']
assert ['', '"', '\\', '\\\\', '\b\f\n\r\t'] == strings
assert ('map_key', 'special\t') in events
def test_surrogate_pairs(adaptor):
event = adaptor.basic_parse(SURROGATE_PAIRS_JSON)[0]
parsed_string = event[1]
assert '💩' == parsed_string
def _get_numbers(adaptor, json, use_float):
events = adaptor.basic_parse(json, use_float=use_float)
return [value for event, value in events if event == 'number']
@pytest.mark.parametrize(
"json, expected_float_type, expected_numbers, use_float",
(
(b'[1, 1.0, 1E2]', Decimal, [1, Decimal("1.0"), Decimal("1e2")], False),
(b'[1, 1.0, 1E2]', float, [1, 1., 100.], True),
(b'1e400', Decimal, [Decimal('1e400')], False),
(b'1e-400', Decimal, [Decimal('1e-400')], False),
(b'1e-400', float, [0], True),
)
)
def test_numbers(adaptor, json, expected_float_type, expected_numbers, use_float):
"""Check that numbers are correctly parsed"""
numbers = _get_numbers(adaptor, json, use_float=use_float)
float_types = set(type(number) for number in numbers)
float_types -= {int}
assert 1 == len(float_types)
assert expected_float_type == next(iter(float_types))
assert expected_numbers == numbers
def test_32bit_ints(adaptor):
"""Test for 64-bit integers support when using use_float=true"""
past32bits = 2 ** 32 + 1
past32bits_as_json = ('%d' % past32bits).encode('utf8')
if adaptor.backend.capabilities.int64:
parsed_number = _get_numbers(adaptor, past32bits_as_json, use_float=True)[0]
assert past32bits == parsed_number
else:
_raises_json_error(adaptor, past32bits_as_json, use_float=True)
def test_max_double(adaptor):
"""Check that numbers bigger than MAX_DOUBLE (usually ~1e308) cannot be represented"""
_raises_json_error(adaptor, b'1e400', use_float=True)
@pytest.mark.parametrize(
"json", [
sign + prefix + suffix
for sign, prefix, suffix in itertools.product(
(b'', b'-'),
(b'00', b'01', b'001'),
(b'', b'.0', b'e0', b'E0')
)
]
)
def test_invalid_leading_zeros(adaptor, json):
"""Check leading zeros are invalid"""
if not adaptor.backend.capabilities.invalid_leading_zeros_detection:
return
_raises_json_error(adaptor, json)
@pytest.mark.parametrize("json", (b'1e', b'0.1e', b'0E'))
def test_incomplete_exponents(adaptor, json):
"""incomplete exponents are invalid JSON"""
_raises_json_error(adaptor, json)
@pytest.mark.parametrize("json", (b'1.', b'.1'))
def test_incomplete_fractions(adaptor, json):
"""incomplete fractions are invalid JSON"""
_raises_json_error(adaptor, json)
def test_incomplete(adaptor):
for json in INCOMPLETE_JSONS:
_raises_incomplete_json_error(adaptor, json)
def test_incomplete_tokens(adaptor):
if not adaptor.backend.capabilities.incomplete_json_tokens_detection:
return
for json in INCOMPLETE_JSON_TOKENS:
_raises_incomplete_json_error(adaptor, json)
def test_invalid(adaptor):
for json in INVALID_JSONS:
if not adaptor.backend.capabilities.incomplete_json_tokens_detection and json == INVALID_JSON_WITH_DANGLING_JUNK:
continue
_raises_json_error(adaptor, json)
def test_comments(adaptor):
json = b'{"a": 2 /* a comment */}'
if adaptor.backend.capabilities.c_comments:
events = adaptor.basic_parse(json, allow_comments=True)
assert events is not None
else:
with pytest.raises(ValueError):
adaptor.basic_parse(json, allow_comments=True)
def test_multiple_values_raises_if_not_supported(adaptor):
"""Test that setting multiple_values raises if not supported"""
if not adaptor.backend.capabilities.multiple_values:
with pytest.raises(ValueError):
adaptor.basic_parse("", multiple_values=True)
def test_multiple_values(adaptor):
"""Test that multiple_values are supported"""
multiple_json = JSON + JSON + JSON
with pytest.raises(common.JSONError):
adaptor.basic_parse(multiple_json)
with pytest.raises(common.JSONError):
adaptor.basic_parse(multiple_json, multiple_values=False)
result = adaptor.basic_parse(multiple_json, multiple_values=True)
assert JSON_EVENTS + JSON_EVENTS + JSON_EVENTS == result
|