| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -58,6 +58,16 @@ def errmsg(msg, doc, pos, end=None): | |||
| 58 | 58 | 'b': '\b', 'f': '\f', 'n': '\n', 'r': '\r', 't': '\t', | |
| 59 | 59 | } | |
| 60 | 60 | ||
| 61 | + def _decode_uXXXX(s, pos): | ||
| 62 | + esc = s[pos + 1:pos + 5] | ||
| 63 | + if len(esc) == 4 and esc[1] not in 'xX': | ||
| 64 | + try: | ||
| 65 | + return int(esc, 16) | ||
| 66 | + except ValueError: | ||
| 67 | + pass | ||
| 68 | + msg = "Invalid \\uXXXX escape" | ||
| 69 | + raise ValueError(errmsg(msg, s, pos)) | ||
| 70 | + | ||
| 61 | 71 | def py_scanstring(s, end, strict=True, | |
| 62 | 72 | _b=BACKSLASH, _m=STRINGCHUNK.match): | |
| 63 | 73 | """Scan the string s for a JSON string. End is the index of the | |
@@ -107,25 +117,14 @@ def py_scanstring(s, end, strict=True, | |||
| 107 | 117 | raise ValueError(errmsg(msg, s, end)) | |
| 108 | 118 | end += 1 | |
| 109 | 119 | else: | |
| 110 | - esc = s[end + 1:end + 5] | ||
| 111 | - next_end = end + 5 | ||
| 112 | - if len(esc) != 4: | ||
| 113 | - msg = "Invalid \\uXXXX escape" | ||
| 114 | - raise ValueError(errmsg(msg, s, end)) | ||
| 115 | - uni = int(esc, 16) | ||
| 116 | - if 0xd800 <= uni <= 0xdbff: | ||
| 117 | - msg = "Invalid \\uXXXX\\uXXXX surrogate pair" | ||
| 118 | - if not s[end + 5:end + 7] == '\\u': | ||
| 119 | - raise ValueError(errmsg(msg, s, end)) | ||
| 120 | - esc2 = s[end + 7:end + 11] | ||
| 121 | - if len(esc2) != 4: | ||
| 122 | - raise ValueError(errmsg(msg, s, end)) | ||
| 123 | - uni2 = int(esc2, 16) | ||
| 124 | - uni = 0x10000 + (((uni - 0xd800) << 10) | (uni2 - 0xdc00)) | ||
| 125 | - next_end += 6 | ||
| 120 | + uni = _decode_uXXXX(s, end) | ||
| 121 | + end += 5 | ||
| 122 | + if 0xd800 <= uni <= 0xdbff and s[end:end + 2] == '\\u': | ||
| 123 | + uni2 = _decode_uXXXX(s, end + 1) | ||
| 124 | + if 0xdc00 <= uni2 <= 0xdfff: | ||
| 125 | + uni = 0x10000 + (((uni - 0xd800) << 10) | (uni2 - 0xdc00)) | ||
| 126 | + end += 6 | ||
| 126 | 127 | char = chr(uni) | |
| 127 | - | ||
| 128 | - end = next_end | ||
| 129 | 128 | _append(char) | |
| 130 | 129 | return ''.join(chunks), end | |
| 131 | 130 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -5,10 +5,6 @@ | |||
| 5 | 5 | class TestScanstring: | |
| 6 | 6 | def test_scanstring(self): | |
| 7 | 7 | scanstring = self.json.decoder.scanstring | |
| 8 | - self.assertEqual( | ||
| 9 | - scanstring('"z\\ud834\\udd20x"', 1, True), | ||
| 10 | - ('z\U0001d120x', 16)) | ||
| 11 | - | ||
| 12 | 8 | self.assertEqual( | |
| 13 | 9 | scanstring('"z\U0001d120x"', 1, True), | |
| 14 | 10 | ('z\U0001d120x', 5)) | |
@@ -89,6 +85,53 @@ def test_scanstring(self): | |||
| 89 | 85 | scanstring('["Bad value", truth]', 2, True), | |
| 90 | 86 | ('Bad value', 12)) | |
| 91 | 87 | ||
| 88 | + def test_surrogates(self): | ||
| 89 | + scanstring = self.json.decoder.scanstring | ||
| 90 | + def assertScan(given, expect): | ||
| 91 | + self.assertEqual(scanstring(given, 1, True), | ||
| 92 | + (expect, len(given))) | ||
| 93 | + | ||
| 94 | + assertScan('"z\\ud834\\u0079x"', 'z\ud834yx') | ||
| 95 | + assertScan('"z\\ud834\\udd20x"', 'z\U0001d120x') | ||
| 96 | + assertScan('"z\\ud834\\ud834\\udd20x"', 'z\ud834\U0001d120x') | ||
| 97 | + assertScan('"z\\ud834x"', 'z\ud834x') | ||
| 98 | + assertScan('"z\\ud834\udd20x12345"', 'z\ud834\udd20x12345') | ||
| 99 | + assertScan('"z\\udd20x"', 'z\udd20x') | ||
| 100 | + assertScan('"z\ud834\udd20x"', 'z\ud834\udd20x') | ||
| 101 | + assertScan('"z\ud834\\udd20x"', 'z\ud834\udd20x') | ||
| 102 | + assertScan('"z\ud834x"', 'z\ud834x') | ||
| 103 | + | ||
| 104 | + def test_bad_escapes(self): | ||
| 105 | + scanstring = self.json.decoder.scanstring | ||
| 106 | + bad_escapes = [ | ||
| 107 | + '"\\"', | ||
| 108 | + '"\\x"', | ||
| 109 | + '"\\u"', | ||
| 110 | + '"\\u0"', | ||
| 111 | + '"\\u01"', | ||
| 112 | + '"\\u012"', | ||
| 113 | + '"\\uz012"', | ||
| 114 | + '"\\u0z12"', | ||
| 115 | + '"\\u01z2"', | ||
| 116 | + '"\\u012z"', | ||
| 117 | + '"\\u0x12"', | ||
| 118 | + '"\\u0X12"', | ||
| 119 | + '"\\ud834\\"', | ||
| 120 | + '"\\ud834\\u"', | ||
| 121 | + '"\\ud834\\ud"', | ||
| 122 | + '"\\ud834\\udd"', | ||
| 123 | + '"\\ud834\\udd2"', | ||
| 124 | + '"\\ud834\\uzdd2"', | ||
| 125 | + '"\\ud834\\udzd2"', | ||
| 126 | + '"\\ud834\\uddz2"', | ||
| 127 | + '"\\ud834\\udd2z"', | ||
| 128 | + '"\\ud834\\u0x20"', | ||
| 129 | + '"\\ud834\\u0X20"', | ||
| 130 | + ] | ||
| 131 | + for s in bad_escapes: | ||
| 132 | + with self.assertRaises(ValueError, msg=s): | ||
| 133 | + scanstring(s, 1, True) | ||
| 134 | + | ||
| 92 | 135 | def test_overflow(self): | |
| 93 | 136 | with self.assertRaises(OverflowError): | |
| 94 | 137 | self.json.decoder.scanstring(b"xxx", sys.maxsize+1) | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -16,6 +16,8 @@ Core and Builtins | |||
| 16 | 16 | Library | |
| 17 | 17 | ------- | |
| 18 | 18 | ||
| 19 | + - Issue #11489: JSON decoder now accepts lone surrogates. | ||
| 20 | + | ||
| 19 | 21 | - Issue #19545: Avoid chained exceptions while passing stray % to | |
| 20 | 22 | time.strptime(). Initial patch by Claudiu Popa. | |
| 21 | 23 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -409,17 +409,10 @@ scanstring_unicode(PyObject *pystr, Py_ssize_t end, int strict, Py_ssize_t *next | |||
| 409 | 409 | } | |
| 410 | 410 | } | |
| 411 | 411 | /* Surrogate pair */ | |
| 412 | - if (Py_UNICODE_IS_HIGH_SURROGATE(c)) { | ||
| 412 | + if (Py_UNICODE_IS_HIGH_SURROGATE(c) && end + 6 < len && | ||
| 413 | + PyUnicode_READ(kind, buf, next++) == '\\' && | ||
| 414 | + PyUnicode_READ(kind, buf, next++) == 'u') { | ||
| 413 | 415 | Py_UCS4 c2 = 0; | |
| 414 | - if (end + 6 >= len) { | ||
| 415 | - raise_errmsg("Unpaired high surrogate", pystr, end - 5); | ||
| 416 | - goto bail; | ||
| 417 | - } | ||
| 418 | - if (PyUnicode_READ(kind, buf, next++) != '\\' || | ||
| 419 | - PyUnicode_READ(kind, buf, next++) != 'u') { | ||
| 420 | - raise_errmsg("Unpaired high surrogate", pystr, end - 5); | ||
| 421 | - goto bail; | ||
| 422 | - } | ||
| 423 | 416 | end += 6; | |
| 424 | 417 | /* Decode 4 hex digits */ | |
| 425 | 418 | for (; next < end; next++) { | |
@@ -440,15 +433,10 @@ scanstring_unicode(PyObject *pystr, Py_ssize_t end, int strict, Py_ssize_t *next | |||
| 440 | 433 | goto bail; | |
| 441 | 434 | } | |
| 442 | 435 | } | |
| 443 | - if (!Py_UNICODE_IS_LOW_SURROGATE(c2)) { | ||
| 444 | - raise_errmsg("Unpaired high surrogate", pystr, end - 5); | ||
| 445 | - goto bail; | ||
| 446 | - } | ||
| 447 | - c = Py_UNICODE_JOIN_SURROGATES(c, c2); | ||
| 448 | - } | ||
| 449 | - else if (Py_UNICODE_IS_LOW_SURROGATE(c)) { | ||
| 450 | - raise_errmsg("Unpaired low surrogate", pystr, end - 5); | ||
| 451 | - goto bail; | ||
| 436 | + if (Py_UNICODE_IS_LOW_SURROGATE(c2)) | ||
| 437 | + c = Py_UNICODE_JOIN_SURROGATES(c, c2); | ||
| 438 | + else | ||
| 439 | + end -= 6; | ||
| 452 | 440 | } | |
| 453 | 441 | } | |
| 454 | 442 | APPEND_OLD_CHUNK | |
| Back | FazBrowse Home | New Git URL |
0 commit comments