Sync from Piper @429333699

PROTOBUF_SYNC_PIPER
diff --git a/python/google/protobuf/__init__.py b/python/google/protobuf/__init__.py
index b8122e8..9319c67 100644
--- a/python/google/protobuf/__init__.py
+++ b/python/google/protobuf/__init__.py
@@ -30,4 +30,4 @@
 
 # Copyright 2007 Google Inc. All Rights Reserved.
 
-__version__ = '3.18.1'
+__version__ = '3.19.4'
diff --git a/python/google/protobuf/descriptor.py b/python/google/protobuf/descriptor.py
index 9615a58..ad70be9 100644
--- a/python/google/protobuf/descriptor.py
+++ b/python/google/protobuf/descriptor.py
@@ -667,7 +667,7 @@
     full_name (str): Full name of the type, including package name
       and any enclosing type(s).
 
-    values (list[EnumValueDescriptors]): List of the values
+    values (list[EnumValueDescriptor]): List of the values
       in this enum.
     values_by_name (dict(str, EnumValueDescriptor)): Same as :attr:`values`,
       but indexed by the "name" field of each EnumValueDescriptor.
diff --git a/python/google/protobuf/internal/_parameterized.py b/python/google/protobuf/internal/_parameterized.py
index 6b6ba65..afdbb78 100755
--- a/python/google/protobuf/internal/_parameterized.py
+++ b/python/google/protobuf/internal/_parameterized.py
@@ -148,10 +148,7 @@
 import functools
 import re
 import types
-try:
-  import unittest2 as unittest
-except ImportError:
-  import unittest
+import unittest
 import uuid
 
 try:
diff --git a/python/google/protobuf/internal/api_implementation.cc b/python/google/protobuf/internal/api_implementation.cc
index 241942e..33f5b04 100644
--- a/python/google/protobuf/internal/api_implementation.cc
+++ b/python/google/protobuf/internal/api_implementation.cc
@@ -28,6 +28,7 @@
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+#define PY_SSIZE_T_CLEAN
 #include <Python.h>
 
 namespace google {
diff --git a/python/google/protobuf/internal/descriptor_database_test.py b/python/google/protobuf/internal/descriptor_database_test.py
index ad3ca64..3c086b9 100644
--- a/python/google/protobuf/internal/descriptor_database_test.py
+++ b/python/google/protobuf/internal/descriptor_database_test.py
@@ -100,7 +100,7 @@
     self.assertEqual(file_desc_proto2, db.FindFileContainingSymbol(
         'protobuf_unittest.TestAllTypes.none_field'))
 
-    with self.assertRaisesRegexp(KeyError, r'\'protobuf_unittest\.NoneMessage\''):
+    with self.assertRaisesRegex(KeyError, r'\'protobuf_unittest\.NoneMessage\''):
       db.FindFileContainingSymbol('protobuf_unittest.NoneMessage')
 
   def testConflictRegister(self):
diff --git a/python/google/protobuf/internal/descriptor_pool_test.py b/python/google/protobuf/internal/descriptor_pool_test.py
index 501c9a6..61cec3f 100644
--- a/python/google/protobuf/internal/descriptor_pool_test.py
+++ b/python/google/protobuf/internal/descriptor_pool_test.py
@@ -654,11 +654,11 @@
     enum_value.number = 0
     self.db.Add(file_proto)
 
-    self.assertRaisesRegexp(KeyError, 'SubMessage',
-                            self.pool.FindMessageTypeByName,
-                            'collector.ErrorMessage')
-    self.assertRaisesRegexp(KeyError, 'SubMessage',
-                            self.pool.FindFileByName, 'error_file')
+    self.assertRaisesRegex(KeyError, 'SubMessage',
+                           self.pool.FindMessageTypeByName,
+                           'collector.ErrorMessage')
+    self.assertRaisesRegex(KeyError, 'SubMessage', self.pool.FindFileByName,
+                           'error_file')
     with self.assertRaises(KeyError) as exc:
       self.pool.FindFileByName('none_file')
     self.assertIn(str(exc.exception), ('\'none_file\'',
diff --git a/python/google/protobuf/internal/enum_type_wrapper.py b/python/google/protobuf/internal/enum_type_wrapper.py
index 9a53999..65d2c7a 100644
--- a/python/google/protobuf/internal/enum_type_wrapper.py
+++ b/python/google/protobuf/internal/enum_type_wrapper.py
@@ -43,6 +43,15 @@
 
   DESCRIPTOR = None
 
+  # This is a type alias, which mypy typing stubs can type as
+  # a genericized parameter constrained to an int, allowing subclasses
+  # to be typed with more constraint in .pyi stubs
+  # Eg.
+  # def MyGeneratedEnum(Message):
+  #   ValueType = NewType('ValueType', int)
+  #   def Name(self, number: MyGeneratedEnum.ValueType) -> str
+  ValueType = int
+
   def __init__(self, enum_type):
     """Inits EnumTypeWrapper with an EnumDescriptor."""
     self._enum_type = enum_type
diff --git a/python/google/protobuf/internal/generator_test.py b/python/google/protobuf/internal/generator_test.py
index 0c4299a..9883fce 100644
--- a/python/google/protobuf/internal/generator_test.py
+++ b/python/google/protobuf/internal/generator_test.py
@@ -39,10 +39,7 @@
 
 __author__ = 'robinson@google.com (Will Robinson)'
 
-try:
-  import unittest2 as unittest  #PY26
-except ImportError:
-  import unittest
+import unittest
 
 from google.protobuf.internal import test_bad_identifiers_pb2
 from google.protobuf import unittest_custom_options_pb2
diff --git a/python/google/protobuf/internal/json_format_test.py b/python/google/protobuf/internal/json_format_test.py
index 3545563..d018c3f 100644
--- a/python/google/protobuf/internal/json_format_test.py
+++ b/python/google/protobuf/internal/json_format_test.py
@@ -100,10 +100,8 @@
 
   def CheckError(self, text, error_message):
     message = json_format_proto3_pb2.TestMessage()
-    self.assertRaisesRegexp(
-        json_format.ParseError,
-        error_message,
-        json_format.Parse, text, message)
+    self.assertRaisesRegex(json_format.ParseError, error_message,
+                           json_format.Parse, text, message)
 
 
 class JsonFormatTest(JsonFormatBase):
@@ -812,9 +810,8 @@
     json_format.Parse('{"messageValue": {}}', parsed_message)
     self.assertTrue(parsed_message.HasField('message_value'))
     # Null is not allowed to be used as an element in repeated field.
-    self.assertRaisesRegexp(
-        json_format.ParseError,
-        r'Failed to parse repeatedInt32Value field: '
+    self.assertRaisesRegex(
+        json_format.ParseError, r'Failed to parse repeatedInt32Value field: '
         r'null is not allowed to be used as an element in a repeated field '
         r'at TestMessage.repeatedInt32Value\[1\].', json_format.Parse,
         '{"repeatedInt32Value":[1, null]}', parsed_message)
@@ -901,7 +898,7 @@
     json_format.Parse(text, message)
     # Proto2 does not accept unknown enums.
     message = unittest_pb2.TestAllTypes()
-    self.assertRaisesRegexp(
+    self.assertRaisesRegex(
         json_format.ParseError,
         'Failed to parse optionalNestedEnum field: Invalid enum value 12345 '
         'for enum type protobuf_unittest.TestAllTypes.NestedEnum at '
@@ -1019,28 +1016,25 @@
   def testInvalidMap(self):
     message = json_format_proto3_pb2.TestMap()
     text = '{"int32Map": {"null": 2, "2": 3}}'
-    self.assertRaisesRegexp(
-        json_format.ParseError,
-        'Failed to parse int32Map field: invalid literal',
-        json_format.Parse, text, message)
+    self.assertRaisesRegex(json_format.ParseError,
+                           'Failed to parse int32Map field: invalid literal',
+                           json_format.Parse, text, message)
     text = '{"int32Map": {1: 2, "2": 3}}'
-    self.assertRaisesRegexp(
-        json_format.ParseError,
-        (r'Failed to load JSON: Expecting property name'
-         r'( enclosed in double quotes)?: line 1'),
-        json_format.Parse, text, message)
+    self.assertRaisesRegex(json_format.ParseError,
+                           (r'Failed to load JSON: Expecting property name'
+                            r'( enclosed in double quotes)?: line 1'),
+                           json_format.Parse, text, message)
     text = '{"boolMap": {"null": 1}}'
-    self.assertRaisesRegexp(
+    self.assertRaisesRegex(
         json_format.ParseError,
         'Failed to parse boolMap field: Expected "true" or "false", not null at '
         'TestMap.boolMap.key', json_format.Parse, text, message)
     text = r'{"stringMap": {"a": 3, "\u0061": 2}}'
-    self.assertRaisesRegexp(
-        json_format.ParseError,
-        'Failed to load JSON: duplicate key a',
-        json_format.Parse, text, message)
+    self.assertRaisesRegex(json_format.ParseError,
+                           'Failed to load JSON: duplicate key a',
+                           json_format.Parse, text, message)
     text = r'{"stringMap": 0}'
-    self.assertRaisesRegexp(
+    self.assertRaisesRegex(
         json_format.ParseError,
         'Failed to parse stringMap field: Map field string_map must be '
         'in a dict which is 0 at TestMap.stringMap.', json_format.Parse, text,
@@ -1055,27 +1049,24 @@
         ' format \'%Y-%m-%dT%H:%M:%S\' at TestTimestamp.value.',
         json_format.Parse, text, message)
     text = '{"value": "1970-01-01T00:00:00.0123456789012Z"}'
-    self.assertRaisesRegexp(
+    self.assertRaisesRegex(
         json_format.ParseError,
-        'nanos 0123456789012 more than 9 fractional digits.',
-        json_format.Parse, text, message)
+        'nanos 0123456789012 more than 9 fractional digits.', json_format.Parse,
+        text, message)
     text = '{"value": "1972-01-01T01:00:00.01+08"}'
-    self.assertRaisesRegexp(
-        json_format.ParseError,
-        (r'Invalid timezone offset value: \+08.'),
-        json_format.Parse, text, message)
+    self.assertRaisesRegex(json_format.ParseError,
+                           (r'Invalid timezone offset value: \+08.'),
+                           json_format.Parse, text, message)
     # Time smaller than minimum time.
     text = '{"value": "0000-01-01T00:00:00Z"}'
-    self.assertRaisesRegexp(
+    self.assertRaisesRegex(
         json_format.ParseError,
         'Failed to parse value field: year (0 )?is out of range.',
         json_format.Parse, text, message)
     # Time bigger than maximum time.
     message.value.seconds = 253402300800
-    self.assertRaisesRegexp(
-        OverflowError,
-        'date value out of range',
-        json_format.MessageToJson, message)
+    self.assertRaisesRegex(OverflowError, 'date value out of range',
+                           json_format.MessageToJson, message)
     # Lower case t does not accept.
     text = '{"value": "0001-01-01t00:00:00Z"}'
     with self.assertRaises(json_format.ParseError) as e:
@@ -1097,11 +1088,10 @@
   def testInvalidListValue(self):
     message = json_format_proto3_pb2.TestListValue()
     text = '{"value": 1234}'
-    self.assertRaisesRegexp(
+    self.assertRaisesRegex(
         json_format.ParseError,
         r'Failed to parse value field: ListValue must be in \[\] which is '
-        '1234 at TestListValue.value.',
-        json_format.Parse, text, message)
+        '1234 at TestListValue.value.', json_format.Parse, text, message)
 
     class UnknownClass(object):
 
@@ -1116,16 +1106,15 @@
   def testInvalidStruct(self):
     message = json_format_proto3_pb2.TestStruct()
     text = '{"value": 1234}'
-    self.assertRaisesRegexp(
+    self.assertRaisesRegex(
         json_format.ParseError,
         'Failed to parse value field: Struct must be in a dict which is '
-        '1234 at TestStruct.value',
-        json_format.Parse, text, message)
+        '1234 at TestStruct.value', json_format.Parse, text, message)
 
   def testTimestampInvalidStringValue(self):
     message = json_format_proto3_pb2.TestTimestamp()
     text = '{"value": {"foo": 123}}'
-    self.assertRaisesRegexp(
+    self.assertRaisesRegex(
         json_format.ParseError,
         r"Timestamp JSON value not a string: {u?'foo': 123}", json_format.Parse,
         text, message)
@@ -1133,15 +1122,14 @@
   def testDurationInvalidStringValue(self):
     message = json_format_proto3_pb2.TestDuration()
     text = '{"value": {"foo": 123}}'
-    self.assertRaisesRegexp(
-        json_format.ParseError,
-        r"Duration JSON value not a string: {u?'foo': 123}", json_format.Parse,
-        text, message)
+    self.assertRaisesRegex(json_format.ParseError,
+                           r"Duration JSON value not a string: {u?'foo': 123}",
+                           json_format.Parse, text, message)
 
   def testFieldMaskInvalidStringValue(self):
     message = json_format_proto3_pb2.TestFieldMask()
     text = '{"value": {"foo": 123}}'
-    self.assertRaisesRegexp(
+    self.assertRaisesRegex(
         json_format.ParseError,
         r"FieldMask JSON value not a string: {u?'foo': 123}", json_format.Parse,
         text, message)
@@ -1149,10 +1137,7 @@
   def testInvalidAny(self):
     message = any_pb2.Any()
     text = '{"@type": "type.googleapis.com/google.protobuf.Int32Value"}'
-    self.assertRaisesRegexp(
-        KeyError,
-        'value',
-        json_format.Parse, text, message)
+    self.assertRaisesRegex(KeyError, 'value', json_format.Parse, text, message)
     text = '{"value": 1234}'
     self.assertRaisesRegex(json_format.ParseError,
                            '@type is missing when parsing any message at Any',
@@ -1247,12 +1232,10 @@
       def __repr__(self):
         return 'v'
     message = json_format_proto3_pb2.TestValue()
-    self.assertRaisesRegexp(
+    self.assertRaisesRegex(
         json_format.ParseError,
         r"Value v has unexpected type <class '.*\.UnknownClass'>.",
-        json_format.ParseDict,
-        {'value': UnknownClass()},
-        message)
+        json_format.ParseDict, {'value': UnknownClass()}, message)
 
   def testMessageToDict(self):
     message = json_format_proto3_pb2.TestMessage()
diff --git a/python/google/protobuf/internal/message_test.py b/python/google/protobuf/internal/message_test.py
index e8d86a2..a5c5a0d 100644
--- a/python/google/protobuf/internal/message_test.py
+++ b/python/google/protobuf/internal/message_test.py
@@ -75,21 +75,19 @@
 
 UCS2_MAXUNICODE = 65535
 
-
 warnings.simplefilter('error', DeprecationWarning)
 
 
-@_parameterized.named_parameters(
-    ('_proto2', unittest_pb2),
-    ('_proto3', unittest_proto3_arena_pb2))
+@_parameterized.named_parameters(('_proto2', unittest_pb2),
+                                ('_proto3', unittest_proto3_arena_pb2))
 @testing_refleaks.TestCase
 class MessageTest(unittest.TestCase):
 
   def testBadUtf8String(self, message_module):
     if api_implementation.Type() != 'python':
-      self.skipTest("Skipping testBadUtf8String, currently only the python "
-                    "api implementation raises UnicodeDecodeError when a "
-                    "string field contains bad utf-8.")
+      self.skipTest('Skipping testBadUtf8String, currently only the python '
+                    'api implementation raises UnicodeDecodeError when a '
+                    'string field contains bad utf-8.')
     bad_utf8_data = test_util.GoldenFileData('bad_utf8_string')
     with self.assertRaises(UnicodeDecodeError) as context:
       message_module.TestAllTypes.FromString(bad_utf8_data)
@@ -100,8 +98,7 @@
     # and doesn't preserve unknown fields, so for proto3 we use a golden
     # message that doesn't have these fields set.
     if message_module is unittest_pb2:
-      golden_data = test_util.GoldenFileData(
-          'golden_message_oneof_implemented')
+      golden_data = test_util.GoldenFileData('golden_message_oneof_implemented')
     else:
       golden_data = test_util.GoldenFileData('golden_message_proto3')
 
@@ -440,8 +437,7 @@
     except TypeError:
       pass
     self.assertEqual(2, len(msg.repeated_nested_message))
-    self.assertEqual([1, 2],
-                     [m.bb for m in msg.repeated_nested_message])
+    self.assertEqual([1, 2], [m.bb for m in msg.repeated_nested_message])
 
   def testInsertRepeatedCompositeField(self, message_module):
     msg = message_module.TestAllTypes()
@@ -463,22 +459,22 @@
     self.assertEqual(5, len(msg.repeated_nested_message))
     self.assertEqual([-1000, 2, -1, 1, 3],
                      [m.bb for m in msg.repeated_nested_message])
-    self.assertEqual(str(msg),
-                     'repeated_nested_message {\n'
-                     '  bb: -1000\n'
-                     '}\n'
-                     'repeated_nested_message {\n'
-                     '  bb: 2\n'
-                     '}\n'
-                     'repeated_nested_message {\n'
-                     '  bb: -1\n'
-                     '}\n'
-                     'repeated_nested_message {\n'
-                     '  bb: 1\n'
-                     '}\n'
-                     'repeated_nested_message {\n'
-                     '  bb: 3\n'
-                     '}\n')
+    self.assertEqual(
+        str(msg), 'repeated_nested_message {\n'
+        '  bb: -1000\n'
+        '}\n'
+        'repeated_nested_message {\n'
+        '  bb: 2\n'
+        '}\n'
+        'repeated_nested_message {\n'
+        '  bb: -1\n'
+        '}\n'
+        'repeated_nested_message {\n'
+        '  bb: 1\n'
+        '}\n'
+        'repeated_nested_message {\n'
+        '  bb: 3\n'
+        '}\n')
     self.assertEqual(sub_msg.bb, 1)
 
   def testMergeFromRepeatedField(self, message_module):
@@ -497,8 +493,7 @@
     self.assertEqual(4, len(msg.repeated_int32))
 
     msg.repeated_nested_message.MergeFrom(other_msg.repeated_nested_message)
-    self.assertEqual([1, 2, 3, 4],
-                     [m.bb for m in msg.repeated_nested_message])
+    self.assertEqual([1, 2, 3, 4], [m.bb for m in msg.repeated_nested_message])
 
   def testAddWrongRepeatedNestedField(self, message_module):
     msg = message_module.TestAllTypes()
@@ -543,8 +538,7 @@
     msg.repeated_nested_message.add(bb=3)
     msg.repeated_nested_message.add(bb=4)
 
-    self.assertEqual([1, 2, 3, 4],
-                     [m.bb for m in msg.repeated_nested_message])
+    self.assertEqual([1, 2, 3, 4], [m.bb for m in msg.repeated_nested_message])
     self.assertEqual([4, 3, 2, 1],
                      [m.bb for m in reversed(msg.repeated_nested_message)])
     self.assertEqual([4, 3, 2, 1],
@@ -627,8 +621,9 @@
     self.assertEqual(message.repeated_nested_message[3].bb, 4)
     self.assertEqual(message.repeated_nested_message[4].bb, 5)
     self.assertEqual(message.repeated_nested_message[5].bb, 6)
-    self.assertEqual(str(message.repeated_nested_message),
-                     '[bb: 1\n, bb: 2\n, bb: 3\n, bb: 4\n, bb: 5\n, bb: 6\n]')
+    self.assertEqual(
+        str(message.repeated_nested_message),
+        '[bb: 1\n, bb: 2\n, bb: 3\n, bb: 4\n, bb: 5\n, bb: 6\n]')
 
   def testSortingRepeatedCompositeFieldsStable(self, message_module):
     """Check passing a custom comparator to sort a repeated composite field."""
@@ -642,18 +637,16 @@
     message.repeated_nested_message.add().bb = 24
     message.repeated_nested_message.add().bb = 10
     message.repeated_nested_message.sort(key=lambda z: z.bb // 10)
-    self.assertEqual(
-        [13, 11, 10, 21, 20, 24, 33],
-        [n.bb for n in message.repeated_nested_message])
+    self.assertEqual([13, 11, 10, 21, 20, 24, 33],
+                     [n.bb for n in message.repeated_nested_message])
 
     # Make sure that for the C++ implementation, the underlying fields
     # are actually reordered.
     pb = message.SerializeToString()
     message.Clear()
     message.MergeFromString(pb)
-    self.assertEqual(
-        [13, 11, 10, 21, 20, 24, 33],
-        [n.bb for n in message.repeated_nested_message])
+    self.assertEqual([13, 11, 10, 21, 20, 24, 33],
+                     [n.bb for n in message.repeated_nested_message])
 
   def testRepeatedCompositeFieldSortArguments(self, message_module):
     """Check sorting a repeated composite field using list.sort() arguments."""
@@ -826,7 +819,7 @@
     self.assertTrue(m.HasField('oneof_uint32'))
     self.assertFalse(m.HasField('oneof_string'))
 
-    m.oneof_string = ""
+    m.oneof_string = ''
     self.assertEqual('oneof_string', m.WhichOneof('oneof_field'))
     self.assertTrue(m.HasField('oneof_string'))
     self.assertFalse(m.HasField('oneof_uint32'))
@@ -973,7 +966,9 @@
 
   def testAssignByteStringToUnicodeField(self, message_module):
     """Assigning a byte string to a string field should result
-    in the value being converted to a Unicode string."""
+
+    in the value being converted to a Unicode string.
+    """
     m = message_module.TestAllTypes()
     m.optional_string = str('')
     self.assertIsInstance(m.optional_string, str)
@@ -1001,8 +996,7 @@
     with self.assertRaises(NameError) as _:
       m.repeated_int32.extend(a for i in range(10))  # pylint: disable=undefined-variable
     with self.assertRaises(NameError) as _:
-      m.repeated_nested_enum.extend(
-          a for i in range(10))  # pylint: disable=undefined-variable
+      m.repeated_nested_enum.extend(a for i in range(10))  # pylint: disable=undefined-variable
 
   FALSY_VALUES = [None, False, 0, 0.0, b'', u'', bytearray(), [], {}, set()]
 
@@ -1179,14 +1173,12 @@
       pickle.dumps(m.repeated_int32, pickle.HIGHEST_PROTOCOL)
 
   def testSortEmptyRepeatedCompositeContainer(self, message_module):
-    """Exercise a scenario that has led to segfaults in the past.
-    """
+    """Exercise a scenario that has led to segfaults in the past."""
     m = message_module.TestAllTypes()
     m.repeated_nested_message.sort()
 
   def testHasFieldOnRepeatedField(self, message_module):
-    """Using HasField on a repeated field should raise an exception.
-    """
+    """Using HasField on a repeated field should raise an exception."""
     m = message_module.TestAllTypes()
     with self.assertRaises(ValueError) as _:
       m.HasField('repeated_int32')
@@ -1226,6 +1218,7 @@
 
   def testReleasedNestedMessages(self, message_module):
     """A case that lead to a segfault when a message detached from its parent
+
     container has itself a child container.
     """
     m = message_module.NestedTestAllTypes()
@@ -1271,17 +1264,17 @@
   def testFieldPresence(self):
     message = unittest_pb2.TestAllTypes()
 
-    self.assertFalse(message.HasField("optional_int32"))
-    self.assertFalse(message.HasField("optional_bool"))
-    self.assertFalse(message.HasField("optional_nested_message"))
+    self.assertFalse(message.HasField('optional_int32'))
+    self.assertFalse(message.HasField('optional_bool'))
+    self.assertFalse(message.HasField('optional_nested_message'))
 
     with self.assertRaises(ValueError):
-      message.HasField("field_doesnt_exist")
+      message.HasField('field_doesnt_exist')
 
     with self.assertRaises(ValueError):
-      message.HasField("repeated_int32")
+      message.HasField('repeated_int32')
     with self.assertRaises(ValueError):
-      message.HasField("repeated_nested_message")
+      message.HasField('repeated_nested_message')
 
     self.assertEqual(0, message.optional_int32)
     self.assertEqual(False, message.optional_bool)
@@ -1291,27 +1284,27 @@
     message.optional_int32 = 0
     message.optional_bool = False
     message.optional_nested_message.bb = 0
-    self.assertTrue(message.HasField("optional_int32"))
-    self.assertTrue(message.HasField("optional_bool"))
-    self.assertTrue(message.HasField("optional_nested_message"))
+    self.assertTrue(message.HasField('optional_int32'))
+    self.assertTrue(message.HasField('optional_bool'))
+    self.assertTrue(message.HasField('optional_nested_message'))
 
     # Set the fields to non-default values.
     message.optional_int32 = 5
     message.optional_bool = True
     message.optional_nested_message.bb = 15
 
-    self.assertTrue(message.HasField(u"optional_int32"))
-    self.assertTrue(message.HasField("optional_bool"))
-    self.assertTrue(message.HasField("optional_nested_message"))
+    self.assertTrue(message.HasField(u'optional_int32'))
+    self.assertTrue(message.HasField('optional_bool'))
+    self.assertTrue(message.HasField('optional_nested_message'))
 
     # Clearing the fields unsets them and resets their value to default.
-    message.ClearField("optional_int32")
-    message.ClearField(u"optional_bool")
-    message.ClearField("optional_nested_message")
+    message.ClearField('optional_int32')
+    message.ClearField(u'optional_bool')
+    message.ClearField('optional_nested_message')
 
-    self.assertFalse(message.HasField("optional_int32"))
-    self.assertFalse(message.HasField("optional_bool"))
-    self.assertFalse(message.HasField("optional_nested_message"))
+    self.assertFalse(message.HasField('optional_int32'))
+    self.assertFalse(message.HasField('optional_bool'))
+    self.assertFalse(message.HasField('optional_nested_message'))
     self.assertEqual(0, message.optional_int32)
     self.assertEqual(False, message.optional_bool)
     self.assertEqual(0, message.optional_nested_message.bb)
@@ -1361,16 +1354,17 @@
     msg1 = more_extensions_pb2.TopLevelMessage()
     msg2 = more_extensions_pb2.TopLevelMessage()
     # Cpp extension will lazily create a sub message which is immutable.
-    self.assertEqual(0, msg1.submessage.Extensions[
-        more_extensions_pb2.optional_int_extension])
+    self.assertEqual(
+        0,
+        msg1.submessage.Extensions[more_extensions_pb2.optional_int_extension])
     self.assertFalse(msg1.HasField('submessage'))
-    msg2.submessage.Extensions[
-        more_extensions_pb2.optional_int_extension] = 123
+    msg2.submessage.Extensions[more_extensions_pb2.optional_int_extension] = 123
     # Make sure cmessage and extensions pointing to a mutable message
     # after merge instead of the lazily created message.
     msg1.MergeFrom(msg2)
-    self.assertEqual(123, msg1.submessage.Extensions[
-        more_extensions_pb2.optional_int_extension])
+    self.assertEqual(
+        123,
+        msg1.submessage.Extensions[more_extensions_pb2.optional_int_extension])
 
   def testGoldenExtensions(self):
     golden_data = test_util.GoldenFileData('golden_message')
@@ -1404,17 +1398,19 @@
     # This is still an incomplete proto - so serializing should fail
     self.assertRaises(message.EncodeError, unpickled_message.SerializeToString)
 
-
   # TODO(haberman): this isn't really a proto2-specific test except that this
   # message has a required field in it.  Should probably be factored out so
   # that we can test the other parts with proto3.
   def testParsingMerge(self):
     """Check the merge behavior when a required or optional field appears
-    multiple times in the input."""
+
+    multiple times in the input.
+    """
     messages = [
         unittest_pb2.TestAllTypes(),
         unittest_pb2.TestAllTypes(),
-        unittest_pb2.TestAllTypes() ]
+        unittest_pb2.TestAllTypes()
+    ]
     messages[0].optional_int32 = 1
     messages[1].optional_int64 = 2
     messages[2].optional_int32 = 3
@@ -1447,15 +1443,16 @@
     self.assertEqual(parsing_merge.optional_all_types, merged_message)
     self.assertEqual(parsing_merge.optionalgroup.optional_group_all_types,
                      merged_message)
-    self.assertEqual(parsing_merge.Extensions[
-                     unittest_pb2.TestParsingMerge.optional_ext],
-                     merged_message)
+    self.assertEqual(
+        parsing_merge.Extensions[unittest_pb2.TestParsingMerge.optional_ext],
+        merged_message)
 
     # Repeated fields should not be merged.
     self.assertEqual(len(parsing_merge.repeated_all_types), 3)
     self.assertEqual(len(parsing_merge.repeatedgroup), 3)
-    self.assertEqual(len(parsing_merge.Extensions[
-        unittest_pb2.TestParsingMerge.repeated_ext]), 3)
+    self.assertEqual(
+        len(parsing_merge.Extensions[
+            unittest_pb2.TestParsingMerge.repeated_ext]), 3)
 
   def testPythonicInit(self):
     message = unittest_pb2.TestAllTypes(
@@ -1467,8 +1464,11 @@
         optional_nested_message={'bb': 500},
         optional_foreign_message={},
         optional_nested_enum='BAZ',
-        repeatedgroup=[{'a': 600},
-                       {'a': 700}],
+        repeatedgroup=[{
+            'a': 600
+        }, {
+            'a': 700
+        }],
         repeated_nested_enum=['FOO', unittest_pb2.TestAllTypes.BAR],
         default_int32=800,
         oneof_string='y')
@@ -1848,8 +1848,7 @@
     self.assertEqual(True, msg2.map_bool_bool[True])
     self.assertEqual(2, msg2.map_int32_enum[888])
     self.assertEqual(456, msg2.map_int32_enum[123])
-    self.assertEqual('{-123: -456}',
-                     str(msg2.map_int32_int32))
+    self.assertEqual('{-123: -456}', str(msg2.map_int32_int32))
 
   def testMapEntryAlwaysSerialized(self):
     msg = map_unittest_pb2.TestMap()
@@ -1912,8 +1911,9 @@
     self.assertEqual(2, len(msg2.map_int32_foreign_message))
     msg2.map_int32_foreign_message[123].c = 1
     # TODO(jieluo): Fix text format for message map.
-    self.assertIn(str(msg2.map_int32_foreign_message),
-                  ('{-456: , 123: c: 1\n}', '{123: c: 1\n, -456: }'))
+    self.assertIn(
+        str(msg2.map_int32_foreign_message),
+        ('{-456: , 123: c: 1\n}', '{123: c: 1\n, -456: }'))
 
   def testNestedMessageMapItemDelete(self):
     msg = map_unittest_pb2.TestMap()
@@ -2041,8 +2041,7 @@
     # Test when cpp extension cache a map.
     m1 = map_unittest_pb2.TestMap()
     m2 = map_unittest_pb2.TestMap()
-    self.assertEqual(m1.map_int32_foreign_message,
-                     m1.map_int32_foreign_message)
+    self.assertEqual(m1.map_int32_foreign_message, m1.map_int32_foreign_message)
     m2.map_int32_foreign_message[123].c = 10
     m1.MergeFrom(m2)
     self.assertEqual(10, m2.map_int32_foreign_message[123].c)
@@ -2070,7 +2069,7 @@
 
   def testMergeFromBadType(self):
     msg = map_unittest_pb2.TestMap()
-    with self.assertRaisesRegexp(
+    with self.assertRaisesRegex(
         TypeError,
         r'Parameter to MergeFrom\(\) must be instance of same class: expected '
         r'.+TestMap got int\.'):
@@ -2078,7 +2077,7 @@
 
   def testCopyFromBadType(self):
     msg = map_unittest_pb2.TestMap()
-    with self.assertRaisesRegexp(
+    with self.assertRaisesRegex(
         TypeError,
         r'Parameter to [A-Za-z]*From\(\) must be instance of same class: '
         r'expected .+TestMap got int\.'):
@@ -2167,6 +2166,34 @@
       for key in int32_foreign_iter:
         pass
 
+  def testModifyMapEntryWhileIterating(self):
+    msg = map_unittest_pb2.TestMap()
+
+    msg.map_string_string['abc'] = '123'
+    msg.map_string_string['def'] = '456'
+    msg.map_string_string['ghi'] = '789'
+
+    msg.map_int32_foreign_message[5].c = 5
+    msg.map_int32_foreign_message[6].c = 6
+    msg.map_int32_foreign_message[7].c = 7
+
+    string_string_keys = list(msg.map_string_string.keys())
+    int32_foreign_keys = list(msg.map_int32_foreign_message.keys())
+
+    keys = []
+    for key in msg.map_string_string:
+      keys.append(key)
+      msg.map_string_string[key] = '000'
+    self.assertEqual(keys, string_string_keys)
+    self.assertEqual(keys, list(msg.map_string_string.keys()))
+
+    keys = []
+    for key in msg.map_int32_foreign_message:
+      keys.append(key)
+      msg.map_int32_foreign_message[key].c = 0
+    self.assertEqual(keys, int32_foreign_keys)
+    self.assertEqual(keys, list(msg.map_int32_foreign_message.keys()))
+
   def testSubmessageMap(self):
     msg = map_unittest_pb2.TestMap()
 
@@ -2278,7 +2305,7 @@
     msg1 = map_unittest_pb2.TestMap()
     msg1.map_string_foreign_message['test'].c = 42
     msg2 = map_unittest_pb2.TestMap(
-      map_string_foreign_message=msg1.map_string_foreign_message)
+        map_string_foreign_message=msg1.map_string_foreign_message)
     self.assertEqual(42, msg2.map_string_foreign_message['test'].c)
 
   def testMapFieldRaisesCorrectError(self):
@@ -2413,24 +2440,21 @@
     msg2.MergeFromString(serialized)
     self.assertEqual(msg2.optional_string, u'😍')
 
-    msg = unittest_proto3_arena_pb2.TestAllTypes(
-        optional_string=u'\ud001')
+    msg = unittest_proto3_arena_pb2.TestAllTypes(optional_string=u'\ud001')
     self.assertEqual(msg.optional_string, u'\ud001')
 
   def testSurrogatesInPython3(self):
     # Surrogates are rejected at setters in Python3.
     with self.assertRaises(ValueError):
-      unittest_proto3_arena_pb2.TestAllTypes(
-          optional_string=u'\ud801\udc01')
+      unittest_proto3_arena_pb2.TestAllTypes(optional_string=u'\ud801\udc01')
     with self.assertRaises(ValueError):
-      unittest_proto3_arena_pb2.TestAllTypes(
-          optional_string=b'\xed\xa0\x81')
+      unittest_proto3_arena_pb2.TestAllTypes(optional_string=b'\xed\xa0\x81')
     with self.assertRaises(ValueError):
-      unittest_proto3_arena_pb2.TestAllTypes(
-          optional_string=u'\ud801')
+      unittest_proto3_arena_pb2.TestAllTypes(optional_string=u'\ud801')
     with self.assertRaises(ValueError):
-      unittest_proto3_arena_pb2.TestAllTypes(
-          optional_string=u'\ud801\ud801')
+      unittest_proto3_arena_pb2.TestAllTypes(optional_string=u'\ud801\ud801')
+
+
 
 
 @testing_refleaks.TestCase
@@ -2441,8 +2465,9 @@
     tp_name = str(type(msg)).split("'")[1]
     valid_names = ('Repeated%sContainer' % base_name,
                    'Repeated%sFieldContainer' % base_name)
-    self.assertTrue(any(tp_name.endswith(v) for v in valid_names),
-                    '%r does end with any of %r' % (tp_name, valid_names))
+    self.assertTrue(
+        any(tp_name.endswith(v) for v in valid_names),
+        '%r does end with any of %r' % (tp_name, valid_names))
 
     parts = tp_name.split('.')
     class_name = parts[-1]
@@ -2455,6 +2480,7 @@
     self.assertImportFromName(pb.repeated_int32, 'Scalar')
     self.assertImportFromName(pb.repeated_nested_message, 'Composite')
 
+
 @testing_refleaks.TestCase
 class PackedFieldTest(unittest.TestCase):
 
@@ -2574,5 +2600,6 @@
     q.ParseFromString(self.p_serialized)
     self.assertEqual(self.p.field.payload, q.field.payload)
 
+
 if __name__ == '__main__':
   unittest.main()
diff --git a/python/google/protobuf/internal/proto_builder_test.py b/python/google/protobuf/internal/proto_builder_test.py
index 8e2afaf..48077b0 100644
--- a/python/google/protobuf/internal/proto_builder_test.py
+++ b/python/google/protobuf/internal/proto_builder_test.py
@@ -31,10 +31,7 @@
 """Tests for google.protobuf.proto_builder."""
 
 import collections
-try:
-  import unittest2 as unittest
-except ImportError:
-  import unittest
+import unittest
 
 from google.protobuf import descriptor_pb2  # pylint: disable=g-import-not-at-top
 from google.protobuf import descriptor
diff --git a/python/google/protobuf/internal/reflection_test.py b/python/google/protobuf/internal/reflection_test.py
index 9343389..66dd0c7 100644
--- a/python/google/protobuf/internal/reflection_test.py
+++ b/python/google/protobuf/internal/reflection_test.py
@@ -37,6 +37,7 @@
 import gc
 import operator
 import struct
+import sys
 import warnings
 import unittest
 
@@ -376,7 +377,8 @@
     self.assertRaises(TypeError, setattr, proto, 'optional_float', 'foo')
     self.assertRaises(TypeError, setattr, proto, 'optional_double', 'foo')
     # TODO(jieluo): Fix type checking difference for python and c extension
-    if api_implementation.Type() == 'python':
+    if (api_implementation.Type() == 'python' or
+        (sys.version_info.major, sys.version_info.minor) >= (3, 10)):
       self.assertRaises(TypeError, setattr, proto, 'optional_bool', 1.1)
     else:
       proto.optional_bool = 1.1
@@ -427,7 +429,7 @@
       pb.optional_uint64 = '2'
 
     # The exact error should propagate with a poorly written custom integer.
-    with self.assertRaisesRegexp(RuntimeError, 'my_error'):
+    with self.assertRaisesRegex(RuntimeError, 'my_error'):
       pb.optional_uint64 = test_util.NonStandardInteger(5, 'my_error')
 
   def assetIntegerBoundsChecking(self, integer_fn, message_module):
diff --git a/python/google/protobuf/internal/well_known_types_test.py b/python/google/protobuf/internal/well_known_types_test.py
index 37aec7e..3912901 100644
--- a/python/google/protobuf/internal/well_known_types_test.py
+++ b/python/google/protobuf/internal/well_known_types_test.py
@@ -36,8 +36,6 @@
 import datetime
 import unittest
 
-import dateutil.tz
-
 from google.protobuf import any_pb2
 from google.protobuf import duration_pb2
 from google.protobuf import field_mask_pb2
@@ -50,9 +48,17 @@
 from google.protobuf.internal import well_known_types
 from google.protobuf import descriptor
 from google.protobuf import text_format
-from google3.pyglib import datelib
 from google.protobuf.internal import _parameterized
 
+try:
+  # New module in Python 3.9:
+  import zoneinfo  # pylint:disable=g-import-not-at-top
+  _TZ_JAPAN = zoneinfo.ZoneInfo('Japan')
+  _TZ_PACIFIC = zoneinfo.ZoneInfo('US/Pacific')
+except ImportError:
+  _TZ_JAPAN = datetime.timezone(datetime.timedelta(hours=9), 'Japan')
+  _TZ_PACIFIC = datetime.timezone(datetime.timedelta(hours=-8), 'US/Pacific')
+
 
 class TimeUtilTestBase(_parameterized.TestCase):
 
@@ -270,12 +276,12 @@
 
   # Two hours after the Unix Epoch, around the world.
   @_parameterized.named_parameters(
-      ('London', [1970, 1, 1, 2], dateutil.tz.UTC),
-      ('Tokyo', [1970, 1, 1, 11], dateutil.tz.gettz('Japan')),
-      ('LA', [1969, 12, 31, 18], dateutil.tz.gettz('US/Pacific')),
+      ('London', [1970, 1, 1, 2], datetime.timezone.utc),
+      ('Tokyo', [1970, 1, 1, 11], _TZ_JAPAN),
+      ('LA', [1969, 12, 31, 18], _TZ_PACIFIC),
   )
   def testTimezoneAwareDatetimeConversion(self, date_parts, tzinfo):
-    original_datetime = datelib.CreateDatetime(*date_parts, tzinfo=tzinfo)
+    original_datetime = datetime.datetime(*date_parts, tzinfo=tzinfo)  # pylint:disable=g-tzinfo-datetime
 
     message = timestamp_pb2.Timestamp()
     message.FromDatetime(original_datetime)
@@ -296,7 +302,7 @@
     aware_datetime = message.ToDatetime(tzinfo=tzinfo)
     self.assertEqual(original_datetime, aware_datetime)
     self.assertEqual(
-        datelib.CreateDatetime(1970, 1, 1, 2, tzinfo=dateutil.tz.UTC),
+        datetime.datetime(1970, 1, 1, 2, tzinfo=datetime.timezone.utc),
         aware_datetime)
     self.assertEqual(tzinfo, aware_datetime.tzinfo)
 
@@ -324,85 +330,64 @@
 
   def testInvalidTimestamp(self):
     message = timestamp_pb2.Timestamp()
-    self.assertRaisesRegexp(
-        ValueError,
-        'Failed to parse timestamp: missing valid timezone offset.',
-        message.FromJsonString,
-        '')
-    self.assertRaisesRegexp(
-        ValueError,
-        'Failed to parse timestamp: invalid trailing data '
-        '1970-01-01T00:00:01Ztrail.',
-        message.FromJsonString,
+    self.assertRaisesRegex(
+        ValueError, 'Failed to parse timestamp: missing valid timezone offset.',
+        message.FromJsonString, '')
+    self.assertRaisesRegex(
+        ValueError, 'Failed to parse timestamp: invalid trailing data '
+        '1970-01-01T00:00:01Ztrail.', message.FromJsonString,
         '1970-01-01T00:00:01Ztrail')
-    self.assertRaisesRegexp(
-        ValueError,
-        'time data \'10000-01-01T00:00:00\' does not match'
-        ' format \'%Y-%m-%dT%H:%M:%S\'',
-        message.FromJsonString, '10000-01-01T00:00:00.00Z')
-    self.assertRaisesRegexp(
-        ValueError,
-        'nanos 0123456789012 more than 9 fractional digits.',
-        message.FromJsonString,
-        '1970-01-01T00:00:00.0123456789012Z')
-    self.assertRaisesRegexp(
+    self.assertRaisesRegex(
+        ValueError, 'time data \'10000-01-01T00:00:00\' does not match'
+        ' format \'%Y-%m-%dT%H:%M:%S\'', message.FromJsonString,
+        '10000-01-01T00:00:00.00Z')
+    self.assertRaisesRegex(
+        ValueError, 'nanos 0123456789012 more than 9 fractional digits.',
+        message.FromJsonString, '1970-01-01T00:00:00.0123456789012Z')
+    self.assertRaisesRegex(
         ValueError,
         (r'Invalid timezone offset value: \+08.'),
         message.FromJsonString,
-        '1972-01-01T01:00:00.01+08',)
-    self.assertRaisesRegexp(
-        ValueError,
-        'year (0 )?is out of range',
-        message.FromJsonString,
-        '0000-01-01T00:00:00Z')
+        '1972-01-01T01:00:00.01+08',
+    )
+    self.assertRaisesRegex(ValueError, 'year (0 )?is out of range',
+                           message.FromJsonString, '0000-01-01T00:00:00Z')
     message.seconds = 253402300800
-    self.assertRaisesRegexp(
-        OverflowError,
-        'date value out of range',
-        message.ToJsonString)
+    self.assertRaisesRegex(OverflowError, 'date value out of range',
+                           message.ToJsonString)
 
   def testInvalidDuration(self):
     message = duration_pb2.Duration()
-    self.assertRaisesRegexp(
-        ValueError,
-        'Duration must end with letter "s": 1.',
-        message.FromJsonString, '1')
-    self.assertRaisesRegexp(
-        ValueError,
-        'Couldn\'t parse duration: 1...2s.',
-        message.FromJsonString, '1...2s')
+    self.assertRaisesRegex(ValueError, 'Duration must end with letter "s": 1.',
+                           message.FromJsonString, '1')
+    self.assertRaisesRegex(ValueError, 'Couldn\'t parse duration: 1...2s.',
+                           message.FromJsonString, '1...2s')
     text = '-315576000001.000000000s'
-    self.assertRaisesRegexp(
+    self.assertRaisesRegex(
         ValueError,
         r'Duration is not valid\: Seconds -315576000001 must be in range'
-        r' \[-315576000000\, 315576000000\].',
-        message.FromJsonString, text)
+        r' \[-315576000000\, 315576000000\].', message.FromJsonString, text)
     text = '315576000001.000000000s'
-    self.assertRaisesRegexp(
+    self.assertRaisesRegex(
         ValueError,
         r'Duration is not valid\: Seconds 315576000001 must be in range'
-        r' \[-315576000000\, 315576000000\].',
-        message.FromJsonString, text)
+        r' \[-315576000000\, 315576000000\].', message.FromJsonString, text)
     message.seconds = -315576000001
     message.nanos = 0
-    self.assertRaisesRegexp(
+    self.assertRaisesRegex(
         ValueError,
         r'Duration is not valid\: Seconds -315576000001 must be in range'
-        r' \[-315576000000\, 315576000000\].',
-        message.ToJsonString)
+        r' \[-315576000000\, 315576000000\].', message.ToJsonString)
     message.seconds = 0
     message.nanos = 999999999 + 1
-    self.assertRaisesRegexp(
-        ValueError,
-        r'Duration is not valid\: Nanos 1000000000 must be in range'
-        r' \[-999999999\, 999999999\].',
-        message.ToJsonString)
+    self.assertRaisesRegex(
+        ValueError, r'Duration is not valid\: Nanos 1000000000 must be in range'
+        r' \[-999999999\, 999999999\].', message.ToJsonString)
     message.seconds = -1
     message.nanos = 1
-    self.assertRaisesRegexp(
-        ValueError,
-        r'Duration is not valid\: Sign mismatch.',
-        message.ToJsonString)
+    self.assertRaisesRegex(ValueError,
+                           r'Duration is not valid\: Sign mismatch.',
+                           message.ToJsonString)
 
 
 class FieldMaskTest(unittest.TestCase):
@@ -724,34 +709,29 @@
                      well_known_types._SnakeCaseToCamelCase('foo3_bar'))
 
     # No uppercase letter is allowed.
-    self.assertRaisesRegexp(
+    self.assertRaisesRegex(
         ValueError,
         'Fail to print FieldMask to Json string: Path name Foo must '
         'not contain uppercase letters.',
-        well_known_types._SnakeCaseToCamelCase,
-        'Foo')
+        well_known_types._SnakeCaseToCamelCase, 'Foo')
     # Any character after a "_" must be a lowercase letter.
     #   1. "_" cannot be followed by another "_".
     #   2. "_" cannot be followed by a digit.
     #   3. "_" cannot appear as the last character.
-    self.assertRaisesRegexp(
+    self.assertRaisesRegex(
         ValueError,
         'Fail to print FieldMask to Json string: The character after a '
         '"_" must be a lowercase letter in path name foo__bar.',
-        well_known_types._SnakeCaseToCamelCase,
-        'foo__bar')
-    self.assertRaisesRegexp(
+        well_known_types._SnakeCaseToCamelCase, 'foo__bar')
+    self.assertRaisesRegex(
         ValueError,
         'Fail to print FieldMask to Json string: The character after a '
         '"_" must be a lowercase letter in path name foo_3bar.',
-        well_known_types._SnakeCaseToCamelCase,
-        'foo_3bar')
-    self.assertRaisesRegexp(
+        well_known_types._SnakeCaseToCamelCase, 'foo_3bar')
+    self.assertRaisesRegex(
         ValueError,
         'Fail to print FieldMask to Json string: Trailing "_" in path '
-        'name foo_bar_.',
-        well_known_types._SnakeCaseToCamelCase,
-        'foo_bar_')
+        'name foo_bar_.', well_known_types._SnakeCaseToCamelCase, 'foo_bar_')
 
   def testCamelCaseToSnakeCase(self):
     self.assertEqual('foo_bar',
@@ -760,11 +740,10 @@
                      well_known_types._CamelCaseToSnakeCase('FooBar'))
     self.assertEqual('foo3_bar',
                      well_known_types._CamelCaseToSnakeCase('foo3Bar'))
-    self.assertRaisesRegexp(
+    self.assertRaisesRegex(
         ValueError,
         'Fail to parse FieldMask: Path name foo_bar must not contain "_"s.',
-        well_known_types._CamelCaseToSnakeCase,
-        'foo_bar')
+        well_known_types._CamelCaseToSnakeCase, 'foo_bar')
 
 
 class StructTest(unittest.TestCase):
diff --git a/python/google/protobuf/message.py b/python/google/protobuf/message.py
index ee46d0e..76c6802 100644
--- a/python/google/protobuf/message.py
+++ b/python/google/protobuf/message.py
@@ -194,6 +194,9 @@
     """Parse serialized protocol buffer data into this message.
 
     Like :func:`MergeFromString()`, except we clear the object first.
+
+    Raises:
+      message.DecodeError if the input cannot be parsed.
     """
     self.Clear()
     return self.MergeFromString(serialized)
diff --git a/python/google/protobuf/proto_api.h b/python/google/protobuf/proto_api.h
index 2e2156a..9969a91 100644
--- a/python/google/protobuf/proto_api.h
+++ b/python/google/protobuf/proto_api.h
@@ -45,6 +45,7 @@
 #ifndef GOOGLE_PROTOBUF_PYTHON_PROTO_API_H__
 #define GOOGLE_PROTOBUF_PYTHON_PROTO_API_H__
 
+#define PY_SSIZE_T_CLEAN
 #include <Python.h>
 
 #include <google/protobuf/descriptor_database.h>
diff --git a/python/google/protobuf/pyext/descriptor.cc b/python/google/protobuf/pyext/descriptor.cc
index cbf6887..1625312 100644
--- a/python/google/protobuf/pyext/descriptor.cc
+++ b/python/google/protobuf/pyext/descriptor.cc
@@ -32,6 +32,7 @@
 
 #include <google/protobuf/pyext/descriptor.h>
 
+#define PY_SSIZE_T_CLEAN
 #include <Python.h>
 #include <frameobject.h>
 
@@ -415,11 +416,15 @@
 
 PyTypeObject PyBaseDescriptor_Type = {
     PyVarObject_HEAD_INIT(&PyType_Type, 0) FULL_MODULE_NAME
-    ".DescriptorBase",                        // tp_name
-    sizeof(PyBaseDescriptor),                 // tp_basicsize
-    0,                                        // tp_itemsize
-    (destructor)Dealloc,                      // tp_dealloc
-    0,                                        // tp_print
+    ".DescriptorBase",         // tp_name
+    sizeof(PyBaseDescriptor),  // tp_basicsize
+    0,                         // tp_itemsize
+    (destructor)Dealloc,       // tp_dealloc
+#if PY_VERSION_HEX < 0x03080000
+    nullptr,  // tp_print
+#else
+    0,  // tp_vectorcall_offset
+#endif
     nullptr,                                  // tp_getattr
     nullptr,                                  // tp_setattr
     nullptr,                                  // tp_compare
@@ -685,11 +690,15 @@
 
 PyTypeObject PyMessageDescriptor_Type = {
     PyVarObject_HEAD_INIT(&PyType_Type, 0) FULL_MODULE_NAME
-    ".MessageDescriptor",                // tp_name
-    sizeof(PyBaseDescriptor),            // tp_basicsize
-    0,                                   // tp_itemsize
-    nullptr,                             // tp_dealloc
-    0,                                   // tp_print
+    ".MessageDescriptor",      // tp_name
+    sizeof(PyBaseDescriptor),  // tp_basicsize
+    0,                         // tp_itemsize
+    nullptr,                   // tp_dealloc
+#if PY_VERSION_HEX < 0x03080000
+    nullptr,  // tp_print
+#else
+    0,  // tp_vectorcall_offset
+#endif
     nullptr,                             // tp_getattr
     nullptr,                             // tp_setattr
     nullptr,                             // tp_compare
@@ -1011,11 +1020,15 @@
 
 PyTypeObject PyFieldDescriptor_Type = {
     PyVarObject_HEAD_INIT(&PyType_Type, 0) FULL_MODULE_NAME
-    ".FieldDescriptor",                  // tp_name
-    sizeof(PyBaseDescriptor),            // tp_basicsize
-    0,                                   // tp_itemsize
-    nullptr,                             // tp_dealloc
-    0,                                   // tp_print
+    ".FieldDescriptor",        // tp_name
+    sizeof(PyBaseDescriptor),  // tp_basicsize
+    0,                         // tp_itemsize
+    nullptr,                   // tp_dealloc
+#if PY_VERSION_HEX < 0x03080000
+    nullptr,  // tp_print
+#else
+    0,  // tp_vectorcall_offset
+#endif
     nullptr,                             // tp_getattr
     nullptr,                             // tp_setattr
     nullptr,                             // tp_compare
@@ -1167,11 +1180,15 @@
 
 PyTypeObject PyEnumDescriptor_Type = {
     PyVarObject_HEAD_INIT(&PyType_Type, 0) FULL_MODULE_NAME
-    ".EnumDescriptor",                   // tp_name
-    sizeof(PyBaseDescriptor),            // tp_basicsize
-    0,                                   // tp_itemsize
-    nullptr,                             // tp_dealloc
-    0,                                   // tp_print
+    ".EnumDescriptor",         // tp_name
+    sizeof(PyBaseDescriptor),  // tp_basicsize
+    0,                         // tp_itemsize
+    nullptr,                   // tp_dealloc
+#if PY_VERSION_HEX < 0x03080000
+    nullptr,  // tp_print
+#else
+    0,  // tp_vectorcall_offset
+#endif
     nullptr,                             // tp_getattr
     nullptr,                             // tp_setattr
     nullptr,                             // tp_compare
@@ -1288,11 +1305,15 @@
 
 PyTypeObject PyEnumValueDescriptor_Type = {
     PyVarObject_HEAD_INIT(&PyType_Type, 0) FULL_MODULE_NAME
-    ".EnumValueDescriptor",              // tp_name
-    sizeof(PyBaseDescriptor),            // tp_basicsize
-    0,                                   // tp_itemsize
-    nullptr,                             // tp_dealloc
-    0,                                   // tp_print
+    ".EnumValueDescriptor",    // tp_name
+    sizeof(PyBaseDescriptor),  // tp_basicsize
+    0,                         // tp_itemsize
+    nullptr,                   // tp_dealloc
+#if PY_VERSION_HEX < 0x03080000
+    nullptr,  // tp_print
+#else
+    0,  // tp_vectorcall_offset
+#endif
     nullptr,                             // tp_getattr
     nullptr,                             // tp_setattr
     nullptr,                             // tp_compare
@@ -1477,40 +1498,44 @@
     sizeof(PyFileDescriptor),              // tp_basicsize
     0,                                     // tp_itemsize
     (destructor)file_descriptor::Dealloc,  // tp_dealloc
-    0,                                     // tp_print
-    nullptr,                               // tp_getattr
-    nullptr,                               // tp_setattr
-    nullptr,                               // tp_compare
-    nullptr,                               // tp_repr
-    nullptr,                               // tp_as_number
-    nullptr,                               // tp_as_sequence
-    nullptr,                               // tp_as_mapping
-    nullptr,                               // tp_hash
-    nullptr,                               // tp_call
-    nullptr,                               // tp_str
-    nullptr,                               // tp_getattro
-    nullptr,                               // tp_setattro
-    nullptr,                               // tp_as_buffer
-    Py_TPFLAGS_DEFAULT,                    // tp_flags
-    "A File Descriptor",                   // tp_doc
-    nullptr,                               // tp_traverse
-    nullptr,                               // tp_clear
-    nullptr,                               // tp_richcompare
-    0,                                     // tp_weaklistoffset
-    nullptr,                               // tp_iter
-    nullptr,                               // tp_iternext
-    file_descriptor::Methods,              // tp_methods
-    nullptr,                               // tp_members
-    file_descriptor::Getters,              // tp_getset
-    &descriptor::PyBaseDescriptor_Type,    // tp_base
-    nullptr,                               // tp_dict
-    nullptr,                               // tp_descr_get
-    nullptr,                               // tp_descr_set
-    0,                                     // tp_dictoffset
-    nullptr,                               // tp_init
-    nullptr,                               // tp_alloc
-    nullptr,                               // tp_new
-    PyObject_GC_Del,                       // tp_free
+#if PY_VERSION_HEX < 0x03080000
+    nullptr,  // tp_print
+#else
+    0,  // tp_vectorcall_offset
+#endif
+    nullptr,                             // tp_getattr
+    nullptr,                             // tp_setattr
+    nullptr,                             // tp_compare
+    nullptr,                             // tp_repr
+    nullptr,                             // tp_as_number
+    nullptr,                             // tp_as_sequence
+    nullptr,                             // tp_as_mapping
+    nullptr,                             // tp_hash
+    nullptr,                             // tp_call
+    nullptr,                             // tp_str
+    nullptr,                             // tp_getattro
+    nullptr,                             // tp_setattro
+    nullptr,                             // tp_as_buffer
+    Py_TPFLAGS_DEFAULT,                  // tp_flags
+    "A File Descriptor",                 // tp_doc
+    nullptr,                             // tp_traverse
+    nullptr,                             // tp_clear
+    nullptr,                             // tp_richcompare
+    0,                                   // tp_weaklistoffset
+    nullptr,                             // tp_iter
+    nullptr,                             // tp_iternext
+    file_descriptor::Methods,            // tp_methods
+    nullptr,                             // tp_members
+    file_descriptor::Getters,            // tp_getset
+    &descriptor::PyBaseDescriptor_Type,  // tp_base
+    nullptr,                             // tp_dict
+    nullptr,                             // tp_descr_get
+    nullptr,                             // tp_descr_set
+    0,                                   // tp_dictoffset
+    nullptr,                             // tp_init
+    nullptr,                             // tp_alloc
+    nullptr,                             // tp_new
+    PyObject_GC_Del,                     // tp_free
 };
 
 PyObject* PyFileDescriptor_FromDescriptor(
@@ -1634,11 +1659,15 @@
 
 PyTypeObject PyOneofDescriptor_Type = {
     PyVarObject_HEAD_INIT(&PyType_Type, 0) FULL_MODULE_NAME
-    ".OneofDescriptor",                  // tp_name
-    sizeof(PyBaseDescriptor),            // tp_basicsize
-    0,                                   // tp_itemsize
-    nullptr,                             // tp_dealloc
-    0,                                   // tp_print
+    ".OneofDescriptor",        // tp_name
+    sizeof(PyBaseDescriptor),  // tp_basicsize
+    0,                         // tp_itemsize
+    nullptr,                   // tp_dealloc
+#if PY_VERSION_HEX < 0x03080000
+    nullptr,  // tp_print
+#else
+    0,  // tp_vectorcall_offset
+#endif
     nullptr,                             // tp_getattr
     nullptr,                             // tp_setattr
     nullptr,                             // tp_compare
@@ -1752,11 +1781,15 @@
 
 PyTypeObject PyServiceDescriptor_Type = {
     PyVarObject_HEAD_INIT(&PyType_Type, 0) FULL_MODULE_NAME
-    ".ServiceDescriptor",                // tp_name
-    sizeof(PyBaseDescriptor),            // tp_basicsize
-    0,                                   // tp_itemsize
-    nullptr,                             // tp_dealloc
-    0,                                   // tp_print
+    ".ServiceDescriptor",      // tp_name
+    sizeof(PyBaseDescriptor),  // tp_basicsize
+    0,                         // tp_itemsize
+    nullptr,                   // tp_dealloc
+#if PY_VERSION_HEX < 0x03080000
+    nullptr,  // tp_print
+#else
+    0,  // tp_vectorcall_offset
+#endif
     nullptr,                             // tp_getattr
     nullptr,                             // tp_setattr
     nullptr,                             // tp_compare
@@ -1876,11 +1909,15 @@
 
 PyTypeObject PyMethodDescriptor_Type = {
     PyVarObject_HEAD_INIT(&PyType_Type, 0) FULL_MODULE_NAME
-    ".MethodDescriptor",                 // tp_name
-    sizeof(PyBaseDescriptor),            // tp_basicsize
-    0,                                   // tp_itemsize
-    nullptr,                             // tp_dealloc
-    0,                                   // tp_print
+    ".MethodDescriptor",       // tp_name
+    sizeof(PyBaseDescriptor),  // tp_basicsize
+    0,                         // tp_itemsize
+    nullptr,                   // tp_dealloc
+#if PY_VERSION_HEX < 0x03080000
+    nullptr,  // tp_print
+#else
+    0,  // tp_vectorcall_offset
+#endif
     nullptr,                             // tp_getattr
     nullptr,                             // tp_setattr
     nullptr,                             // tp_compare
diff --git a/python/google/protobuf/pyext/descriptor.h b/python/google/protobuf/pyext/descriptor.h
index b99209d..d97e2f8 100644
--- a/python/google/protobuf/pyext/descriptor.h
+++ b/python/google/protobuf/pyext/descriptor.h
@@ -33,6 +33,7 @@
 #ifndef GOOGLE_PROTOBUF_PYTHON_CPP_DESCRIPTOR_H__
 #define GOOGLE_PROTOBUF_PYTHON_CPP_DESCRIPTOR_H__
 
+#define PY_SSIZE_T_CLEAN
 #include <Python.h>
 
 #include <google/protobuf/descriptor.h>
diff --git a/python/google/protobuf/pyext/descriptor_containers.cc b/python/google/protobuf/pyext/descriptor_containers.cc
index adb1690..a87155b 100644
--- a/python/google/protobuf/pyext/descriptor_containers.cc
+++ b/python/google/protobuf/pyext/descriptor_containers.cc
@@ -49,6 +49,7 @@
 // because the Python API is based on C, and does not play well with C++
 // inheritance.
 
+#define PY_SSIZE_T_CLEAN
 #include <Python.h>
 
 #include <google/protobuf/descriptor.h>
@@ -549,12 +550,16 @@
     sizeof(PyContainer),                                         // tp_basicsize
     0,                                                           // tp_itemsize
     nullptr,                                                     // tp_dealloc
-    0,                                                           // tp_pkrint
-    nullptr,                                                     // tp_getattr
-    nullptr,                                                     // tp_setattr
-    nullptr,                                                     // tp_compare
-    (reprfunc)ContainerRepr,                                     // tp_repr
-    nullptr,                                                     // tp_as_number
+#if PY_VERSION_HEX < 0x03080000
+    nullptr,  // tp_print
+#else
+    0,  // tp_vectorcall_offset
+#endif
+    nullptr,                   // tp_getattr
+    nullptr,                   // tp_setattr
+    nullptr,                   // tp_compare
+    (reprfunc)ContainerRepr,   // tp_repr
+    nullptr,                   // tp_as_number
     &MappingSequenceMethods,   // tp_as_sequence
     &MappingMappingMethods,    // tp_as_mapping
     nullptr,                   // tp_hash
@@ -729,10 +734,14 @@
 
 PyTypeObject DescriptorSequence_Type = {
     PyVarObject_HEAD_INIT(&PyType_Type, 0) "DescriptorSequence",  // tp_name
-    sizeof(PyContainer),       // tp_basicsize
-    0,                         // tp_itemsize
-    nullptr,                   // tp_dealloc
-    0,                         // tp_print
+    sizeof(PyContainer),  // tp_basicsize
+    0,                    // tp_itemsize
+    nullptr,              // tp_dealloc
+#if PY_VERSION_HEX < 0x03080000
+    nullptr,  // tp_print
+#else
+    0,  // tp_vectorcall_offset
+#endif
     nullptr,                   // tp_getattr
     nullptr,                   // tp_setattr
     nullptr,                   // tp_compare
@@ -874,25 +883,29 @@
     sizeof(PyContainerIterator),                             // tp_basicsize
     0,                                                       // tp_itemsize
     (destructor)Iterator_Dealloc,                            // tp_dealloc
-    0,                                                       // tp_print
-    nullptr,                                                 // tp_getattr
-    nullptr,                                                 // tp_setattr
-    nullptr,                                                 // tp_compare
-    nullptr,                                                 // tp_repr
-    nullptr,                                                 // tp_as_number
-    nullptr,                                                 // tp_as_sequence
-    nullptr,                                                 // tp_as_mapping
-    nullptr,                                                 // tp_hash
-    nullptr,                                                 // tp_call
-    nullptr,                                                 // tp_str
-    nullptr,                                                 // tp_getattro
-    nullptr,                                                 // tp_setattro
-    nullptr,                                                 // tp_as_buffer
-    Py_TPFLAGS_DEFAULT,                                      // tp_flags
-    nullptr,                                                 // tp_doc
-    nullptr,                                                 // tp_traverse
-    nullptr,                                                 // tp_clear
-    nullptr,                                                 // tp_richcompare
+#if PY_VERSION_HEX < 0x03080000
+    nullptr,  // tp_print
+#else
+    0,  // tp_vectorcall_offset
+#endif
+    nullptr,                      // tp_getattr
+    nullptr,                      // tp_setattr
+    nullptr,                      // tp_compare
+    nullptr,                      // tp_repr
+    nullptr,                      // tp_as_number
+    nullptr,                      // tp_as_sequence
+    nullptr,                      // tp_as_mapping
+    nullptr,                      // tp_hash
+    nullptr,                      // tp_call
+    nullptr,                      // tp_str
+    nullptr,                      // tp_getattro
+    nullptr,                      // tp_setattro
+    nullptr,                      // tp_as_buffer
+    Py_TPFLAGS_DEFAULT,           // tp_flags
+    nullptr,                      // tp_doc
+    nullptr,                      // tp_traverse
+    nullptr,                      // tp_clear
+    nullptr,                      // tp_richcompare
     0,                            // tp_weaklistoffset
     PyObject_SelfIter,            // tp_iter
     (iternextfunc)Iterator_Next,  // tp_iternext
diff --git a/python/google/protobuf/pyext/descriptor_containers.h b/python/google/protobuf/pyext/descriptor_containers.h
index 4e05c58..cf2cf4a 100644
--- a/python/google/protobuf/pyext/descriptor_containers.h
+++ b/python/google/protobuf/pyext/descriptor_containers.h
@@ -34,6 +34,7 @@
 // Mappings and Sequences of descriptors.
 // They implement containers like fields_by_name, EnumDescriptor.values...
 // See descriptor_containers.cc for more description.
+#define PY_SSIZE_T_CLEAN
 #include <Python.h>
 
 namespace google {
diff --git a/python/google/protobuf/pyext/descriptor_database.h b/python/google/protobuf/pyext/descriptor_database.h
index d1bd7a3..5621a22 100644
--- a/python/google/protobuf/pyext/descriptor_database.h
+++ b/python/google/protobuf/pyext/descriptor_database.h
@@ -31,6 +31,7 @@
 #ifndef GOOGLE_PROTOBUF_PYTHON_CPP_DESCRIPTOR_DATABASE_H__
 #define GOOGLE_PROTOBUF_PYTHON_CPP_DESCRIPTOR_DATABASE_H__
 
+#define PY_SSIZE_T_CLEAN
 #include <Python.h>
 
 #include <google/protobuf/descriptor_database.h>
diff --git a/python/google/protobuf/pyext/descriptor_pool.cc b/python/google/protobuf/pyext/descriptor_pool.cc
index 5b0ceeb..2f39281 100644
--- a/python/google/protobuf/pyext/descriptor_pool.cc
+++ b/python/google/protobuf/pyext/descriptor_pool.cc
@@ -32,6 +32,7 @@
 
 #include <unordered_map>
 
+#define PY_SSIZE_T_CLEAN
 #include <Python.h>
 
 #include <google/protobuf/descriptor.pb.h>
@@ -689,11 +690,15 @@
 
 PyTypeObject PyDescriptorPool_Type = {
     PyVarObject_HEAD_INIT(&PyType_Type, 0) FULL_MODULE_NAME
-    ".DescriptorPool",                        // tp_name
-    sizeof(PyDescriptorPool),                 // tp_basicsize
-    0,                                        // tp_itemsize
-    cdescriptor_pool::Dealloc,                // tp_dealloc
-    0,                                        // tp_print
+    ".DescriptorPool",          // tp_name
+    sizeof(PyDescriptorPool),   // tp_basicsize
+    0,                          // tp_itemsize
+    cdescriptor_pool::Dealloc,  // tp_dealloc
+#if PY_VERSION_HEX < 0x03080000
+    nullptr,  // tp_print
+#else
+    0,  // tp_vectorcall_offset
+#endif
     nullptr,                                  // tp_getattr
     nullptr,                                  // tp_setattr
     nullptr,                                  // tp_compare
diff --git a/python/google/protobuf/pyext/descriptor_pool.h b/python/google/protobuf/pyext/descriptor_pool.h
index 48658d3..5d3c3a9 100644
--- a/python/google/protobuf/pyext/descriptor_pool.h
+++ b/python/google/protobuf/pyext/descriptor_pool.h
@@ -31,6 +31,7 @@
 #ifndef GOOGLE_PROTOBUF_PYTHON_CPP_DESCRIPTOR_POOL_H__
 #define GOOGLE_PROTOBUF_PYTHON_CPP_DESCRIPTOR_POOL_H__
 
+#define PY_SSIZE_T_CLEAN
 #include <Python.h>
 
 #include <unordered_map>
diff --git a/python/google/protobuf/pyext/extension_dict.cc b/python/google/protobuf/pyext/extension_dict.cc
index 760fc91..692029f 100644
--- a/python/google/protobuf/pyext/extension_dict.cc
+++ b/python/google/protobuf/pyext/extension_dict.cc
@@ -367,12 +367,16 @@
 }  // namespace extension_dict
 
 PyTypeObject ExtensionDict_Type = {
-    PyVarObject_HEAD_INIT(&PyType_Type, 0)     //
-    FULL_MODULE_NAME ".ExtensionDict",         // tp_name
-    sizeof(ExtensionDict),                     // tp_basicsize
-    0,                                         //  tp_itemsize
-    (destructor)extension_dict::dealloc,       //  tp_dealloc
-    0,                                         //  tp_print
+    PyVarObject_HEAD_INIT(&PyType_Type, 0)  //
+    FULL_MODULE_NAME ".ExtensionDict",      // tp_name
+    sizeof(ExtensionDict),                  // tp_basicsize
+    0,                                      //  tp_itemsize
+    (destructor)extension_dict::dealloc,    //  tp_dealloc
+#if PY_VERSION_HEX < 0x03080000
+    nullptr,  // tp_print
+#else
+    0,  // tp_vectorcall_offset
+#endif
     nullptr,                                   //  tp_getattr
     nullptr,                                   //  tp_setattr
     nullptr,                                   //  tp_compare
@@ -439,37 +443,41 @@
     sizeof(extension_dict::ExtensionIterator),  //  tp_basicsize
     0,                                          //  tp_itemsize
     extension_dict::DeallocExtensionIterator,   //  tp_dealloc
-    0,                                          //  tp_print
-    nullptr,                                    //  tp_getattr
-    nullptr,                                    //  tp_setattr
-    nullptr,                                    //  tp_compare
-    nullptr,                                    //  tp_repr
-    nullptr,                                    //  tp_as_number
-    nullptr,                                    //  tp_as_sequence
-    nullptr,                                    //  tp_as_mapping
-    nullptr,                                    //  tp_hash
-    nullptr,                                    //  tp_call
-    nullptr,                                    //  tp_str
-    nullptr,                                    //  tp_getattro
-    nullptr,                                    //  tp_setattro
-    nullptr,                                    //  tp_as_buffer
-    Py_TPFLAGS_DEFAULT,                         //  tp_flags
-    "A scalar map iterator",                    //  tp_doc
-    nullptr,                                    //  tp_traverse
-    nullptr,                                    //  tp_clear
-    nullptr,                                    //  tp_richcompare
-    0,                                          //  tp_weaklistoffset
-    PyObject_SelfIter,                          //  tp_iter
-    IterNext,                                   //  tp_iternext
-    nullptr,                                    //  tp_methods
-    nullptr,                                    //  tp_members
-    nullptr,                                    //  tp_getset
-    nullptr,                                    //  tp_base
-    nullptr,                                    //  tp_dict
-    nullptr,                                    //  tp_descr_get
-    nullptr,                                    //  tp_descr_set
-    0,                                          //  tp_dictoffset
-    nullptr,                                    //  tp_init
+#if PY_VERSION_HEX < 0x03080000
+    nullptr,  // tp_print
+#else
+    0,  // tp_vectorcall_offset
+#endif
+    nullptr,                  //  tp_getattr
+    nullptr,                  //  tp_setattr
+    nullptr,                  //  tp_compare
+    nullptr,                  //  tp_repr
+    nullptr,                  //  tp_as_number
+    nullptr,                  //  tp_as_sequence
+    nullptr,                  //  tp_as_mapping
+    nullptr,                  //  tp_hash
+    nullptr,                  //  tp_call
+    nullptr,                  //  tp_str
+    nullptr,                  //  tp_getattro
+    nullptr,                  //  tp_setattro
+    nullptr,                  //  tp_as_buffer
+    Py_TPFLAGS_DEFAULT,       //  tp_flags
+    "A scalar map iterator",  //  tp_doc
+    nullptr,                  //  tp_traverse
+    nullptr,                  //  tp_clear
+    nullptr,                  //  tp_richcompare
+    0,                        //  tp_weaklistoffset
+    PyObject_SelfIter,        //  tp_iter
+    IterNext,                 //  tp_iternext
+    nullptr,                  //  tp_methods
+    nullptr,                  //  tp_members
+    nullptr,                  //  tp_getset
+    nullptr,                  //  tp_base
+    nullptr,                  //  tp_dict
+    nullptr,                  //  tp_descr_get
+    nullptr,                  //  tp_descr_set
+    0,                        //  tp_dictoffset
+    nullptr,                  //  tp_init
 };
 }  // namespace python
 }  // namespace protobuf
diff --git a/python/google/protobuf/pyext/extension_dict.h b/python/google/protobuf/pyext/extension_dict.h
index c9da443..a058194 100644
--- a/python/google/protobuf/pyext/extension_dict.h
+++ b/python/google/protobuf/pyext/extension_dict.h
@@ -34,6 +34,7 @@
 #ifndef GOOGLE_PROTOBUF_PYTHON_CPP_EXTENSION_DICT_H__
 #define GOOGLE_PROTOBUF_PYTHON_CPP_EXTENSION_DICT_H__
 
+#define PY_SSIZE_T_CLEAN
 #include <Python.h>
 
 #include <memory>
diff --git a/python/google/protobuf/pyext/field.cc b/python/google/protobuf/pyext/field.cc
index 611ac8a..0d3b0b9 100644
--- a/python/google/protobuf/pyext/field.cc
+++ b/python/google/protobuf/pyext/field.cc
@@ -87,39 +87,43 @@
     sizeof(PyMessageFieldProperty),         // tp_basicsize
     0,                                      // tp_itemsize
     nullptr,                                // tp_dealloc
-    0,                                      // tp_print
-    nullptr,                                // tp_getattr
-    nullptr,                                // tp_setattr
-    nullptr,                                // tp_compare
-    (reprfunc)field::Repr,                  // tp_repr
-    nullptr,                                // tp_as_number
-    nullptr,                                // tp_as_sequence
-    nullptr,                                // tp_as_mapping
-    nullptr,                                // tp_hash
-    nullptr,                                // tp_call
-    nullptr,                                // tp_str
-    nullptr,                                // tp_getattro
-    nullptr,                                // tp_setattro
-    nullptr,                                // tp_as_buffer
-    Py_TPFLAGS_DEFAULT,                     // tp_flags
-    "Field property of a Message",          // tp_doc
-    nullptr,                                // tp_traverse
-    nullptr,                                // tp_clear
-    nullptr,                                // tp_richcompare
-    0,                                      // tp_weaklistoffset
-    nullptr,                                // tp_iter
-    nullptr,                                // tp_iternext
-    nullptr,                                // tp_methods
-    nullptr,                                // tp_members
-    field::Getters,                         // tp_getset
-    nullptr,                                // tp_base
-    nullptr,                                // tp_dict
-    (descrgetfunc)field::DescrGet,          // tp_descr_get
-    (descrsetfunc)field::DescrSet,          // tp_descr_set
-    0,                                      // tp_dictoffset
-    nullptr,                                // tp_init
-    nullptr,                                // tp_alloc
-    nullptr,                                // tp_new
+#if PY_VERSION_HEX < 0x03080000
+    nullptr,  // tp_print
+#else
+    0,  // tp_vectorcall_offset
+#endif
+    nullptr,                        // tp_getattr
+    nullptr,                        // tp_setattr
+    nullptr,                        // tp_compare
+    (reprfunc)field::Repr,          // tp_repr
+    nullptr,                        // tp_as_number
+    nullptr,                        // tp_as_sequence
+    nullptr,                        // tp_as_mapping
+    nullptr,                        // tp_hash
+    nullptr,                        // tp_call
+    nullptr,                        // tp_str
+    nullptr,                        // tp_getattro
+    nullptr,                        // tp_setattro
+    nullptr,                        // tp_as_buffer
+    Py_TPFLAGS_DEFAULT,             // tp_flags
+    "Field property of a Message",  // tp_doc
+    nullptr,                        // tp_traverse
+    nullptr,                        // tp_clear
+    nullptr,                        // tp_richcompare
+    0,                              // tp_weaklistoffset
+    nullptr,                        // tp_iter
+    nullptr,                        // tp_iternext
+    nullptr,                        // tp_methods
+    nullptr,                        // tp_members
+    field::Getters,                 // tp_getset
+    nullptr,                        // tp_base
+    nullptr,                        // tp_dict
+    (descrgetfunc)field::DescrGet,  // tp_descr_get
+    (descrsetfunc)field::DescrSet,  // tp_descr_set
+    0,                              // tp_dictoffset
+    nullptr,                        // tp_init
+    nullptr,                        // tp_alloc
+    nullptr,                        // tp_new
 };
 PyTypeObject* CFieldProperty_Type = &_CFieldProperty_Type;
 
diff --git a/python/google/protobuf/pyext/field.h b/python/google/protobuf/pyext/field.h
index 7b4660c..f9f94c4 100644
--- a/python/google/protobuf/pyext/field.h
+++ b/python/google/protobuf/pyext/field.h
@@ -31,6 +31,7 @@
 #ifndef GOOGLE_PROTOBUF_PYTHON_CPP_FIELD_H__
 #define GOOGLE_PROTOBUF_PYTHON_CPP_FIELD_H__
 
+#define PY_SSIZE_T_CLEAN
 #include <Python.h>
 
 namespace google {
diff --git a/python/google/protobuf/pyext/map_container.cc b/python/google/protobuf/pyext/map_container.cc
index c953c65..4d516d2 100644
--- a/python/google/protobuf/pyext/map_container.cc
+++ b/python/google/protobuf/pyext/map_container.cc
@@ -432,12 +432,12 @@
     return -1;
   }
 
-  self->version++;
-
   if (v) {
     // Set item to v.
-    reflection->InsertOrLookupMapValue(message, self->parent_field_descriptor,
-                                       map_key, &value);
+    if (reflection->InsertOrLookupMapValue(
+            message, self->parent_field_descriptor, map_key, &value)) {
+      self->version++;
+    }
 
     if (!PythonToMapValueRef(self, v, reflection->SupportsUnknownEnumValues(),
                              &value)) {
@@ -448,6 +448,7 @@
     // Delete key from map.
     if (reflection->DeleteMapValue(message, self->parent_field_descriptor,
                                    map_key)) {
+      self->version++;
       return 0;
     } else {
       PyErr_Format(PyExc_KeyError, "Key not present in map");
@@ -853,11 +854,15 @@
 
 PyTypeObject MapIterator_Type = {
     PyVarObject_HEAD_INIT(&PyType_Type, 0) FULL_MODULE_NAME
-    ".MapIterator",                 //  tp_name
-    sizeof(MapIterator),            //  tp_basicsize
-    0,                              //  tp_itemsize
-    DeallocMapIterator,             //  tp_dealloc
-    0,                              //  tp_print
+    ".MapIterator",       //  tp_name
+    sizeof(MapIterator),  //  tp_basicsize
+    0,                    //  tp_itemsize
+    DeallocMapIterator,   //  tp_dealloc
+#if PY_VERSION_HEX < 0x03080000
+    nullptr,  // tp_print
+#else
+    0,  // tp_vectorcall_offset
+#endif
     nullptr,                        //  tp_getattr
     nullptr,                        //  tp_setattr
     nullptr,                        //  tp_compare
diff --git a/python/google/protobuf/pyext/map_container.h b/python/google/protobuf/pyext/map_container.h
index 842602e..e14136e 100644
--- a/python/google/protobuf/pyext/map_container.h
+++ b/python/google/protobuf/pyext/map_container.h
@@ -31,6 +31,7 @@
 #ifndef GOOGLE_PROTOBUF_PYTHON_CPP_MAP_CONTAINER_H__
 #define GOOGLE_PROTOBUF_PYTHON_CPP_MAP_CONTAINER_H__
 
+#define PY_SSIZE_T_CLEAN
 #include <Python.h>
 
 #include <cstdint>
diff --git a/python/google/protobuf/pyext/message.cc b/python/google/protobuf/pyext/message.cc
index 3a33824..35594ec 100644
--- a/python/google/protobuf/pyext/message.cc
+++ b/python/google/protobuf/pyext/message.cc
@@ -454,11 +454,15 @@
 
 static PyTypeObject _CMessageClass_Type = {
     PyVarObject_HEAD_INIT(&PyType_Type, 0) FULL_MODULE_NAME
-    ".MessageMeta",                       // tp_name
-    sizeof(CMessageClass),                // tp_basicsize
-    0,                                    // tp_itemsize
-    message_meta::Dealloc,                // tp_dealloc
-    0,                                    // tp_print
+    ".MessageMeta",         // tp_name
+    sizeof(CMessageClass),  // tp_basicsize
+    0,                      // tp_itemsize
+    message_meta::Dealloc,  // tp_dealloc
+#if PY_VERSION_HEX < 0x03080000
+    nullptr, /* tp_print */
+#else
+    0, /* tp_vectorcall_offset */
+#endif
     nullptr,                              // tp_getattr
     nullptr,                              // tp_setattr
     nullptr,                              // tp_compare
@@ -1930,7 +1934,7 @@
         "Error parsing message as the message exceeded the protobuf limit "
         "with type '%s'",
         self->GetMessageClass()->message_descriptor->full_name().c_str());
-    return NULL;
+    return nullptr;
   }
 
   // ctx has an explicit limit set (length of string_view), so we have to
@@ -2735,20 +2739,24 @@
     sizeof(CMessage),               // tp_basicsize
     0,                              //  tp_itemsize
     (destructor)cmessage::Dealloc,  //  tp_dealloc
-    0,                              //  tp_print
-    nullptr,                        //  tp_getattr
-    nullptr,                        //  tp_setattr
-    nullptr,                        //  tp_compare
-    (reprfunc)cmessage::ToStr,      //  tp_repr
-    nullptr,                        //  tp_as_number
-    nullptr,                        //  tp_as_sequence
-    nullptr,                        //  tp_as_mapping
-    PyObject_HashNotImplemented,    //  tp_hash
-    nullptr,                        //  tp_call
-    (reprfunc)cmessage::ToStr,      //  tp_str
-    cmessage::GetAttr,              //  tp_getattro
-    nullptr,                        //  tp_setattro
-    nullptr,                        //  tp_as_buffer
+#if PY_VERSION_HEX < 0x03080000
+    nullptr,  // tp_print
+#else
+    0,  // tp_vectorcall_offset
+#endif
+    nullptr,                      //  tp_getattr
+    nullptr,                      //  tp_setattr
+    nullptr,                      //  tp_compare
+    (reprfunc)cmessage::ToStr,    //  tp_repr
+    nullptr,                      //  tp_as_number
+    nullptr,                      //  tp_as_sequence
+    nullptr,                      //  tp_as_mapping
+    PyObject_HashNotImplemented,  //  tp_hash
+    nullptr,                      //  tp_call
+    (reprfunc)cmessage::ToStr,    //  tp_str
+    cmessage::GetAttr,            //  tp_getattro
+    nullptr,                      //  tp_setattro
+    nullptr,                      //  tp_as_buffer
     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE |
         Py_TPFLAGS_HAVE_VERSION_TAG,     //  tp_flags
     "A ProtocolMessage",                 //  tp_doc
diff --git a/python/google/protobuf/pyext/message.h b/python/google/protobuf/pyext/message.h
index 29af255..b17daa5 100644
--- a/python/google/protobuf/pyext/message.h
+++ b/python/google/protobuf/pyext/message.h
@@ -34,6 +34,7 @@
 #ifndef GOOGLE_PROTOBUF_PYTHON_CPP_MESSAGE_H__
 #define GOOGLE_PROTOBUF_PYTHON_CPP_MESSAGE_H__
 
+#define PY_SSIZE_T_CLEAN
 #include <Python.h>
 
 #include <cstdint>
diff --git a/python/google/protobuf/pyext/message_factory.cc b/python/google/protobuf/pyext/message_factory.cc
index 64f9fed..bc44dd4 100644
--- a/python/google/protobuf/pyext/message_factory.cc
+++ b/python/google/protobuf/pyext/message_factory.cc
@@ -30,6 +30,7 @@
 
 #include <unordered_map>
 
+#define PY_SSIZE_T_CLEAN
 #include <Python.h>
 
 #include <google/protobuf/dynamic_message.h>
@@ -252,20 +253,24 @@
     sizeof(PyMessageFactory),  // tp_basicsize
     0,                         // tp_itemsize
     message_factory::Dealloc,  // tp_dealloc
-    0,                         // tp_print
-    nullptr,                   // tp_getattr
-    nullptr,                   // tp_setattr
-    nullptr,                   // tp_compare
-    nullptr,                   // tp_repr
-    nullptr,                   // tp_as_number
-    nullptr,                   // tp_as_sequence
-    nullptr,                   // tp_as_mapping
-    nullptr,                   // tp_hash
-    nullptr,                   // tp_call
-    nullptr,                   // tp_str
-    nullptr,                   // tp_getattro
-    nullptr,                   // tp_setattro
-    nullptr,                   // tp_as_buffer
+#if PY_VERSION_HEX < 0x03080000
+    nullptr,  // tp_print
+#else
+    0,  // tp_vectorcall_offset
+#endif
+    nullptr,  // tp_getattr
+    nullptr,  // tp_setattr
+    nullptr,  // tp_compare
+    nullptr,  // tp_repr
+    nullptr,  // tp_as_number
+    nullptr,  // tp_as_sequence
+    nullptr,  // tp_as_mapping
+    nullptr,  // tp_hash
+    nullptr,  // tp_call
+    nullptr,  // tp_str
+    nullptr,  // tp_getattro
+    nullptr,  // tp_setattro
+    nullptr,  // tp_as_buffer
     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,  // tp_flags
     "A static Message Factory",                                     // tp_doc
     message_factory::GcTraverse,  // tp_traverse
diff --git a/python/google/protobuf/pyext/message_factory.h b/python/google/protobuf/pyext/message_factory.h
index 515c29c..7dfe425 100644
--- a/python/google/protobuf/pyext/message_factory.h
+++ b/python/google/protobuf/pyext/message_factory.h
@@ -31,6 +31,7 @@
 #ifndef GOOGLE_PROTOBUF_PYTHON_CPP_MESSAGE_FACTORY_H__
 #define GOOGLE_PROTOBUF_PYTHON_CPP_MESSAGE_FACTORY_H__
 
+#define PY_SSIZE_T_CLEAN
 #include <Python.h>
 
 #include <unordered_map>
diff --git a/python/google/protobuf/pyext/message_module.cc b/python/google/protobuf/pyext/message_module.cc
index 1f953fc..2d3c1d2 100644
--- a/python/google/protobuf/pyext/message_module.cc
+++ b/python/google/protobuf/pyext/message_module.cc
@@ -28,6 +28,7 @@
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+#define PY_SSIZE_T_CLEAN
 #include <Python.h>
 
 #include <google/protobuf/message_lite.h>
diff --git a/python/google/protobuf/pyext/repeated_composite_container.h b/python/google/protobuf/pyext/repeated_composite_container.h
index e241827..30536ea 100644
--- a/python/google/protobuf/pyext/repeated_composite_container.h
+++ b/python/google/protobuf/pyext/repeated_composite_container.h
@@ -34,6 +34,7 @@
 #ifndef GOOGLE_PROTOBUF_PYTHON_CPP_REPEATED_COMPOSITE_CONTAINER_H__
 #define GOOGLE_PROTOBUF_PYTHON_CPP_REPEATED_COMPOSITE_CONTAINER_H__
 
+#define PY_SSIZE_T_CLEAN
 #include <Python.h>
 
 #include <memory>
diff --git a/python/google/protobuf/pyext/repeated_scalar_container.h b/python/google/protobuf/pyext/repeated_scalar_container.h
index f9f0ea8..372b6f0 100644
--- a/python/google/protobuf/pyext/repeated_scalar_container.h
+++ b/python/google/protobuf/pyext/repeated_scalar_container.h
@@ -34,6 +34,7 @@
 #ifndef GOOGLE_PROTOBUF_PYTHON_CPP_REPEATED_SCALAR_CONTAINER_H__
 #define GOOGLE_PROTOBUF_PYTHON_CPP_REPEATED_SCALAR_CONTAINER_H__
 
+#define PY_SSIZE_T_CLEAN
 #include <Python.h>
 
 #include <memory>
diff --git a/python/google/protobuf/pyext/scoped_pyobject_ptr.h b/python/google/protobuf/pyext/scoped_pyobject_ptr.h
index e6ea7e3..f684152 100644
--- a/python/google/protobuf/pyext/scoped_pyobject_ptr.h
+++ b/python/google/protobuf/pyext/scoped_pyobject_ptr.h
@@ -35,6 +35,7 @@
 
 #include <google/protobuf/stubs/common.h>
 
+#define PY_SSIZE_T_CLEAN
 #include <Python.h>
 namespace google {
 namespace protobuf {
diff --git a/python/google/protobuf/pyext/unknown_fields.cc b/python/google/protobuf/pyext/unknown_fields.cc
index fbfee3d..dcd63b2 100644
--- a/python/google/protobuf/pyext/unknown_fields.cc
+++ b/python/google/protobuf/pyext/unknown_fields.cc
@@ -30,6 +30,7 @@
 
 #include <google/protobuf/pyext/unknown_fields.h>
 
+#define PY_SSIZE_T_CLEAN
 #include <Python.h>
 #include <set>
 #include <memory>
@@ -155,11 +156,15 @@
 
 PyTypeObject PyUnknownFields_Type = {
     PyVarObject_HEAD_INIT(&PyType_Type, 0) FULL_MODULE_NAME
-    ".PyUnknownFields",           // tp_name
-    sizeof(PyUnknownFields),      // tp_basicsize
-    0,                            //  tp_itemsize
-    unknown_fields::Dealloc,      //  tp_dealloc
-    0,                            //  tp_print
+    ".PyUnknownFields",       // tp_name
+    sizeof(PyUnknownFields),  // tp_basicsize
+    0,                        //  tp_itemsize
+    unknown_fields::Dealloc,  //  tp_dealloc
+#if PY_VERSION_HEX < 0x03080000
+    nullptr,  // tp_print
+#else
+    0,  // tp_vectorcall_offset
+#endif
     nullptr,                      //  tp_getattr
     nullptr,                      //  tp_setattr
     nullptr,                      //  tp_compare
@@ -219,7 +224,7 @@
                  "The parent message might be cleared.");
     return nullptr;
   }
-  ssize_t total_size = fields->field_count();
+  Py_ssize_t total_size = fields->field_count();
   if (self->index >= total_size) {
     PyErr_Format(PyExc_ValueError,
                  "UnknownField does not exist. "
@@ -312,11 +317,15 @@
 
 PyTypeObject PyUnknownFieldRef_Type = {
     PyVarObject_HEAD_INIT(&PyType_Type, 0) FULL_MODULE_NAME
-    ".PyUnknownFieldRef",         // tp_name
-    sizeof(PyUnknownFieldRef),    //  tp_basicsize
-    0,                            //  tp_itemsize
-    unknown_field::Dealloc,       //  tp_dealloc
-    0,                            //  tp_print
+    ".PyUnknownFieldRef",       // tp_name
+    sizeof(PyUnknownFieldRef),  //  tp_basicsize
+    0,                          //  tp_itemsize
+    unknown_field::Dealloc,     //  tp_dealloc
+#if PY_VERSION_HEX < 0x03080000
+    nullptr,  // tp_print
+#else
+    0,  // tp_vectorcall_offset
+#endif
     nullptr,                      //  tp_getattr
     nullptr,                      //  tp_setattr
     nullptr,                      //  tp_compare
diff --git a/python/google/protobuf/pyext/unknown_fields.h b/python/google/protobuf/pyext/unknown_fields.h
index 94d55e1..e7b0b35 100644
--- a/python/google/protobuf/pyext/unknown_fields.h
+++ b/python/google/protobuf/pyext/unknown_fields.h
@@ -31,6 +31,7 @@
 #ifndef GOOGLE_PROTOBUF_PYTHON_CPP_UNKNOWN_FIELDS_H__
 #define GOOGLE_PROTOBUF_PYTHON_CPP_UNKNOWN_FIELDS_H__
 
+#define PY_SSIZE_T_CLEAN
 #include <Python.h>
 
 #include <memory>
diff --git a/python/google/protobuf/python_protobuf.h b/python/google/protobuf/python_protobuf.h
index 8db1ffb..4fcf065 100644
--- a/python/google/protobuf/python_protobuf.h
+++ b/python/google/protobuf/python_protobuf.h
@@ -36,6 +36,7 @@
 #ifndef GOOGLE_PROTOBUF_PYTHON_PYTHON_PROTOBUF_H__
 #define GOOGLE_PROTOBUF_PYTHON_PYTHON_PROTOBUF_H__
 
+#define PY_SSIZE_T_CLEAN
 #include <Python.h>
 
 namespace google {
diff --git a/python/google/protobuf/service_reflection.py b/python/google/protobuf/service_reflection.py
index 75c51ff..f82ab71 100644
--- a/python/google/protobuf/service_reflection.py
+++ b/python/google/protobuf/service_reflection.py
@@ -133,7 +133,7 @@
     """
     self.descriptor = service_descriptor
 
-  def BuildService(self, cls):
+  def BuildService(builder, cls):
     """Constructs the service class.
 
     Args:
@@ -143,18 +143,26 @@
     # CallMethod needs to operate with an instance of the Service class. This
     # internal wrapper function exists only to be able to pass the service
     # instance to the method that does the real CallMethod work.
-    def _WrapCallMethod(srvc, method_descriptor,
-                        rpc_controller, request, callback):
-      return self._CallMethod(srvc, method_descriptor,
-                       rpc_controller, request, callback)
-    self.cls = cls
+    # Making sure to use exact argument names from the abstract interface in
+    # service.py to match the type signature
+    def _WrapCallMethod(self, method_descriptor, rpc_controller, request, done):
+      return builder._CallMethod(self, method_descriptor, rpc_controller,
+                                 request, done)
+
+    def _WrapGetRequestClass(self, method_descriptor):
+      return builder._GetRequestClass(method_descriptor)
+
+    def _WrapGetResponseClass(self, method_descriptor):
+      return builder._GetResponseClass(method_descriptor)
+
+    builder.cls = cls
     cls.CallMethod = _WrapCallMethod
-    cls.GetDescriptor = staticmethod(lambda: self.descriptor)
-    cls.GetDescriptor.__doc__ = "Returns the service descriptor."
-    cls.GetRequestClass = self._GetRequestClass
-    cls.GetResponseClass = self._GetResponseClass
-    for method in self.descriptor.methods:
-      setattr(cls, method.name, self._GenerateNonImplementedMethod(method))
+    cls.GetDescriptor = staticmethod(lambda: builder.descriptor)
+    cls.GetDescriptor.__doc__ = 'Returns the service descriptor.'
+    cls.GetRequestClass = _WrapGetRequestClass
+    cls.GetResponseClass = _WrapGetResponseClass
+    for method in builder.descriptor.methods:
+      setattr(cls, method.name, builder._GenerateNonImplementedMethod(method))
 
   def _CallMethod(self, srvc, method_descriptor,
                   rpc_controller, request, callback):
diff --git a/python/setup.py b/python/setup.py
index ab42b6f..fb35cf5 100755
--- a/python/setup.py
+++ b/python/setup.py
@@ -1,6 +1,9 @@
 #! /usr/bin/env python
 #
 # See README for usage instructions.
+
+# pylint:disable=missing-module-docstring
+# pylint:disable=g-bad-import-order
 from distutils import util
 import fnmatch
 import glob
@@ -10,7 +13,9 @@
 import subprocess
 import sys
 import sysconfig
-import platform
+
+# pylint:disable=g-importing-member
+# pylint:disable=g-multiple-import
 
 # We must use setuptools, not distutils, because we need to use the
 # namespace_packages option for the "google" package.
@@ -24,44 +29,52 @@
 # Find the Protocol Compiler.
 if 'PROTOC' in os.environ and os.path.exists(os.environ['PROTOC']):
   protoc = os.environ['PROTOC']
-elif os.path.exists("../src/protoc"):
-  protoc = "../src/protoc"
-elif os.path.exists("../src/protoc.exe"):
-  protoc = "../src/protoc.exe"
-elif os.path.exists("../vsprojects/Debug/protoc.exe"):
-  protoc = "../vsprojects/Debug/protoc.exe"
-elif os.path.exists("../vsprojects/Release/protoc.exe"):
-  protoc = "../vsprojects/Release/protoc.exe"
+elif os.path.exists('../src/protoc'):
+  protoc = '../src/protoc'
+elif os.path.exists('../src/protoc.exe'):
+  protoc = '../src/protoc.exe'
+elif os.path.exists('../vsprojects/Debug/protoc.exe'):
+  protoc = '../vsprojects/Debug/protoc.exe'
+elif os.path.exists('../vsprojects/Release/protoc.exe'):
+  protoc = '../vsprojects/Release/protoc.exe'
 else:
-  protoc = find_executable("protoc")
+  protoc = find_executable('protoc')
 
 
 def GetVersion():
-  """Gets the version from google/protobuf/__init__.py
+  """Reads and returns the version from google/protobuf/__init__.py.
 
   Do not import google.protobuf.__init__ directly, because an installed
-  protobuf library may be loaded instead."""
+  protobuf library may be loaded instead.
+
+  Returns:
+      The version.
+  """
 
   with open(os.path.join('google', 'protobuf', '__init__.py')) as version_file:
-    exec(version_file.read(), globals())
-    global __version__
-    return __version__
+    exec(version_file.read(), globals())  # pylint:disable=exec-used
+    return __version__  # pylint:disable=undefined-variable
 
 
-def generate_proto(source, require = True):
-  """Invokes the Protocol Compiler to generate a _pb2.py from the given
-  .proto file.  Does nothing if the output already exists and is newer than
-  the input."""
+def GenProto(source, require=True):
+  """Generates a _pb2.py from the given .proto file.
+
+  Does nothing if the output already exists and is newer than the input.
+
+  Args:
+      source: the .proto file path.
+      require: if True, exit immediately when a path is not found.
+  """
 
   if not require and not os.path.exists(source):
     return
 
-  output = source.replace(".proto", "_pb2.py").replace("../src/", "")
+  output = source.replace('.proto', '_pb2.py').replace('../src/', '')
 
   if (not os.path.exists(output) or
       (os.path.exists(source) and
        os.path.getmtime(source) > os.path.getmtime(output))):
-    print("Generating %s..." % output)
+    print('Generating %s...' % output)
 
     if not os.path.exists(source):
       sys.stderr.write("Can't find required file: %s\n" % source)
@@ -69,78 +82,85 @@
 
     if protoc is None:
       sys.stderr.write(
-          "protoc is not installed nor found in ../src.  Please compile it "
-          "or install the binary package.\n")
+          'protoc is not installed nor found in ../src.  Please compile it '
+          'or install the binary package.\n')
       sys.exit(-1)
 
-    protoc_command = [ protoc, "-I../src", "-I.", "--python_out=.", source ]
+    protoc_command = [protoc, '-I../src', '-I.', '--python_out=.', source]
     if subprocess.call(protoc_command) != 0:
       sys.exit(-1)
 
+
 def GenerateUnittestProtos():
-  generate_proto("../src/google/protobuf/any_test.proto", False)
-  generate_proto("../src/google/protobuf/map_proto2_unittest.proto", False)
-  generate_proto("../src/google/protobuf/map_unittest.proto", False)
-  generate_proto("../src/google/protobuf/test_messages_proto3.proto", False)
-  generate_proto("../src/google/protobuf/test_messages_proto2.proto", False)
-  generate_proto("../src/google/protobuf/unittest_arena.proto", False)
-  generate_proto("../src/google/protobuf/unittest.proto", False)
-  generate_proto("../src/google/protobuf/unittest_custom_options.proto", False)
-  generate_proto("../src/google/protobuf/unittest_import.proto", False)
-  generate_proto("../src/google/protobuf/unittest_import_public.proto", False)
-  generate_proto("../src/google/protobuf/unittest_mset.proto", False)
-  generate_proto("../src/google/protobuf/unittest_mset_wire_format.proto", False)
-  generate_proto("../src/google/protobuf/unittest_no_generic_services.proto", False)
-  generate_proto("../src/google/protobuf/unittest_proto3_arena.proto", False)
-  generate_proto("../src/google/protobuf/util/json_format.proto", False)
-  generate_proto("../src/google/protobuf/util/json_format_proto3.proto", False)
-  generate_proto("google/protobuf/internal/any_test.proto", False)
-  generate_proto("google/protobuf/internal/descriptor_pool_test1.proto", False)
-  generate_proto("google/protobuf/internal/descriptor_pool_test2.proto", False)
-  generate_proto("google/protobuf/internal/factory_test1.proto", False)
-  generate_proto("google/protobuf/internal/factory_test2.proto", False)
-  generate_proto("google/protobuf/internal/file_options_test.proto", False)
-  generate_proto("google/protobuf/internal/import_test_package/inner.proto", False)
-  generate_proto("google/protobuf/internal/import_test_package/outer.proto", False)
-  generate_proto("google/protobuf/internal/missing_enum_values.proto", False)
-  generate_proto("google/protobuf/internal/message_set_extensions.proto", False)
-  generate_proto("google/protobuf/internal/more_extensions.proto", False)
-  generate_proto("google/protobuf/internal/more_extensions_dynamic.proto", False)
-  generate_proto("google/protobuf/internal/more_messages.proto", False)
-  generate_proto("google/protobuf/internal/no_package.proto", False)
-  generate_proto("google/protobuf/internal/packed_field_test.proto", False)
-  generate_proto("google/protobuf/internal/test_bad_identifiers.proto", False)
-  generate_proto("google/protobuf/internal/test_proto3_optional.proto", False)
-  generate_proto("google/protobuf/pyext/python.proto", False)
+  """Generates protobuf code for unittests."""
+  GenProto('../src/google/protobuf/any_test.proto', False)
+  GenProto('../src/google/protobuf/map_proto2_unittest.proto', False)
+  GenProto('../src/google/protobuf/map_unittest.proto', False)
+  GenProto('../src/google/protobuf/test_messages_proto3.proto', False)
+  GenProto('../src/google/protobuf/test_messages_proto2.proto', False)
+  GenProto('../src/google/protobuf/unittest_arena.proto', False)
+  GenProto('../src/google/protobuf/unittest.proto', False)
+  GenProto('../src/google/protobuf/unittest_custom_options.proto', False)
+  GenProto('../src/google/protobuf/unittest_import.proto', False)
+  GenProto('../src/google/protobuf/unittest_import_public.proto', False)
+  GenProto('../src/google/protobuf/unittest_mset.proto', False)
+  GenProto('../src/google/protobuf/unittest_mset_wire_format.proto', False)
+  GenProto('../src/google/protobuf/unittest_no_generic_services.proto', False)
+  GenProto('../src/google/protobuf/unittest_proto3_arena.proto', False)
+  GenProto('../src/google/protobuf/util/json_format.proto', False)
+  GenProto('../src/google/protobuf/util/json_format_proto3.proto', False)
+  GenProto('google/protobuf/internal/any_test.proto', False)
+  GenProto('google/protobuf/internal/descriptor_pool_test1.proto', False)
+  GenProto('google/protobuf/internal/descriptor_pool_test2.proto', False)
+  GenProto('google/protobuf/internal/factory_test1.proto', False)
+  GenProto('google/protobuf/internal/factory_test2.proto', False)
+  GenProto('google/protobuf/internal/file_options_test.proto', False)
+  GenProto('google/protobuf/internal/import_test_package/inner.proto', False)
+  GenProto('google/protobuf/internal/import_test_package/outer.proto', False)
+  GenProto('google/protobuf/internal/missing_enum_values.proto', False)
+  GenProto('google/protobuf/internal/message_set_extensions.proto', False)
+  GenProto('google/protobuf/internal/more_extensions.proto', False)
+  GenProto('google/protobuf/internal/more_extensions_dynamic.proto', False)
+  GenProto('google/protobuf/internal/more_messages.proto', False)
+  GenProto('google/protobuf/internal/no_package.proto', False)
+  GenProto('google/protobuf/internal/packed_field_test.proto', False)
+  GenProto('google/protobuf/internal/test_bad_identifiers.proto', False)
+  GenProto('google/protobuf/internal/test_proto3_optional.proto', False)
+  GenProto('google/protobuf/pyext/python.proto', False)
 
 
-class clean(_clean):
+class CleanCmd(_clean):
+  """Custom clean command for building the protobuf extension."""
+
   def run(self):
     # Delete generated files in the code tree.
-    for (dirpath, dirnames, filenames) in os.walk("."):
+    for (dirpath, unused_dirnames, filenames) in os.walk('.'):
       for filename in filenames:
         filepath = os.path.join(dirpath, filename)
-        if filepath.endswith("_pb2.py") or filepath.endswith(".pyc") or \
-          filepath.endswith(".so") or filepath.endswith(".o"):
+        if (filepath.endswith('_pb2.py') or filepath.endswith('.pyc') or
+            filepath.endswith('.so') or filepath.endswith('.o')):
           os.remove(filepath)
     # _clean is an old-style class, so super() doesn't work.
     _clean.run(self)
 
-class build_py(_build_py):
+
+class BuildPyCmd(_build_py):
+  """Custom build_py command for building the protobuf runtime."""
+
   def run(self):
     # Generate necessary .proto file if it doesn't exist.
-    generate_proto("../src/google/protobuf/descriptor.proto")
-    generate_proto("../src/google/protobuf/compiler/plugin.proto")
-    generate_proto("../src/google/protobuf/any.proto")
-    generate_proto("../src/google/protobuf/api.proto")
-    generate_proto("../src/google/protobuf/duration.proto")
-    generate_proto("../src/google/protobuf/empty.proto")
-    generate_proto("../src/google/protobuf/field_mask.proto")
-    generate_proto("../src/google/protobuf/source_context.proto")
-    generate_proto("../src/google/protobuf/struct.proto")
-    generate_proto("../src/google/protobuf/timestamp.proto")
-    generate_proto("../src/google/protobuf/type.proto")
-    generate_proto("../src/google/protobuf/wrappers.proto")
+    GenProto('../src/google/protobuf/descriptor.proto')
+    GenProto('../src/google/protobuf/compiler/plugin.proto')
+    GenProto('../src/google/protobuf/any.proto')
+    GenProto('../src/google/protobuf/api.proto')
+    GenProto('../src/google/protobuf/duration.proto')
+    GenProto('../src/google/protobuf/empty.proto')
+    GenProto('../src/google/protobuf/field_mask.proto')
+    GenProto('../src/google/protobuf/source_context.proto')
+    GenProto('../src/google/protobuf/struct.proto')
+    GenProto('../src/google/protobuf/timestamp.proto')
+    GenProto('../src/google/protobuf/type.proto')
+    GenProto('../src/google/protobuf/wrappers.proto')
     GenerateUnittestProtos()
 
     # _build_py is an old-style class, so super() doesn't work.
@@ -148,17 +168,18 @@
 
   def find_package_modules(self, package, package_dir):
     exclude = (
-        "*test*",
-        "google/protobuf/internal/*_pb2.py",
-        "google/protobuf/internal/_parameterized.py",
-        "google/protobuf/pyext/python_pb2.py",
+        '*test*',
+        'google/protobuf/internal/*_pb2.py',
+        'google/protobuf/internal/_parameterized.py',
+        'google/protobuf/pyext/python_pb2.py',
     )
     modules = _build_py.find_package_modules(self, package, package_dir)
     return [(pkg, mod, fil) for (pkg, mod, fil) in modules
             if not any(fnmatch.fnmatchcase(fil, pat=pat) for pat in exclude)]
 
 
-class build_ext(_build_ext):
+class BuildExtCmd(_build_ext):
+  """Command class for building the protobuf Python extension."""
 
   def get_ext_filename(self, ext_name):
     # since python3.5, python extensions' shared libraries use a suffix that
@@ -169,23 +190,25 @@
     # suffix so that the resulting file name matches the target architecture
     # and we end up with a well-formed wheel.
     filename = _build_ext.get_ext_filename(self, ext_name)
-    orig_ext_suffix = sysconfig.get_config_var("EXT_SUFFIX")
-    new_ext_suffix = os.getenv("PROTOCOL_BUFFERS_OVERRIDE_EXT_SUFFIX")
+    orig_ext_suffix = sysconfig.get_config_var('EXT_SUFFIX')
+    new_ext_suffix = os.getenv('PROTOCOL_BUFFERS_OVERRIDE_EXT_SUFFIX')
     if new_ext_suffix and filename.endswith(orig_ext_suffix):
       filename = filename[:-len(orig_ext_suffix)] + new_ext_suffix
     return filename
 
-class test_conformance(_build_py):
+
+class TestConformanceCmd(_build_py):
   target = 'test_python'
+
   def run(self):
     # Python 2.6 dodges these extra failures.
-    os.environ["CONFORMANCE_PYTHON_EXTRA_FAILURES"] = (
-        "--failure_list failure_list_python-post26.txt")
-    cmd = 'cd ../conformance && make %s' % (test_conformance.target)
-    status = subprocess.check_call(cmd, shell=True)
+    os.environ['CONFORMANCE_PYTHON_EXTRA_FAILURES'] = (
+        '--failure_list failure_list_python-post26.txt')
+    cmd = 'cd ../conformance && make %s' % (TestConformanceCmd.target)
+    subprocess.check_call(cmd, shell=True)
 
 
-def get_option_from_sys_argv(option_str):
+def GetOptionFromArgv(option_str):
   if option_str in sys.argv:
     sys.argv.remove(option_str)
     return True
@@ -195,21 +218,37 @@
 if __name__ == '__main__':
   ext_module_list = []
   warnings_as_errors = '--warnings_as_errors'
-  if get_option_from_sys_argv('--cpp_implementation'):
+  if GetOptionFromArgv('--cpp_implementation'):
     # Link libprotobuf.a and libprotobuf-lite.a statically with the
     # extension. Note that those libraries have to be compiled with
     # -fPIC for this to work.
-    compile_static_ext = get_option_from_sys_argv('--compile_static_extension')
+    compile_static_ext = GetOptionFromArgv('--compile_static_extension')
     libraries = ['protobuf']
     extra_objects = None
     if compile_static_ext:
       libraries = None
       extra_objects = ['../src/.libs/libprotobuf.a',
                        '../src/.libs/libprotobuf-lite.a']
-    test_conformance.target = 'test_python_cpp'
+    TestConformanceCmd.target = 'test_python_cpp'
 
     extra_compile_args = []
 
+    message_extra_link_args = None
+    api_implementation_link_args = None
+    if 'darwin' in sys.platform:
+      if sys.version_info[0] == 2:
+        message_init_symbol = 'init_message'
+        api_implementation_init_symbol = 'init_api_implementation'
+      else:
+        message_init_symbol = 'PyInit__message'
+        api_implementation_init_symbol = 'PyInit__api_implementation'
+      message_extra_link_args = [
+          '-Wl,-exported_symbol,_%s' % message_init_symbol
+      ]
+      api_implementation_link_args = [
+          '-Wl,-exported_symbol,_%s' % api_implementation_init_symbol
+      ]
+
     if sys.platform != 'win32':
       extra_compile_args.append('-Wno-write-strings')
       extra_compile_args.append('-Wno-invalid-offsetof')
@@ -218,8 +257,8 @@
       extra_compile_args.append('-std=c++11')
 
     if sys.platform == 'darwin':
-      extra_compile_args.append("-Wno-shorten-64-to-32");
-      extra_compile_args.append("-Wno-deprecated-register");
+      extra_compile_args.append('-Wno-shorten-64-to-32')
+      extra_compile_args.append('-Wno-deprecated-register')
 
     # https://developer.apple.com/documentation/xcode_release_notes/xcode_10_release_notes
     # C++ projects must now migrate to libc++ and are recommended to set a
@@ -227,7 +266,7 @@
     if sys.platform == 'darwin':
       mac_target = str(sysconfig.get_config_var('MACOSX_DEPLOYMENT_TARGET'))
       if mac_target and (pkg_resources.parse_version(mac_target) <
-                       pkg_resources.parse_version('10.9.0')):
+                         pkg_resources.parse_version('10.9.0')):
         os.environ['MACOSX_DEPLOYMENT_TARGET'] = '10.9'
         os.environ['_PYTHON_HOST_PLATFORM'] = re.sub(
             r'macosx-[0-9]+\.[0-9]+-(.+)', r'macosx-10.9-\1',
@@ -242,10 +281,10 @@
       extra_compile_args.append('-DMS_WIN64')
 
     # MSVS default is dymanic
-    if (sys.platform == 'win32'):
+    if sys.platform == 'win32':
       extra_compile_args.append('/MT')
 
-    if "clang" in os.popen('$CC --version 2> /dev/null').read():
+    if 'clang' in os.popen('$CC --version 2> /dev/null').read():
       extra_compile_args.append('-Wno-shorten-64-to-32')
 
     if warnings_as_errors in sys.argv:
@@ -255,18 +294,21 @@
     # C++ implementation extension
     ext_module_list.extend([
         Extension(
-            "google.protobuf.pyext._message",
+            'google.protobuf.pyext._message',
             glob.glob('google/protobuf/pyext/*.cc'),
-            include_dirs=[".", "../src"],
+            include_dirs=['.', '../src'],
             libraries=libraries,
             extra_objects=extra_objects,
+            extra_link_args=message_extra_link_args,
             library_dirs=['../src/.libs'],
             extra_compile_args=extra_compile_args,
         ),
         Extension(
-            "google.protobuf.internal._api_implementation",
+            'google.protobuf.internal._api_implementation',
             glob.glob('google/protobuf/internal/api_implementation.cc'),
-            extra_compile_args=extra_compile_args + ['-DPYTHON_PROTO2_CPP_IMPL_V2'],
+            extra_compile_args=(extra_compile_args +
+                                ['-DPYTHON_PROTO2_CPP_IMPL_V2']),
+            extra_link_args=api_implementation_link_args,
         ),
     ])
     os.environ['PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION'] = 'cpp'
@@ -283,15 +325,14 @@
       url='https://developers.google.com/protocol-buffers/',
       maintainer='protobuf@googlegroups.com',
       maintainer_email='protobuf@googlegroups.com',
-      license='3-Clause BSD License',
+      license='BSD-3-Clause',
       classifiers=[
-          "Programming Language :: Python",
-          "Programming Language :: Python :: 3",
-          "Programming Language :: Python :: 3.3",
-          "Programming Language :: Python :: 3.4",
-          "Programming Language :: Python :: 3.5",
-          "Programming Language :: Python :: 3.6",
-          "Programming Language :: Python :: 3.7",
+          'Programming Language :: Python',
+          'Programming Language :: Python :: 3',
+          'Programming Language :: Python :: 3.7',
+          'Programming Language :: Python :: 3.8',
+          'Programming Language :: Python :: 3.9',
+          'Programming Language :: Python :: 3.10',
       ],
       namespace_packages=['google'],
       packages=find_packages(
@@ -301,12 +342,12 @@
           ],),
       test_suite='google.protobuf.internal',
       cmdclass={
-          'clean': clean,
-          'build_py': build_py,
-          'build_ext': build_ext,
-          'test_conformance': test_conformance,
+          'clean': CleanCmd,
+          'build_py': BuildPyCmd,
+          'build_ext': BuildExtCmd,
+          'test_conformance': TestConformanceCmd,
       },
       install_requires=install_requires,
       ext_modules=ext_module_list,
-      python_requires=">=3.5",
+      python_requires='>=3.7',
   )