From 16679d30668e2d5d33de9b24425a33ea4102adad Mon Sep 17 00:00:00 2001 From: Federicorao <157750791+Federicorao@users.noreply.github.com> Date: Fri, 12 Jun 2026 12:04:25 +0000 Subject: [PATCH 1/2] Honor int_max_str_digits in native JSON scanner Route native _json integer parsing through bytes_to_int with the live VM digit limit, matching int() and the pure-Python JSON path. Add snippet coverage for the boundary. Assisted-by: OpenAI GPT-5:gpt-5 --- crates/stdlib/src/json.rs | 12 ++++++++---- extra_tests/snippets/stdlib_json.py | 14 ++++++++++++++ 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/crates/stdlib/src/json.rs b/crates/stdlib/src/json.rs index dc2fbbc8892..2ba6c11f330 100644 --- a/crates/stdlib/src/json.rs +++ b/crates/stdlib/src/json.rs @@ -9,12 +9,11 @@ mod _json { builtins::{PyBaseExceptionRef, PyStrRef, PyType}, convert::ToPyResult, function::{IntoFuncArgs, OptionalArg}, - protocol::PyIterReturn, + protocol::{handle_bytes_to_int_err, PyIterReturn}, types::{Callable, Constructor}, }; use core::str::FromStr; - use malachite_bigint::BigInt; - use rustpython_common::wtf8::Wtf8Buf; + use rustpython_common::{int::bytes_to_int, wtf8::Wtf8Buf}; use std::collections::HashMap; /// Skip JSON whitespace characters (space, tab, newline, carriage return). @@ -243,7 +242,12 @@ mod _json { } else if let Some(ref parse_int) = self.parse_int { parse_int.call((buf,), vm) } else { - Ok(vm.new_pyobj(BigInt::from_str(buf).unwrap())) + let value = bytes_to_int(buf.as_bytes(), 10, vm.state.int_max_str_digits.load()) + .map_err(|e| { + let obj = vm.ctx.new_str(buf); + handle_bytes_to_int_err(e, obj.as_object(), vm) + })?; + Ok(vm.new_pyobj(value)) }; Some((ret, buf.len())) } diff --git a/extra_tests/snippets/stdlib_json.py b/extra_tests/snippets/stdlib_json.py index e55382c8223..5237cc67453 100644 --- a/extra_tests/snippets/stdlib_json.py +++ b/extra_tests/snippets/stdlib_json.py @@ -261,3 +261,17 @@ def assert_no_native_stack_overflow(func): assert e.pos == 5, f"expected pos=5, got {e.pos}" else: raise AssertionError("expected JSONDecodeError") + +# Test that json.loads honors sys.int_max_str_digits +import sys + +_min_limit = sys.int_info.str_digits_check_threshold +_orig_limit = sys.get_int_max_str_digits() +try: + sys.set_int_max_str_digits(_min_limit) + with assert_raises(ValueError): + json.loads("1" * (_min_limit + 1)) + assert json.loads("1" * _min_limit) == int("1" * _min_limit) + assert json.loads('42') == 42 +finally: + sys.set_int_max_str_digits(_orig_limit) From 2707f543e05a0106ab7ea291dbe9ae50d0699a09 Mon Sep 17 00:00:00 2001 From: Federicorao Date: Thu, 18 Jun 2026 12:31:38 +0200 Subject: [PATCH 2/2] Fix JSON int limit CI failures --- crates/stdlib/src/json.rs | 6 +++--- extra_tests/snippets/stdlib_json.py | 3 +-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/crates/stdlib/src/json.rs b/crates/stdlib/src/json.rs index 2ba6c11f330..37691d2b926 100644 --- a/crates/stdlib/src/json.rs +++ b/crates/stdlib/src/json.rs @@ -242,12 +242,12 @@ mod _json { } else if let Some(ref parse_int) = self.parse_int { parse_int.call((buf,), vm) } else { - let value = bytes_to_int(buf.as_bytes(), 10, vm.state.int_max_str_digits.load()) + bytes_to_int(buf.as_bytes(), 10, vm.state.int_max_str_digits.load()) + .map(|value| vm.new_pyobj(value)) .map_err(|e| { let obj = vm.ctx.new_str(buf); handle_bytes_to_int_err(e, obj.as_object(), vm) - })?; - Ok(vm.new_pyobj(value)) + }) }; Some((ret, buf.len())) } diff --git a/extra_tests/snippets/stdlib_json.py b/extra_tests/snippets/stdlib_json.py index 5237cc67453..406476ae8c6 100644 --- a/extra_tests/snippets/stdlib_json.py +++ b/extra_tests/snippets/stdlib_json.py @@ -1,4 +1,5 @@ import json +import sys from io import BytesIO, StringIO from testutils import assert_raises @@ -263,8 +264,6 @@ def assert_no_native_stack_overflow(func): raise AssertionError("expected JSONDecodeError") # Test that json.loads honors sys.int_max_str_digits -import sys - _min_limit = sys.int_info.str_digits_check_threshold _orig_limit = sys.get_int_max_str_digits() try: