Support tuples when parsing JSON dicts

PiperOrigin-RevId: 696911739
diff --git a/csharp/src/Google.Protobuf/Reflection/FeatureSetDescriptor.g.cs b/csharp/src/Google.Protobuf/Reflection/FeatureSetDescriptor.g.cs
deleted file mode 100644
index 208ce1f..0000000
--- a/csharp/src/Google.Protobuf/Reflection/FeatureSetDescriptor.g.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-#region Copyright notice and license
-// Protocol Buffers - Google's data interchange format
-// Copyright 2008 Google Inc.  All rights reserved.
-//
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file or at
-// https://developers.google.com/open-source/licenses/bsd
-#endregion
-
-namespace Google.Protobuf.Reflection;
-
-internal sealed partial class FeatureSetDescriptor
-{
-    // Canonical serialized form of the edition defaults, generated by embed_edition_defaults.
-    private const string DefaultsBase64 =
-        "ChMYhAciACoMCAEQAhgCIAMoATACChMY5wciACoMCAIQARgBIAIoATABChMY6AciDAgBEAEYASACKAEwASoAIOYHKOgH";
-}
diff --git a/python/google/protobuf/internal/json_format_test.py b/python/google/protobuf/internal/json_format_test.py
index f7a124d..54fefd0 100644
--- a/python/google/protobuf/internal/json_format_test.py
+++ b/python/google/protobuf/internal/json_format_test.py
@@ -597,8 +597,8 @@
     parsed_message = json_format_proto3_pb2.TestStruct()
     self.CheckParseBack(message, parsed_message)
     # check for regression; this used to raise
-    parsed_message.value['empty_struct']
-    parsed_message.value['empty_list']
+    _ = parsed_message.value['empty_struct']
+    _ = parsed_message.value['empty_list']
 
   def testValueMessage(self):
     message = json_format_proto3_pb2.TestValue()
@@ -1530,6 +1530,30 @@
     json_format.ParseDict(js_dict, message)
     self.assertEqual(expected, message.int32_value)
 
+  def testParseDictAcceptsPairValueTuples(self):
+    expected = [1, 2, 3]
+    js_dict = {'repeatedInt32Value': (1, 2, 3)}
+    message = json_format_proto3_pb2.TestMessage()
+    json_format.ParseDict(js_dict, message)
+    self.assertEqual(expected, message.repeated_int32_value)
+
+  def testParseDictAcceptsRepeatedValueTuples(self):
+    expected = json_format_proto3_pb2.TestListValue(
+        repeated_value=[
+            struct_pb2.ListValue(
+                values=[
+                    struct_pb2.Value(number_value=4),
+                    struct_pb2.Value(number_value=5),
+                ]
+            ),
+            struct_pb2.ListValue(values=[struct_pb2.Value(number_value=6)]),
+        ]
+    )
+    js_dict = {'repeated_value': ((4, 5), (6,))}
+    message = json_format_proto3_pb2.TestListValue()
+    json_format.ParseDict(js_dict, message)
+    self.assertEqual(expected, message)
+
   def testParseDictAnyDescriptorPoolMissingType(self):
     # Confirm that ParseDict does not raise ParseError with default pool
     js_dict = {
diff --git a/python/google/protobuf/json_format.py b/python/google/protobuf/json_format.py
index 2a6bba9..cde74ab 100644
--- a/python/google/protobuf/json_format.py
+++ b/python/google/protobuf/json_format.py
@@ -497,6 +497,7 @@
 
 
 _INT_OR_FLOAT = (int, float)
+_LIST_LIKE = (list, tuple)
 
 
 class _Parser(object):
@@ -638,7 +639,7 @@
           )
         elif field.label == descriptor.FieldDescriptor.LABEL_REPEATED:
           message.ClearField(field.name)
-          if not isinstance(value, list):
+          if not isinstance(value, _LIST_LIKE):
             raise ParseError(
                 'repeated field {0} must be in [] which is {1} at {2}'.format(
                     name, value, path
@@ -752,8 +753,8 @@
     """Convert a JSON representation into Value message."""
     if isinstance(value, dict):
       self._ConvertStructMessage(value, message.struct_value, path)
-    elif isinstance(value, list):
-      self._ConvertListValueMessage(value, message.list_value, path)
+    elif isinstance(value, _LIST_LIKE):
+      self._ConvertListOrTupleValueMessage(value, message.list_value, path)
     elif value is None:
       message.null_value = 0
     elif isinstance(value, bool):
@@ -769,9 +770,9 @@
           )
       )
 
-  def _ConvertListValueMessage(self, value, message, path):
+  def _ConvertListOrTupleValueMessage(self, value, message, path):
     """Convert a JSON representation into ListValue message."""
-    if not isinstance(value, list):
+    if not isinstance(value, _LIST_LIKE):
       raise ParseError(
           'ListValue must be in [] which is {0} at {1}'.format(value, path)
       )
@@ -1052,7 +1053,7 @@
     ],
     'google.protobuf.ListValue': [
         '_ListValueMessageToJsonObject',
-        '_ConvertListValueMessage',
+        '_ConvertListOrTupleValueMessage',
     ],
     'google.protobuf.Struct': [
         '_StructMessageToJsonObject',