From fce5aed5faa4e6ef31962f4a9f51d841921d459e Mon Sep 17 00:00:00 2001 From: CPython Developers <> Date: Sat, 14 Feb 2026 13:15:35 +0000 Subject: [PATCH] Update json from v3.14.3 --- Lib/json/decoder.py | 14 -------------- Lib/test/test_json/__init__.py | 6 ++---- Lib/test/test_json/test_decode.py | 1 + Lib/test/test_json/test_default.py | 2 +- Lib/test/test_json/test_fail.py | 1 + Lib/test/test_json/test_scanstring.py | 2 +- Lib/test/test_json/test_speedups.py | 2 +- Lib/test/test_json/test_unicode.py | 7 ++++--- scripts/update_lib/cmd_auto_mark.py | 25 ++++++++++++++++++++++++- scripts/update_lib/patch_spec.py | 14 ++++++++++++++ 10 files changed, 49 insertions(+), 25 deletions(-) diff --git a/Lib/json/decoder.py b/Lib/json/decoder.py index db87724a897..92ad6352557 100644 --- a/Lib/json/decoder.py +++ b/Lib/json/decoder.py @@ -27,20 +27,6 @@ class JSONDecodeError(ValueError): colno: The column corresponding to pos """ - # RUSTPYTHON SPECIFIC - @classmethod - def _from_serde(cls, msg, doc, line, col): - pos = 0 - # 0-indexed - line -= 1 - col -= 1 - while line > 0: - i = doc.index('\n', pos) - line -= 1 - pos = i - pos += col - return cls(msg, doc, pos) - # Note that this exception is used from _json def __init__(self, msg, doc, pos): lineno = doc.count('\n', 0, pos) + 1 diff --git a/Lib/test/test_json/__init__.py b/Lib/test/test_json/__init__.py index 41c06beaa38..e433f31f080 100644 --- a/Lib/test/test_json/__init__.py +++ b/Lib/test/test_json/__init__.py @@ -8,9 +8,7 @@ # import json with and without accelerations -# XXX RUSTPYTHON: we don't import _json as fresh since the fresh module isn't placed -# into the sys.modules cache, and therefore the vm can't recognize the _json.Scanner class -cjson = import_helper.import_fresh_module('json') #, fresh=['_json']) +cjson = import_helper.import_fresh_module('json', fresh=['_json']) pyjson = import_helper.import_fresh_module('json', blocked=['_json']) # JSONDecodeError is cached inside the _json module cjson.JSONDecodeError = cjson.decoder.JSONDecodeError = json.JSONDecodeError @@ -41,7 +39,7 @@ def test_pyjson(self): 'json.encoder') class TestCTest(CTest): - @unittest.expectedFailure # TODO: RUSTPYTHON + @unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: 'NoneType' object has no attribute '__module__'. Did you mean: '__reduce__'? def test_cjson(self): self.assertEqual(self.json.scanner.make_scanner.__module__, '_json') self.assertEqual(self.json.decoder.scanstring.__module__, '_json') diff --git a/Lib/test/test_json/test_decode.py b/Lib/test/test_json/test_decode.py index 7b3b30ce449..75735b382e4 100644 --- a/Lib/test/test_json/test_decode.py +++ b/Lib/test/test_json/test_decode.py @@ -136,6 +136,7 @@ def test_limit_int(self): class TestPyDecode(TestDecode, PyTest): pass class TestCDecode(TestDecode, CTest): + @unittest.expectedFailure # TODO: RUSTPYTHON def test_limit_int(self): return super().test_limit_int() diff --git a/Lib/test/test_json/test_default.py b/Lib/test/test_json/test_default.py index 4d569dadfa4..e120a520d23 100644 --- a/Lib/test/test_json/test_default.py +++ b/Lib/test/test_json/test_default.py @@ -10,7 +10,7 @@ def test_default(self): self.dumps(type, default=repr), self.dumps(repr(type))) - @unittest.expectedFailure # TODO: RUSTPYTHON + @unittest.expectedFailure # TODO: RUSTPYTHON; 'when serializing type object'] def test_bad_default(self): def default(obj): if obj is NotImplemented: diff --git a/Lib/test/test_json/test_fail.py b/Lib/test/test_json/test_fail.py index 4adfcb17c4d..be53b42d8ad 100644 --- a/Lib/test/test_json/test_fail.py +++ b/Lib/test/test_json/test_fail.py @@ -240,6 +240,7 @@ def test_linecol(self): class TestPyFail(TestFail, PyTest): pass class TestCFail(TestFail, CTest): + @unittest.expectedFailure # TODO: RUSTPYTHON def test_failures(self): return super().test_failures() diff --git a/Lib/test/test_json/test_scanstring.py b/Lib/test/test_json/test_scanstring.py index e77ec152280..5e722b71737 100644 --- a/Lib/test/test_json/test_scanstring.py +++ b/Lib/test/test_json/test_scanstring.py @@ -144,7 +144,7 @@ def test_bad_escapes(self): with self.assertRaises(self.JSONDecodeError, msg=s): scanstring(s, 1, True) - @unittest.expectedFailure # TODO: RUSTPYTHON + @unittest.expectedFailure # TODO: RUSTPYTHON; json.decoder.JSONDecodeError: Unterminated string starting at: line 1 column 9223372036854775808 (char 9223372036854775807) def test_overflow(self): with self.assertRaises(OverflowError): self.json.decoder.scanstring("xxx", sys.maxsize+1) diff --git a/Lib/test/test_json/test_speedups.py b/Lib/test/test_json/test_speedups.py index 370a2539d10..e2084ee20c4 100644 --- a/Lib/test/test_json/test_speedups.py +++ b/Lib/test/test_json/test_speedups.py @@ -62,7 +62,7 @@ def bad_encoder2(*args): with self.assertRaises(ZeroDivisionError): enc('spam', 4) - @unittest.expectedFailure # TODO: RUSTPYTHON + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: "make_encoder\(\) argument 1 must be dict or None, not int" does not match "'NoneType' object is not callable" def test_bad_markers_argument_to_encoder(self): # https://bugs.python.org/issue45269 with self.assertRaisesRegex( diff --git a/Lib/test/test_json/test_unicode.py b/Lib/test/test_json/test_unicode.py index ab1be6ea6e8..a35605d401d 100644 --- a/Lib/test/test_json/test_unicode.py +++ b/Lib/test/test_json/test_unicode.py @@ -138,9 +138,6 @@ def test_object_pairs_hook_with_unicode(self): class TestPyUnicode(TestUnicode, PyTest): pass class TestCUnicode(TestUnicode, CTest): - @unittest.expectedFailure # TODO: RUSTPYTHON - def test_ascii_non_printable_encode(self): - return super().test_ascii_non_printable_encode() @unittest.skip("TODO: RUSTPYTHON; panics with 'str has surrogates'") def test_single_surrogate_decode(self): @@ -149,3 +146,7 @@ def test_single_surrogate_decode(self): @unittest.skip("TODO: RUSTPYTHON; panics with 'str has surrogates'") def test_single_surrogate_encode(self): return super().test_single_surrogate_encode() + + @unittest.expectedFailure # TODO: RUSTPYTHON; ? ^ + def test_ascii_non_printable_encode(self): + return super().test_ascii_non_printable_encode() diff --git a/scripts/update_lib/cmd_auto_mark.py b/scripts/update_lib/cmd_auto_mark.py index c77cbf300f1..e3e0432ed9a 100644 --- a/scripts/update_lib/cmd_auto_mark.py +++ b/scripts/update_lib/cmd_auto_mark.py @@ -697,7 +697,30 @@ def strip_reasonless_expected_failures( for idx in sorted(lines_to_remove, reverse=True): del lines[idx] - return "\n".join(lines) + "\n" if lines else "", stripped_tests + # Check if any classes are now empty and add 'pass' if needed + result = "\n".join(lines) + "\n" if lines else "" + try: + result_tree = ast.parse(result) + # Track classes that need 'pass' added + classes_needing_pass = [] + for node in result_tree.body: + if isinstance(node, ast.ClassDef) and len(node.body) == 0: + # Empty class - need to add pass + classes_needing_pass.append((node.lineno, node.col_offset)) + + if classes_needing_pass: + result_lines = result.splitlines() + # Insert 'pass' for each empty class (in reverse order to preserve line numbers) + for lineno, col_offset in reversed(classes_needing_pass): + class_line_idx = lineno - 1 + indent = " " * (col_offset + 4) # Class body indent + result_lines.insert(class_line_idx + 1, f"{indent}pass") + result = "\n".join(result_lines) + "\n" + except SyntaxError: + # If result has syntax errors, return original contents + return contents, set() + + return result, stripped_tests def extract_test_methods(contents: str) -> set[tuple[str, str]]: diff --git a/scripts/update_lib/patch_spec.py b/scripts/update_lib/patch_spec.py index d27d2e22fa7..1e7fc3fd5cd 100644 --- a/scripts/update_lib/patch_spec.py +++ b/scripts/update_lib/patch_spec.py @@ -352,6 +352,20 @@ def apply_patches(contents: str, patches: Patches) -> str: tree = ast.parse(contents) lines = contents.splitlines() + # First pass: Expand classes with only 'pass' on the same line if they need methods added + classes_needing_methods = set(patches.keys()) + for node in tree.body: + if isinstance(node, ast.ClassDef) and node.name in classes_needing_methods: + # Check if class has only 'pass' on the same line + if ( + len(node.body) == 1 + and isinstance(node.body[0], ast.Pass) + and node.lineno == node.end_lineno + ): + # Expand the class: replace ': pass' with ':' + line_idx = node.lineno - 1 # Convert to 0-indexed + lines[line_idx] = lines[line_idx].replace(': pass', ':') + modifications = list(_iter_patch_lines(tree, patches)) # If we have modifications and unittest is not imported, add it