From f2ec951597794942be98630b851852d8a4860b54 Mon Sep 17 00:00:00 2001 From: Mike Kruskal Date: Mon, 9 Dec 2024 20:51:30 -0800 Subject: [PATCH] Fix python JSON parser to accept string fields containing integer-equivalent floats. This fixes non-conformant behavior in python. PiperOrigin-RevId: 704529468 --- conformance/failure_list_python.txt | 1 - conformance/failure_list_python_cpp.txt | 1 - conformance/failure_list_python_upb.txt | 1 - .../protobuf/internal/json_format_test.py | 19 +++++++++++++++++++ python/google/protobuf/json_format.py | 15 ++++++++++++++- 5 files changed, 33 insertions(+), 4 deletions(-) diff --git a/conformance/failure_list_python.txt b/conformance/failure_list_python.txt index e7b56042a784..e69de29bb2d1 100644 --- a/conformance/failure_list_python.txt +++ b/conformance/failure_list_python.txt @@ -1 +0,0 @@ -Required.*.JsonInput.Int32FieldQuotedExponentialValue.* # Failed to parse input or produce output. diff --git a/conformance/failure_list_python_cpp.txt b/conformance/failure_list_python_cpp.txt index c65251cdf04d..5c934051605a 100644 --- a/conformance/failure_list_python_cpp.txt +++ b/conformance/failure_list_python_cpp.txt @@ -6,4 +6,3 @@ # # TODO: insert links to corresponding bugs tracking the issue. # Should we use GitHub issues or the Google-internal bug tracker? -Required.*.JsonInput.Int32FieldQuotedExponentialValue.* # Failed to parse input or produce output. diff --git a/conformance/failure_list_python_upb.txt b/conformance/failure_list_python_upb.txt index e7b56042a784..e69de29bb2d1 100644 --- a/conformance/failure_list_python_upb.txt +++ b/conformance/failure_list_python_upb.txt @@ -1 +0,0 @@ -Required.*.JsonInput.Int32FieldQuotedExponentialValue.* # Failed to parse input or produce output. diff --git a/python/google/protobuf/internal/json_format_test.py b/python/google/protobuf/internal/json_format_test.py index 54fefd0ef6b6..f323d0e23a26 100644 --- a/python/google/protobuf/internal/json_format_test.py +++ b/python/google/protobuf/internal/json_format_test.py @@ -321,6 +321,15 @@ def testIntegersRepresentedAsFloat(self): json_format.Parse('{"int32Value": 1.0}', message) self.assertEqual(message.int32_value, 1) + def testIntegersRepresentedAsFloatStrings(self): + message = json_format_proto3_pb2.TestMessage() + json_format.Parse('{"int32Value": "-2.147483648e9"}', message) + self.assertEqual(message.int32_value, -2147483648) + json_format.Parse('{"int32Value": "1e5"}', message) + self.assertEqual(message.int32_value, 100000) + json_format.Parse('{"int32Value": "1.0"}', message) + self.assertEqual(message.int32_value, 1) + def testMapFields(self): message = json_format_proto3_pb2.TestNestedMap() self.assertEqual( @@ -1167,6 +1176,16 @@ def testInvalidIntegerValue(self): 'Failed to parse int32Value field: ' "Couldn't parse integer: 1.5 at TestMessage.int32Value.", ) + self.CheckError( + '{"int32Value": "1.5"}', + 'Failed to parse int32Value field: ' + 'Couldn\'t parse non-integer string: "1.5" at TestMessage.int32Value.', + ) + self.CheckError( + '{"int32Value": "foo"}', + 'Failed to parse int32Value field: invalid literal for int\(\) with' + " base 10: 'foo'.", + ) self.CheckError( '{"int32Value": 012345}', (r'Failed to load JSON: Expecting \'?,\'? delimiter: ' r'line 1.'), diff --git a/python/google/protobuf/json_format.py b/python/google/protobuf/json_format.py index cde74ab0fddc..7891be94ba52 100644 --- a/python/google/protobuf/json_format.py +++ b/python/google/protobuf/json_format.py @@ -972,7 +972,20 @@ def _ConvertInteger(value): 'Bool value {0} is not acceptable for integer field'.format(value) ) - return int(value) + try: + return int(value) + except ValueError as e: + # Attempt to parse as an integer-valued float. + try: + f = float(value) + except ValueError: + # Raise the original exception for the int parse. + raise e # pylint: disable=raise-missing-from + if not f.is_integer(): + raise ParseError( + 'Couldn\'t parse non-integer string: "{0}"'.format(value) + ) from e + return int(f) def _ConvertFloat(value, field):