this repo has no description
at trunk 846 lines 32 kB view raw
1#!/usr/bin/env python3 2# Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com) 3import sys 4import unittest 5 6from test_support import cpython_only 7 8 9if sys.implementation.name == "skybison": 10 from _json import loads, JSONDecodeError 11else: 12 from json import loads, JSONDecodeError 13 14 15class LoadsTests(unittest.TestCase): 16 def test_with_bytes_utf8_no_bom_returns_str(self): 17 self.assertEqual(loads(b'""'), "") 18 self.assertEqual(loads(b'"hello"'), "hello") 19 self.assertEqual(loads(b'"long string \\n blabla"'), "long string \n blabla") 20 self.assertEqual( 21 loads(b'"\xf0\x9f\x91\x8d\xf0\x9f\x90\x8d"'), "\U0001f44d\U0001f40d" 22 ) 23 24 def test_with_bytes_utf8_bom_returns_str(self): 25 self.assertEqual(loads(b'\xef\xbb\xbf""'), "") 26 self.assertEqual(loads(b'\xef\xbb\xbf"hello"'), "hello") 27 self.assertEqual( 28 loads(b'\xef\xbb\xbf"long string \\n blabla"'), "long string \n blabla" 29 ) 30 self.assertEqual( 31 loads(b'\xef\xbb\xbf"\xf0\x9f\x91\x8d\xf0\x9f\x90\x8d"'), 32 "\U0001f44d\U0001f40d", 33 ) 34 35 def test_with_bytearray_utf8_returns_str(self): 36 self.assertEqual(loads(bytearray(b'""')), "") 37 self.assertEqual(loads(bytearray(b'"hello"')), "hello") 38 self.assertEqual( 39 loads(bytearray(b'"long string \\n blabla"')), "long string \n blabla" 40 ) 41 self.assertEqual( 42 loads(bytearray(b'"\xf0\x9f\x91\x8d\xf0\x9f\x90\x8d"')), 43 "\U0001f44d\U0001f40d", 44 ) 45 self.assertEqual( 46 loads(bytearray(b'\xef\xbb\xbf"\xf0\x9f\x91\x8d\xf0\x9f\x90\x8d"')), 47 "\U0001f44d\U0001f40d", 48 ) 49 50 # TODO(T81477911) Implement UTF-16 decoding. 51 @cpython_only 52 def test_with_bytes_utf16_le_bom_returns_object(self): 53 self.assertEqual(loads(b"\xff\xfe1\x002\x003\x00"), 123) 54 self.assertEqual(loads(b'\xff\xfe"\x00\xaf!"\x00'), "\u21af") 55 self.assertEqual(loads(b'\xff\xfe"\x00<\xd8\x84\xdf"\x00'), "\U0001f384") 56 self.assertEqual( 57 loads(bytearray(b'\xff\xfe"\x00<\xd8\x84\xdf"\x00')), "\U0001f384" 58 ) 59 60 # TODO(T81477911) Implement UTF-16 decoding. 61 @cpython_only 62 def test_with_bytes_utf16_le_no_bom_returns_object(self): 63 self.assertEqual(loads(b"1\x002\x003\x00"), 123) 64 self.assertEqual(loads(b'"\x00\xaf!"\x00'), "\u21af") 65 self.assertEqual(loads(b'"\x00<\xd8\x84\xdf"\x00'), "\U0001f384") 66 self.assertEqual(loads(bytearray(b'"\x00<\xd8\x84\xdf"\x00')), "\U0001f384") 67 68 # TODO(T81477911) Implement UTF-16 decoding. 69 @cpython_only 70 def test_with_bytes_utf16_be_bom_returns_object(self): 71 self.assertEqual(loads(b"\xfe\xff\x005\x004\x007"), 547) 72 self.assertEqual(loads(b'\xfe\xff\x00"!\xc4\x00"'), "\u21c4") 73 self.assertEqual(loads(b'\xfe\xff\x00"\xd8>\xdd]\x00"'), "\U0001f95d") 74 self.assertEqual( 75 loads(bytearray(b'\xfe\xff\x00"\xd8>\xdd]\x00"')), "\U0001f95d" 76 ) 77 78 # TODO(T81477911) Implement UTF-16 decoding. 79 @cpython_only 80 def test_with_bytes_utf16_be_no_bom_returns_object(self): 81 self.assertEqual(loads(b"\x005\x004\x007"), 547) 82 self.assertEqual(loads(b'\x00"!\xc4\x00"'), "\u21c4") 83 self.assertEqual(loads(b'\x00"\xd8>\xdd]\x00"'), "\U0001f95d") 84 self.assertEqual(loads(bytearray(b'\x00"\xd8>\xdd]\x00"')), "\U0001f95d") 85 86 # TODO(T81478020) Implement UTF-32 decoding. 87 @cpython_only 88 def test_with_bytes_utf32_le_bom_returns_object(self): 89 self.assertEqual(loads(b"\xff\xfe\x00\x007\x00\x00\x006\x00\x00\x00"), 76) 90 self.assertEqual( 91 loads(b'\xff\xfe\x00\x00"\x00\x00\x00Q\xf9\x01\x00"\x00\x00\x00'), 92 "\U0001f951", 93 ) 94 95 # TODO(T81478020) Implement UTF-32 decoding. 96 @cpython_only 97 def test_with_bytes_utf32_le_no_bom_returns_object(self): 98 self.assertEqual(loads(b"7\x00\x00\x006\x00\x00\x00"), 76) 99 self.assertEqual( 100 loads(b'"\x00\x00\x00Q\xf9\x01\x00"\x00\x00\x00'), "\U0001f951" 101 ) 102 103 # TODO(T81478020) Implement UTF-32 decoding. 104 @cpython_only 105 def test_with_bytes_utf32_be_bom_returns_object(self): 106 self.assertEqual(loads(b"\x00\x00\xfe\xff\x00\x00\x00-\x00\x00\x005"), -5) 107 self.assertEqual( 108 loads(b'\x00\x00\xfe\xff\x00\x00\x00"\x00\x01\xf9W\x00\x00\x00"'), 109 "\U0001f957", 110 ) 111 112 # TODO(T81478020) Implement UTF-32 decoding. 113 @cpython_only 114 def test_with_bytes_utf32_be_no_bom_returns_object(self): 115 self.assertEqual(loads(b"\x00\x00\x00-\x00\x00\x005"), -5) 116 self.assertEqual( 117 loads(b'\x00\x00\x00"\x00\x01\xf9W\x00\x00\x00"'), "\U0001f957" 118 ) 119 120 def test_number_returns_int(self): 121 self.assertEqual(loads("0"), 0) 122 self.assertEqual(loads("1"), 1) 123 self.assertEqual(loads("2"), 2) 124 self.assertEqual(loads("3"), 3) 125 self.assertEqual(loads("4"), 4) 126 self.assertEqual(loads("5"), 5) 127 self.assertEqual(loads("6"), 6) 128 self.assertEqual(loads("7"), 7) 129 self.assertEqual(loads("8"), 8) 130 self.assertEqual(loads("9"), 9) 131 self.assertEqual(loads("10"), 10) 132 self.assertEqual(loads("91284647"), 91284647) 133 self.assertEqual(loads("-1"), -1) 134 self.assertEqual(loads("-42"), -42) 135 self.assertEqual(loads("-0"), 0) 136 self.assertEqual(loads("1000000000000000000"), 1000000000000000000) 137 self.assertEqual(loads("-1000000000000000000"), -1000000000000000000) 138 139 def test_large_number_returns_int(self): 140 self.assertEqual(loads("10000000000000000000"), 10000000000000000000) 141 self.assertEqual(loads("-10000000000000000000"), -10000000000000000000) 142 self.assertEqual( 143 loads("123456789123456789123456789123456789123456789"), 144 123456789123456789123456789123456789123456789, 145 ) 146 self.assertEqual( 147 loads("-666666666666666666666666666666"), -666666666666666666666666666666 148 ) 149 150 def test_number_with_whitespace_returns_int(self): 151 self.assertEqual(loads("0 "), 0) 152 self.assertEqual(loads("\r0"), 0) 153 self.assertEqual(loads("-1\t"), -1) 154 self.assertEqual(loads("\n-1"), -1) 155 self.assertEqual(loads("99 "), 99) 156 self.assertEqual(loads("\t99"), 99) 157 self.assertEqual(loads(" \r\n\t-5700 "), -5700) 158 self.assertEqual(loads(" -5700\t\r \t\t \n"), -5700) 159 160 def test_number_calls_parse_int(self): 161 arg = None 162 marker = object() 163 164 def func(string): 165 nonlocal arg 166 arg = string 167 return marker 168 169 self.assertIs(loads("12", parse_int=func), marker) 170 self.assertEqual(arg, "12") 171 self.assertIs(loads(" 0\t", parse_int=func), marker) 172 self.assertEqual(arg, "0") 173 self.assertIs(loads("-712\r\n", parse_int=func), marker) 174 self.assertEqual(arg, "-712") 175 self.assertIs( 176 loads(" 1234567890123456789012345687898239746924673564", parse_int=func), 177 marker, 178 ) 179 self.assertEqual(arg, "1234567890123456789012345687898239746924673564") 180 self.assertIs( 181 loads("-12233344445555566666677777778888888999999999", parse_int=func), 182 marker, 183 ) 184 self.assertEqual(arg, "-12233344445555566666677777778888888999999999") 185 186 def test_minus_without_digit_raises_decode_error(self): 187 with self.assertRaisesRegex( 188 JSONDecodeError, r"Expecting value: line 1 column 1 \(char 0\)" 189 ): 190 loads("-") 191 with self.assertRaisesRegex( 192 JSONDecodeError, r"Expecting value: line 1 column 1 \(char 0\)" 193 ): 194 loads("-a") 195 with self.assertRaisesRegex( 196 JSONDecodeError, r"Expecting value: line 1 column 1 \(char 0\)" 197 ): 198 loads("- 5") 199 with self.assertRaisesRegex( 200 JSONDecodeError, r"Expecting value: line 1 column 1 \(char 0\)" 201 ): 202 loads("- Infinity") 203 204 def test_leading_zeros_raises_decode_error(self): 205 with self.assertRaisesRegex( 206 JSONDecodeError, r"Extra data: line 1 column 2 \(char 1\)" 207 ): 208 loads("00") 209 with self.assertRaisesRegex( 210 JSONDecodeError, r"Extra data: line 1 column 2 \(char 1\)" 211 ): 212 loads("04") 213 with self.assertRaisesRegex( 214 JSONDecodeError, r"Extra data: line 1 column 2 \(char 1\)" 215 ): 216 loads("099") 217 218 def test_number_returns_float(self): 219 self.assertEqual(str(loads("0.0")), "0.0") 220 self.assertEqual(str(loads("-0.0")), "-0.0") 221 self.assertEqual(str(loads("1.0")), "1.0") 222 self.assertEqual(str(loads("42.0")), "42.0") 223 self.assertEqual(str(loads("-312.0")), "-312.0") 224 self.assertEqual( 225 str(loads("987654321987654321987654321987654321.0")), 226 "9.876543219876544e+35", 227 ) 228 229 def test_number_with_fraction_returns_float(self): 230 self.assertEqual(str(loads("0.5")), "0.5") 231 self.assertEqual(str(loads("-3.125")), "-3.125") 232 self.assertEqual(str(loads("99.987654321")), "99.987654321") 233 self.assertEqual(str(loads("-123456789.987654321")), "-123456789.98765433") 234 self.assertEqual( 235 str(loads("987654321.987654321987654321987654321987654321")), 236 "987654321.9876543", 237 ) 238 239 def test_number_with_exponent_returns_flaot(self): 240 self.assertEqual(str(loads("0e0")), "0.0") 241 self.assertEqual(str(loads("0e+0")), "0.0") 242 self.assertEqual(str(loads("0e-0")), "0.0") 243 self.assertEqual(str(loads("0e000")), "0.0") 244 self.assertEqual(str(loads("0e-00")), "0.0") 245 self.assertEqual(str(loads("0e-0000")), "0.0") 246 self.assertEqual(str(loads("0e+1000")), "0.0") 247 self.assertEqual(str(loads("0e-1000")), "0.0") 248 249 self.assertEqual(str(loads("1e4")), "10000.0") 250 self.assertEqual(str(loads("5e+5")), "500000.0") 251 self.assertEqual( 252 str(loads("5e+0000000000000000000000000000000000005")), "500000.0" 253 ) 254 self.assertEqual(str(loads("10e-6")), "1e-05") 255 self.assertEqual(str(loads("15e+77")), "1.5e+78") 256 self.assertEqual(str(loads("1234e-98")), "1.234e-95") 257 258 def test_number_returns_float_infinity(self): 259 self.assertEqual(loads("1e10000"), float("inf")) 260 self.assertEqual(loads("-3e10000"), float("-inf")) 261 262 def test_number_returns_zero(self): 263 self.assertEqual(str(loads("1e-10000")), "0.0") 264 self.assertEqual(str(loads("-1e-10000")), "-0.0") 265 266 def test_number_with_whitespace_returns_float(self): 267 self.assertEqual(str(loads(" 0.0")), "0.0") 268 self.assertEqual(str(loads("0.0 ")), "0.0") 269 self.assertEqual(str(loads(" 0.0 ")), "0.0") 270 271 def test_number_calls_parse_float(self): 272 arg = None 273 marker = object() 274 275 def func(string): 276 nonlocal arg 277 arg = string 278 return marker 279 280 self.assertIs(loads("1.1", parse_float=func), marker) 281 self.assertEqual(arg, "1.1") 282 self.assertIs(loads(" -4.0\r\n", parse_float=func), marker) 283 self.assertEqual(arg, "-4.0") 284 self.assertIs(loads("41e81 ", parse_float=func), marker) 285 self.assertEqual(arg, "41e81") 286 self.assertIs(loads("\t -3.4E-7", parse_float=func), marker) 287 self.assertEqual(arg, "-3.4E-7") 288 289 def test_dot_raises_decode_error(self): 290 with self.assertRaisesRegex( 291 JSONDecodeError, r"Expecting value: line 1 column 1 \(char 0\)" 292 ): 293 loads(".") 294 with self.assertRaisesRegex( 295 JSONDecodeError, r"Expecting value: line 1 column 1 \(char 0\)" 296 ): 297 loads(".4") 298 299 def test_number_dot_raises_decode_error(self): 300 with self.assertRaisesRegex( 301 JSONDecodeError, r"Extra data: line 1 column 2 \(char 1\)" 302 ): 303 loads("1.") 304 with self.assertRaisesRegex( 305 JSONDecodeError, r"Extra data: line 1 column 2 \(char 1\)" 306 ): 307 loads("2.a") 308 with self.assertRaisesRegex( 309 JSONDecodeError, r"Extra data: line 1 column 2 \(char 1\)" 310 ): 311 loads("3e") 312 with self.assertRaisesRegex( 313 JSONDecodeError, r"Extra data: line 1 column 2 \(char 1\)" 314 ): 315 loads("4e-") 316 with self.assertRaisesRegex( 317 JSONDecodeError, r"Extra data: line 1 column 2 \(char 1\)" 318 ): 319 loads("5e+") 320 with self.assertRaisesRegex( 321 JSONDecodeError, r"Extra data: line 1 column 2 \(char 1\)" 322 ): 323 loads("6e+x") 324 with self.assertRaisesRegex( 325 JSONDecodeError, r"Extra data: line 1 column 4 \(char 3\)" 326 ): 327 loads("6.2e") 328 with self.assertRaisesRegex( 329 JSONDecodeError, r"Extra data: line 1 column 2 \(char 1\)" 330 ): 331 loads("6ea") 332 333 def test_string_returns_str(self): 334 self.assertEqual(loads('""'), "") 335 self.assertEqual(loads('" "'), " ") 336 self.assertEqual(loads('"hello"'), "hello") 337 self.assertEqual(loads('"hello y\'all"'), "hello y'all") 338 339 def test_control_character_in_string_raises_decode_error(self): 340 with self.assertRaisesRegex( 341 JSONDecodeError, r"Invalid control character at: line 1 column 2 \(char 1\)" 342 ): 343 loads('"\x00"') 344 with self.assertRaisesRegex( 345 JSONDecodeError, r"Invalid control character at: line 1 column 7 \(char 6\)" 346 ): 347 loads('"hello\x01"') 348 with self.assertRaisesRegex( 349 JSONDecodeError, r"Invalid control character at: line 2 column 5 \(char 5\)" 350 ): 351 loads('\n"hel\x10lo"') 352 with self.assertRaisesRegex( 353 JSONDecodeError, r"Invalid control character at: line 1 column 4 \(char 3\)" 354 ): 355 loads('\t "\x1fhello"') 356 357 def test_unterminated_string_raises_decode_error(self): 358 with self.assertRaisesRegex( 359 JSONDecodeError, 360 r"Unterminated string starting at: line 1 column 1 \(char 0\)", 361 ): 362 loads('"') 363 with self.assertRaisesRegex( 364 JSONDecodeError, 365 r"Unterminated string starting at: line 2 column 1 \(char 2\)", 366 ): 367 loads('\t\n"he\nlo', strict=False) 368 369 def test_with_strict_false_returns_str(self): 370 self.assertEqual(loads('"he\x00llo"', strict=False), "he\x00llo") 371 control_chars = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" 372 self.assertEqual(loads(f'"{control_chars}"', strict=False), control_chars) 373 374 def test_string_with_escape_returns_str(self): 375 self.assertEqual(loads('"\\""'), '"') 376 self.assertEqual(loads('"\\\\"'), "\\") 377 self.assertEqual(loads('"\\/"'), "/") 378 self.assertEqual(loads('"\\b"'), "\b") 379 self.assertEqual(loads('"\\f"'), "\f") 380 self.assertEqual(loads('"\\n"'), "\n") 381 self.assertEqual(loads('"\\r"'), "\r") 382 self.assertEqual(loads('"\\t"'), "\t") 383 384 def test_string_with_u_escape_returns_str(self): 385 self.assertEqual(loads('"\\u0059"'), "Y") 386 self.assertEqual(loads('"\\u1234"'), "\u1234") 387 self.assertEqual(loads('"\\ucafe"'), "\ucafe") 388 self.assertEqual(loads('"\\uCAFE"'), "\ucafe") 389 self.assertEqual(loads('"\\uCa00"'), "\uca00") 390 self.assertEqual(loads('"\\u99fE"'), "\u99fe") 391 392 def test_string_with_u_escape_combines_surrogates(self): 393 self.assertEqual(loads('"\\ud83e\\udd20"'), "\U0001f920") 394 self.assertEqual(loads('"\\udbff\\udfff"'), "\U0010ffff") 395 396 def test_string_with_lone_surrogates_returns_str(self): 397 self.assertEqual(loads('"\\ud83e"'), "\ud83e") 398 self.assertEqual(loads('"\\ud83e\\ud83e"'), "\ud83e\ud83e") 399 self.assertEqual(loads('"\\udd20"'), "\udd20") 400 self.assertEqual(loads('"\\udd20\\ud83e"'), "\udd20\ud83e") 401 402 def test_string_with_whitespace_returns_str(self): 403 self.assertEqual(loads(' ""'), "") 404 self.assertEqual(loads('"" '), "") 405 self.assertEqual(loads(' "" '), "") 406 407 def test_chars_after_string_raises_json_decode_error(self): 408 with self.assertRaisesRegex( 409 JSONDecodeError, r"Extra data: line 1 column 3 \(char 2\)" 410 ): 411 loads('""a') 412 with self.assertRaisesRegex( 413 JSONDecodeError, r"Extra data: line 1 column 7 \(char 6\)" 414 ): 415 loads('"" ""') 416 417 def test_string_with_unterminated_escape_raises_decode_error(self): 418 with self.assertRaisesRegex( 419 JSONDecodeError, 420 r"Unterminated string starting at: line 1 column 1 \(char 0\)", 421 ): 422 loads('"\\') 423 424 def test_string_with_invalid_escape_raises_decode_error(self): 425 with self.assertRaisesRegex( 426 JSONDecodeError, r"Invalid \\escape: line 1 column 2 \(char 1\)" 427 ): 428 loads('"\\x"') 429 with self.assertRaisesRegex( 430 JSONDecodeError, r"Invalid \\escape: line 1 column 2 \(char 1\)" 431 ): 432 loads('"\\\U0001f974"') 433 434 def test_string_with_invalid_u_escape_raises_decode_error(self): 435 with self.assertRaisesRegex( 436 JSONDecodeError, r"Invalid \\uXXXX escape: line 1 column 3 \(char 2\)" 437 ): 438 loads('"\\u') 439 with self.assertRaisesRegex( 440 JSONDecodeError, r"Invalid \\uXXXX escape: line 1 column 3 \(char 2\)" 441 ): 442 loads('"\\u123') 443 with self.assertRaisesRegex( 444 JSONDecodeError, r"Invalid \\uXXXX escape: line 1 column 3 \(char 2\)" 445 ): 446 loads('"\\u000g') 447 with self.assertRaisesRegex( 448 JSONDecodeError, r"Invalid \\uXXXX escape: line 1 column 9 \(char 8\)" 449 ): 450 loads('"\\ud83e\\u5x"') 451 452 def test_true_returns_bool(self): 453 self.assertIs(loads("true"), True) 454 455 def test_false_returns_bool(self): 456 self.assertIs(loads("false"), False) 457 458 def test_null_returns_none(self): 459 self.assertIs(loads("null"), None) 460 461 def test_infinity_returns_float(self): 462 self.assertEqual(loads("Infinity"), float("inf")) 463 464 def test_minus_infinity_returns_float(self): 465 self.assertEqual(loads("-Infinity"), float("-inf")) 466 467 def test_nan_returns_float(self): 468 self.assertEqual(str(loads("NaN")), "nan") 469 470 def test_whitespace_around_constant_is_ignored(self): 471 self.assertIs(loads("true "), True) 472 self.assertIs(loads("\tfalse"), False) 473 self.assertIs(loads("\r null \n"), None) 474 self.assertEqual(loads(" Infinity "), float("inf")) 475 self.assertEqual(loads("\n\r-Infinity\t"), float("-inf")) 476 self.assertEqual(str(loads("\r\nNaN\t")), "nan") 477 478 def test_calls_parse_constant(self): 479 arg = None 480 marker = object() 481 482 def func(string): 483 nonlocal arg 484 arg = string 485 return marker 486 487 self.assertIs(loads("NaN", parse_constant=func), marker) 488 self.assertEqual(arg, "NaN") 489 self.assertIs(loads(" -Infinity\t\r", parse_constant=func), marker) 490 self.assertEqual(arg, "-Infinity") 491 self.assertIs(loads(" Infinity\n\r", parse_constant=func), marker) 492 self.assertEqual(arg, "Infinity") 493 494 def test_does_not_call_parse_constant(self): 495 def func(string): 496 raise Exception("should not be called") 497 498 self.assertIs(loads("true", parse_constant=func), True) 499 self.assertIs(loads("false", parse_constant=func), False) 500 self.assertIs(loads("null", parse_constant=func), None) 501 502 def test_parse_constant_propagates_exception(self): 503 def func(string): 504 raise UserWarning("test") 505 506 with self.assertRaises(UserWarning): 507 loads("NaN", parse_constant=func) 508 509 def test_constant_with_extra_chars_raises_json_decode_error(self): 510 with self.assertRaisesRegex( 511 JSONDecodeError, r"Extra data: line 1 column 5 \(char 4\)" 512 ): 513 loads("truee") 514 with self.assertRaisesRegex( 515 JSONDecodeError, r"Extra data: line 1 column 7 \(char 6\)" 516 ): 517 loads("false null") 518 with self.assertRaisesRegex( 519 JSONDecodeError, r"Extra data: line 2 column 2 \(char 5\)" 520 ): 521 loads("NaN\n\ta") 522 523 def test_whitespace_string_raises_json_decode_error(self): 524 with self.assertRaisesRegex( 525 JSONDecodeError, r"Expecting value: line 1 column 2 \(char 1\)" 526 ): 527 loads(" ") 528 with self.assertRaisesRegex( 529 JSONDecodeError, r"Expecting value: line 2 column 1 \(char 4\)" 530 ): 531 loads("\t\r \n") 532 533 def test_empty_string_raises_json_decode_error(self): 534 with self.assertRaisesRegex( 535 JSONDecodeError, r"Expecting value: line 1 column 1 \(char 0\)" 536 ): 537 loads("") 538 539 def test_list_returns_list(self): 540 result = loads("[1,2,3]") 541 self.assertIs(type(result), list) 542 self.assertEqual(result, [1, 2, 3]) 543 544 def test_list_misc_returns_list(self): 545 self.assertEqual(loads('[ "hello","world",\r\n42]'), ["hello", "world", 42]) 546 self.assertEqual( 547 loads("[true, false, null, 13.13 \r]"), [True, False, None, 13.13] 548 ) 549 self.assertEqual( 550 loads('\n[5,\r-Infinity,\t"",Infinity]'), 551 [5, float("-inf"), "", float("inf")], 552 ) 553 result = loads("[13, NaN]") 554 self.assertEqual(result[0], 13) 555 self.assertEqual(str(result[1]), "nan") 556 557 def test_nested_lists_returns_list(self): 558 self.assertEqual(loads("[[]]"), [[]]) 559 self.assertEqual(loads("[1, [] ]"), [1, []]) 560 self.assertEqual(loads('[\t[], "test"]'), [[], "test"]) 561 self.assertEqual( 562 loads('[[\r[[[]\t]\n], [-4.5, [[], ["a"] ]]]]'), 563 [[[[[]]], [-4.5, [[], ["a"]]]]], 564 ) 565 566 def test_empty_list_returns_list(self): 567 self.assertEqual(loads("[]"), []) 568 self.assertEqual(loads(" []"), []) 569 self.assertEqual(loads("[ ]"), []) 570 self.assertEqual(loads("[] "), []) 571 572 def test_deep_list_nesting_raises_recursion_error(self): 573 with self.assertRaises(RecursionError): 574 loads("[" * 2000000 + "]" * 2000000) 575 576 def test_unbalanced_lists_raises_json_decode_error(self): 577 with self.assertRaisesRegex( 578 JSONDecodeError, r"Expecting value: line 1 column 2 \(char 1\)" 579 ): 580 loads("[") 581 with self.assertRaisesRegex( 582 JSONDecodeError, r"Expecting ',' delimiter: line 1 column 4 \(char 3\)" 583 ): 584 loads("[[]") 585 with self.assertRaisesRegex( 586 JSONDecodeError, r"Expecting ',' delimiter: line 1 column 14 \(char 13\)" 587 ): 588 loads("[[], [], [[]]") 589 590 def test_missing_list_delimiter_raises_json_decode_error(self): 591 with self.assertRaisesRegex( 592 JSONDecodeError, r"Expecting ',' delimiter: line 1 column 4 \(char 3\)" 593 ): 594 loads("[1 2]") 595 with self.assertRaisesRegex( 596 JSONDecodeError, r"Expecting ',' delimiter: line 1 column 4 \(char 3\)" 597 ): 598 loads('[""') 599 with self.assertRaisesRegex( 600 JSONDecodeError, r"Expecting ',' delimiter: line 1 column 4 \(char 3\)" 601 ): 602 loads("[[]4.4]") 603 604 def test_dict_returns_dict(self): 605 result = loads('{"foo": "bar", "baz": 42}') 606 self.assertIs(type(result), dict) 607 self.assertEqual(list(result.keys()), ["foo", "baz"]) 608 self.assertEqual(list(result.values()), ["bar", 42]) 609 610 def test_dict_misc_returns_dict(self): 611 self.assertEqual(loads('{"": ""}'), {"": ""}) 612 self.assertEqual( 613 loads( 614 '{\t"foo": [], "1"\n:\r2, "3" :4.4,"4":\r\ntrue, "5": Infinity\t}' 615 ), 616 {"foo": [], "1": 2, "3": 4.4, "4": True, "5": float("inf")}, 617 ) 618 619 def test_dict_with_repeatet_keys_returns_dict(self): 620 self.assertEqual(loads('{"a": [], "a": 7.7}'), {"a": 7.7}) 621 self.assertEqual( 622 loads('{"a": 42, "b": 7, "c": 4, "a": -8, "b": 11}'), 623 {"a": -8, "b": 11, "c": 4}, 624 ) 625 626 def test_empty_dict_returns_dict(self): 627 self.assertEqual(loads("{}"), {}) 628 self.assertEqual(loads(" {}"), {}) 629 self.assertEqual(loads("{ }"), {}) 630 self.assertEqual(loads("{} "), {}) 631 632 def test_dict_with_non_string_key_raises_json_decode_error(self): 633 with self.assertRaisesRegex( 634 JSONDecodeError, 635 r"Expecting property name enclosed in double quotes: line 1 column 2 \(char 1\)", 636 ): 637 loads("{42:1}") 638 with self.assertRaisesRegex( 639 JSONDecodeError, 640 r"Expecting property name enclosed in double quotes: line 1 column 2 \(char 1\)", 641 ): 642 loads("{[]:1}") 643 with self.assertRaisesRegex( 644 JSONDecodeError, 645 r"Expecting property name enclosed in double quotes: line 1 column 2 \(char 1\)", 646 ): 647 loads("{{}:1}") 648 649 def test_dict_with_missing_colon_raises_json_decode_error(self): 650 with self.assertRaisesRegex( 651 JSONDecodeError, r"Expecting ':' delimiter: line 1 column 4 \(char 3\)" 652 ): 653 loads('{""}') 654 with self.assertRaisesRegex( 655 JSONDecodeError, r"Expecting ':' delimiter: line 1 column 19 \(char 18\)" 656 ): 657 loads('{"foo": 42, "bar" 5}') 658 659 def test_dict_with_extra_comma_raises_json_decode_error(self): 660 with self.assertRaisesRegex( 661 JSONDecodeError, 662 r"Expecting property name enclosed in double quotes: line 1 column 8 \(char 7\)", 663 ): 664 loads('{"": 4,}') 665 666 def test_dict_with_missing_comma_raises_json_decode_error(self): 667 with self.assertRaisesRegex( 668 JSONDecodeError, r"Expecting ',' delimiter: line 1 column 8 \(char 7\)" 669 ): 670 loads('{"": 4 "a": 5}') 671 672 def test_dict_with_deep_nesting_raises_recursion_error(self): 673 with self.assertRaises(RecursionError): 674 loads('{"":' * 2000000 + "{" + "}" * 2000000) 675 676 def test_dict_calls_object_hook(self): 677 arg = None 678 marker = object() 679 680 def func(d): 681 nonlocal arg 682 arg = d 683 return marker 684 685 self.assertIs(loads("{}", object_hook=func), marker) 686 self.assertIs(type(arg), dict) 687 self.assertEqual(arg, {}) 688 689 self.assertEqual( 690 loads('[4, {"foo": 4, "aa": -8}, false]', object_hook=func), 691 [4, marker, False], 692 ) 693 self.assertIs(type(arg), dict) 694 self.assertEqual(list(arg.keys()), ["foo", "aa"]) 695 self.assertEqual(list(arg.values()), [4, -8]) 696 697 def test_dict_calls_object_pairs_hook(self): 698 arg = None 699 marker = object() 700 701 def func(pairs): 702 nonlocal arg 703 arg = pairs 704 return marker 705 706 self.assertIs(loads("{}", object_pairs_hook=func), marker) 707 self.assertIs(type(arg), list) 708 self.assertEqual(arg, []) 709 710 self.assertEqual( 711 loads('[4, {"b": 4, "a": -8, "c":null}, false]', object_pairs_hook=func), 712 [4, marker, False], 713 ) 714 self.assertIs(type(arg), list) 715 self.assertIs(type(arg[0]), tuple) 716 self.assertEqual(arg, [("b", 4), ("a", -8), ("c", None)]) 717 718 def test_dict_with_both_hooks_calls_object_pairs_hook(self): 719 arg0 = None 720 arg1 = None 721 marker = object() 722 723 def func0(d): 724 nonlocal arg0 725 arg0 = d 726 raise Exception("should not be called") 727 728 def func1(pairs): 729 nonlocal arg1 730 arg1 = pairs 731 return marker 732 733 self.assertIs( 734 loads('{"foo": "baz"}', object_hook=func0, object_pairs_hook=func1), marker 735 ) 736 self.assertIs(arg0, None) 737 self.assertEqual(arg1, [("foo", "baz")]) 738 739 def test_dict_keys_are_merged(self): 740 result = loads( 741 '[{"foo": 5, "bar": 8, "a very long key": 11}, [{"a very long key": 4, "foo": []}]]' 742 ) 743 self.assertEqual( 744 result, 745 [ 746 {"foo": 5, "bar": 8, "a very long key": 11}, 747 [{"a very long key": 4, "foo": []}], 748 ], 749 ) 750 d0 = result[0] 751 d1 = result[1][0] 752 d0_keys = list(d0.keys()) 753 d1_keys = list(d1.keys()) 754 self.assertEqual(d0_keys, ["foo", "bar", "a very long key"]) 755 self.assertEqual(d1_keys, ["a very long key", "foo"]) 756 self.assertIs(d0_keys[0], d1_keys[1]) 757 self.assertIs(d0_keys[2], d1_keys[0]) 758 759 def test_with_cls_calls_cls_and_calls_decode(self): 760 class C: 761 def decode(self, s): 762 return f"decode called on {s}" 763 764 func_args = None 765 func_kwargs = None 766 767 def func(*args, **kwargs): 768 nonlocal func_args 769 nonlocal func_kwargs 770 func_args = args 771 func_kwargs = kwargs 772 return C() 773 774 result = loads("<test input>", cls=func, foo=42, bar="hello") 775 self.assertEqual(func_args, ()) 776 self.assertEqual( 777 func_kwargs, 778 { 779 "foo": 42, 780 "bar": "hello", 781 }, 782 ) 783 self.assertEqual(result, "decode called on <test input>") 784 785 func_args = None 786 func_kwargs = None 787 result = loads( 788 s="<test input>", 789 encoding="ignored", 790 cls=func, 791 object_hook=1, 792 parse_float=2, 793 parse_int=3, 794 parse_constant=4, 795 object_pairs_hook=5, 796 strict=6, 797 foobar=7, 798 ) 799 self.assertEqual(func_args, ()) 800 self.assertEqual( 801 func_kwargs, 802 { 803 "object_hook": 1, 804 "parse_float": 2, 805 "parse_int": 3, 806 "parse_constant": 4, 807 "object_pairs_hook": 5, 808 "strict": 6, 809 "foobar": 7, 810 }, 811 ) 812 self.assertEqual(result, "decode called on <test input>") 813 814 def test_unexpected_char_raises_json_decode_error(self): 815 with self.assertRaisesRegex( 816 JSONDecodeError, r"Expecting value: line 1 column 1 \(char 0\)" 817 ): 818 loads("a") 819 with self.assertRaisesRegex( 820 JSONDecodeError, r"Expecting value: line 1 column 1 \(char 0\)" 821 ): 822 loads("$") 823 with self.assertRaisesRegex( 824 JSONDecodeError, r"Expecting value: line 1 column 1 \(char 0\)" 825 ): 826 loads("\x00") 827 with self.assertRaisesRegex( 828 JSONDecodeError, r"Expecting value: line 1 column 1 \(char 0\)" 829 ): 830 loads("\U0001f480") 831 832 def test_utf8_bom_raises_decode_error(self): 833 with self.assertRaisesRegex( 834 JSONDecodeError, r"Unexpected UTF-8 BOM \(decode using utf-8-sig\)" 835 ): 836 loads("\ufeff0") 837 838 def test_json_raises_type_error(self): 839 with self.assertRaisesRegex( 840 TypeError, "the JSON object must be str, bytes or bytearray, not float" 841 ): 842 loads(42.42) 843 844 845if __name__ == "__main__": 846 unittest.main()