diff options
Diffstat (limited to 'python/google/protobuf/internal')
-rwxr-xr-x | python/google/protobuf/internal/_parameterized.py | 50 | ||||
-rwxr-xr-x | python/google/protobuf/internal/api_implementation.py | 11 | ||||
-rwxr-xr-x | python/google/protobuf/internal/containers.py | 4 | ||||
-rw-r--r-- | python/google/protobuf/internal/descriptor_database_test.py | 21 | ||||
-rw-r--r-- | python/google/protobuf/internal/descriptor_pool_test.py | 56 | ||||
-rwxr-xr-x | python/google/protobuf/internal/descriptor_test.py | 13 | ||||
-rwxr-xr-x | python/google/protobuf/internal/encoder.py | 19 | ||||
-rw-r--r-- | python/google/protobuf/internal/json_format_test.py | 15 | ||||
-rwxr-xr-x | python/google/protobuf/internal/message_test.py | 45 | ||||
-rw-r--r-- | python/google/protobuf/internal/no_package.proto | 10 | ||||
-rwxr-xr-x | python/google/protobuf/internal/text_format_test.py | 100 | ||||
-rw-r--r-- | python/google/protobuf/internal/well_known_types.py | 10 | ||||
-rw-r--r-- | python/google/protobuf/internal/well_known_types_test.py | 18 |
13 files changed, 299 insertions, 73 deletions
diff --git a/python/google/protobuf/internal/_parameterized.py b/python/google/protobuf/internal/_parameterized.py index 23a78f03..f2c0b305 100755 --- a/python/google/protobuf/internal/_parameterized.py +++ b/python/google/protobuf/internal/_parameterized.py @@ -37,8 +37,8 @@ argument tuples. A simple example: - class AdditionExample(parameterized.ParameterizedTestCase): - @parameterized.Parameters( + class AdditionExample(parameterized.TestCase): + @parameterized.parameters( (1, 2, 3), (4, 5, 9), (1, 1, 3)) @@ -54,8 +54,8 @@ fail due to an assertion error (1 + 1 != 3). Parameters for invididual test cases can be tuples (with positional parameters) or dictionaries (with named parameters): - class AdditionExample(parameterized.ParameterizedTestCase): - @parameterized.Parameters( + class AdditionExample(parameterized.TestCase): + @parameterized.parameters( {'op1': 1, 'op2': 2, 'result': 3}, {'op1': 4, 'op2': 5, 'result': 9}, ) @@ -77,13 +77,13 @@ stay the same across several invocations, object representations like '<__main__.Foo object at 0x23d8610>' are turned into '<__main__.Foo>'. For even more descriptive names, -especially in test logs, you can use the NamedParameters decorator. In +especially in test logs, you can use the named_parameters decorator. In this case, only tuples are supported, and the first parameters has to be a string (or an object that returns an apt name when converted via str()): - class NamedExample(parameterized.ParameterizedTestCase): - @parameterized.NamedParameters( + class NamedExample(parameterized.TestCase): + @parameterized.named_parameters( ('Normal', 'aa', 'aaa', True), ('EmptyPrefix', '', 'abc', True), ('BothEmpty', '', '', True)) @@ -103,13 +103,13 @@ from the command line: Parameterized Classes ===================== If invocation arguments are shared across test methods in a single -ParameterizedTestCase class, instead of decorating all test methods +TestCase class, instead of decorating all test methods individually, the class itself can be decorated: - @parameterized.Parameters( + @parameterized.parameters( (1, 2, 3) (4, 5, 9)) - class ArithmeticTest(parameterized.ParameterizedTestCase): + class ArithmeticTest(parameterized.TestCase): def testAdd(self, arg1, arg2, result): self.assertEqual(arg1 + arg2, result) @@ -122,8 +122,8 @@ If parameters should be shared across several test cases, or are dynamically created from other sources, a single non-tuple iterable can be passed into the decorator. This iterable will be used to obtain the test cases: - class AdditionExample(parameterized.ParameterizedTestCase): - @parameterized.Parameters( + class AdditionExample(parameterized.TestCase): + @parameterized.parameters( c.op1, c.op2, c.result for c in testcases ) def testAddition(self, op1, op2, result): @@ -135,8 +135,8 @@ Single-Argument Test Methods If a test method takes only one argument, the single argument does not need to be wrapped into a tuple: - class NegativeNumberExample(parameterized.ParameterizedTestCase): - @parameterized.Parameters( + class NegativeNumberExample(parameterized.TestCase): + @parameterized.parameters( -1, -3, -4, -5 ) def testIsNegative(self, arg): @@ -212,7 +212,7 @@ class _ParameterizedTestIter(object): def __call__(self, *args, **kwargs): raise RuntimeError('You appear to be running a parameterized test case ' 'without having inherited from parameterized.' - 'ParameterizedTestCase. This is bad because none of ' + 'TestCase. This is bad because none of ' 'your test cases are actually being run.') def __iter__(self): @@ -306,7 +306,7 @@ def _ParameterDecorator(naming_type, testcases): return _Apply -def Parameters(*testcases): +def parameters(*testcases): # pylint: disable=invalid-name """A decorator for creating parameterized tests. See the module docstring for a usage example. @@ -321,7 +321,7 @@ def Parameters(*testcases): return _ParameterDecorator(_ARGUMENT_REPR, testcases) -def NamedParameters(*testcases): +def named_parameters(*testcases): # pylint: disable=invalid-name """A decorator for creating parameterized tests. See the module docstring for a usage example. The first element of @@ -348,7 +348,7 @@ class TestGeneratorMetaclass(type): up as tests by the unittest framework. In general, it is supposed to be used in conjunction with the - Parameters decorator. + parameters decorator. """ def __new__(mcs, class_name, bases, dct): @@ -385,8 +385,8 @@ def _UpdateClassDictForParamTestCase(dct, id_suffix, name, iterator): id_suffix[new_name] = getattr(func, '__x_extra_id__', '') -class ParameterizedTestCase(unittest.TestCase): - """Base class for test cases using the Parameters decorator.""" +class TestCase(unittest.TestCase): + """Base class for test cases using the parameters decorator.""" __metaclass__ = TestGeneratorMetaclass def _OriginalName(self): @@ -409,10 +409,10 @@ class ParameterizedTestCase(unittest.TestCase): self._id_suffix.get(self._testMethodName, '')) -def CoopParameterizedTestCase(other_base_class): +def CoopTestCase(other_base_class): """Returns a new base class with a cooperative metaclass base. - This enables the ParameterizedTestCase to be used in combination + This enables the TestCase to be used in combination with other base classes that have custom metaclasses, such as mox.MoxTestBase. @@ -425,7 +425,7 @@ def CoopParameterizedTestCase(other_base_class): from google3.testing.pybase import parameterized - class ExampleTest(parameterized.CoopParameterizedTestCase(mox.MoxTestBase)): + class ExampleTest(parameterized.CoopTestCase(mox.MoxTestBase)): ... Args: @@ -439,5 +439,5 @@ def CoopParameterizedTestCase(other_base_class): (other_base_class.__metaclass__, TestGeneratorMetaclass), {}) return metaclass( - 'CoopParameterizedTestCase', - (other_base_class, ParameterizedTestCase), {}) + 'CoopTestCase', + (other_base_class, TestCase), {}) diff --git a/python/google/protobuf/internal/api_implementation.py b/python/google/protobuf/internal/api_implementation.py index 553fcdb6..ab9e7812 100755 --- a/python/google/protobuf/internal/api_implementation.py +++ b/python/google/protobuf/internal/api_implementation.py @@ -66,10 +66,13 @@ if _api_version < 0: # Still unspecified? from google.protobuf.internal import use_pure_python del use_pure_python # Avoids a pylint error and namespace pollution. except ImportError: - if _proto_extension_modules_exist_in_build: - if sys.version_info[0] >= 3: # Python 3 defaults to C++ impl v2. - _api_version = 2 - # TODO(b/17427486): Make Python 2 default to C++ impl v2. + # TODO(b/74017912): It's unsafe to enable :use_fast_cpp_protos by default; + # it can cause data loss if you have any Python-only extensions to any + # message passed back and forth with C++ code. + # + # TODO(b/17427486): Once that bug is fixed, we want to make both Python 2 + # and Python 3 default to `_api_version = 2` (C++ implementation V2). + pass _default_implementation_type = ( 'python' if _api_version <= 0 else 'cpp') diff --git a/python/google/protobuf/internal/containers.py b/python/google/protobuf/internal/containers.py index 68be9e54..c6a3692a 100755 --- a/python/google/protobuf/internal/containers.py +++ b/python/google/protobuf/internal/containers.py @@ -549,10 +549,10 @@ class MessageMap(MutableMapping): self._values = {} def __getitem__(self, key): + key = self._key_checker.CheckValue(key) try: return self._values[key] except KeyError: - key = self._key_checker.CheckValue(key) new_element = self._message_descriptor._concrete_class() new_element._SetListener(self._message_listener) self._values[key] = new_element @@ -584,12 +584,14 @@ class MessageMap(MutableMapping): return default def __contains__(self, item): + item = self._key_checker.CheckValue(item) return item in self._values def __setitem__(self, key, value): raise ValueError('May not set values directly, call my_map[key].foo = 5') def __delitem__(self, key): + key = self._key_checker.CheckValue(key) del self._values[key] self._message_listener.Modified() diff --git a/python/google/protobuf/internal/descriptor_database_test.py b/python/google/protobuf/internal/descriptor_database_test.py index 1f1a3db9..f97477b3 100644 --- a/python/google/protobuf/internal/descriptor_database_test.py +++ b/python/google/protobuf/internal/descriptor_database_test.py @@ -38,6 +38,7 @@ try: import unittest2 as unittest #PY26 except ImportError: import unittest +import warnings from google.protobuf import unittest_pb2 from google.protobuf import descriptor_pb2 @@ -98,6 +99,26 @@ class DescriptorDatabaseTest(unittest.TestCase): db.FindFileContainingSymbol, 'protobuf_unittest.NoneMessage') + def testConflictRegister(self): + db = descriptor_database.DescriptorDatabase() + unittest_fd = descriptor_pb2.FileDescriptorProto.FromString( + unittest_pb2.DESCRIPTOR.serialized_pb) + db.Add(unittest_fd) + conflict_fd = descriptor_pb2.FileDescriptorProto.FromString( + unittest_pb2.DESCRIPTOR.serialized_pb) + conflict_fd.name = 'other_file' + with warnings.catch_warnings(record=True) as w: + # Cause all warnings to always be triggered. + warnings.simplefilter('always') + db.Add(conflict_fd) + self.assertTrue(len(w)) + self.assertIs(w[0].category, RuntimeWarning) + self.assertIn('Conflict register for file "other_file": ', + str(w[0].message)) + self.assertIn('already defined in file ' + '"google/protobuf/unittest.proto"', + str(w[0].message)) + if __name__ == '__main__': unittest.main() diff --git a/python/google/protobuf/internal/descriptor_pool_test.py b/python/google/protobuf/internal/descriptor_pool_test.py index 15c857bb..2cbf7813 100644 --- a/python/google/protobuf/internal/descriptor_pool_test.py +++ b/python/google/protobuf/internal/descriptor_pool_test.py @@ -34,8 +34,10 @@ __author__ = 'matthewtoia@google.com (Matt Toia)' +import copy import os import sys +import warnings try: import unittest2 as unittest #PY26 @@ -119,6 +121,14 @@ class DescriptorPoolTestBase(object): self.assertEqual('google/protobuf/unittest.proto', file_desc5.name) + # Tests the generated pool. + assert descriptor_pool.Default().FindFileContainingSymbol( + 'google.protobuf.python.internal.Factory2Message.one_more_field') + assert descriptor_pool.Default().FindFileContainingSymbol( + 'google.protobuf.python.internal.another_field') + assert descriptor_pool.Default().FindFileContainingSymbol( + 'protobuf_unittest.TestService') + def testFindFileContainingSymbolFailure(self): with self.assertRaises(KeyError): self.pool.FindFileContainingSymbol('Does not exist') @@ -492,6 +502,52 @@ class DescriptorPoolTestBase(object): TEST1_FILE.CheckFile(self, self.pool) TEST2_FILE.CheckFile(self, self.pool) + def testConflictRegister(self): + if isinstance(self, SecondaryDescriptorFromDescriptorDB): + if api_implementation.Type() == 'cpp': + # Cpp extension cannot call Add on a DescriptorPool + # that uses a DescriptorDatabase. + # TODO(jieluo): Fix python and cpp extension diff. + return + unittest_fd = descriptor_pb2.FileDescriptorProto.FromString( + unittest_pb2.DESCRIPTOR.serialized_pb) + conflict_fd = copy.deepcopy(unittest_fd) + conflict_fd.name = 'other_file' + if api_implementation.Type() == 'cpp': + try: + self.pool.Add(unittest_fd) + self.pool.Add(conflict_fd) + except TypeError: + pass + else: + with warnings.catch_warnings(record=True) as w: + # Cause all warnings to always be triggered. + warnings.simplefilter('always') + pool = copy.deepcopy(self.pool) + # No warnings to add the same descriptors. + file_descriptor = unittest_pb2.DESCRIPTOR + pool.AddDescriptor( + file_descriptor.message_types_by_name['TestAllTypes']) + pool.AddEnumDescriptor( + file_descriptor.enum_types_by_name['ForeignEnum']) + pool.AddServiceDescriptor( + file_descriptor.services_by_name['TestService']) + pool.AddExtensionDescriptor( + file_descriptor.extensions_by_name['optional_int32_extension']) + self.assertEqual(len(w), 0) + # Check warnings for conflict descriptors with the same name. + pool.Add(unittest_fd) + pool.Add(conflict_fd) + pool.FindFileByName(unittest_fd.name) + pool.FindFileByName(conflict_fd.name) + self.assertTrue(len(w)) + self.assertIs(w[0].category, RuntimeWarning) + self.assertIn('Conflict register for file "other_file": ', + str(w[0].message)) + self.assertIn('already defined in file ' + '"google/protobuf/unittest.proto"', + str(w[0].message)) + class DefaultDescriptorPoolTest(DescriptorPoolTestBase, unittest.TestCase): diff --git a/python/google/protobuf/internal/descriptor_test.py b/python/google/protobuf/internal/descriptor_test.py index f1d5934e..02a43d15 100755 --- a/python/google/protobuf/internal/descriptor_test.py +++ b/python/google/protobuf/internal/descriptor_test.py @@ -742,6 +742,19 @@ class DescriptorCopyToProtoTest(unittest.TestCase): deprecated: true > > + field { + name: "deprecated_int32_in_oneof" + number: 2 + label: LABEL_OPTIONAL + type: TYPE_INT32 + options { + deprecated: true + } + oneof_index: 0 + } + oneof_decl { + name: "oneof_fields" + } """ self._InternalTestCopyToProto( diff --git a/python/google/protobuf/internal/encoder.py b/python/google/protobuf/internal/encoder.py index 40c62d67..0d1f49dd 100755 --- a/python/google/protobuf/internal/encoder.py +++ b/python/google/protobuf/internal/encoder.py @@ -372,7 +372,7 @@ def MapSizer(field_descriptor, is_message_map): def _VarintEncoder(): """Return an encoder for a basic varint value (does not include tag).""" - def EncodeVarint(write, value, unused_deterministic): + def EncodeVarint(write, value, unused_deterministic=None): bits = value & 0x7f value >>= 7 while value: @@ -388,7 +388,7 @@ def _SignedVarintEncoder(): """Return an encoder for a basic signed varint value (does not include tag).""" - def EncodeSignedVarint(write, value, unused_deterministic): + def EncodeSignedVarint(write, value, unused_deterministic=None): if value < 0: value += (1 << 64) bits = value & 0x7f @@ -418,7 +418,8 @@ def _VarintBytes(value): def TagBytes(field_number, wire_type): """Encode the given tag and return the bytes. Only called at startup.""" - return six.binary_type( _VarintBytes(wire_format.PackTag(field_number, wire_type)) ) + return six.binary_type( + _VarintBytes(wire_format.PackTag(field_number, wire_type))) # -------------------------------------------------------------------- # As with sizers (see above), we have a number of common encoder @@ -523,14 +524,14 @@ def _StructPackEncoder(wire_type, format): return EncodePackedField elif is_repeated: tag_bytes = TagBytes(field_number, wire_type) - def EncodeRepeatedField(write, value, unused_deterministic): + def EncodeRepeatedField(write, value, unused_deterministic=None): for element in value: write(tag_bytes) write(local_struct_pack(format, element)) return EncodeRepeatedField else: tag_bytes = TagBytes(field_number, wire_type) - def EncodeField(write, value, unused_deterministic): + def EncodeField(write, value, unused_deterministic=None): write(tag_bytes) return write(local_struct_pack(format, value)) return EncodeField @@ -594,7 +595,7 @@ def _FloatingPointEncoder(wire_type, format): return EncodePackedField elif is_repeated: tag_bytes = TagBytes(field_number, wire_type) - def EncodeRepeatedField(write, value, unused_deterministic): + def EncodeRepeatedField(write, value, unused_deterministic=None): for element in value: write(tag_bytes) try: @@ -604,7 +605,7 @@ def _FloatingPointEncoder(wire_type, format): return EncodeRepeatedField else: tag_bytes = TagBytes(field_number, wire_type) - def EncodeField(write, value, unused_deterministic): + def EncodeField(write, value, unused_deterministic=None): write(tag_bytes) try: write(local_struct_pack(format, value)) @@ -661,7 +662,7 @@ def BoolEncoder(field_number, is_repeated, is_packed): return EncodePackedField elif is_repeated: tag_bytes = TagBytes(field_number, wire_format.WIRETYPE_VARINT) - def EncodeRepeatedField(write, value, unused_deterministic): + def EncodeRepeatedField(write, value, unused_deterministic=None): for element in value: write(tag_bytes) if element: @@ -671,7 +672,7 @@ def BoolEncoder(field_number, is_repeated, is_packed): return EncodeRepeatedField else: tag_bytes = TagBytes(field_number, wire_format.WIRETYPE_VARINT) - def EncodeField(write, value, unused_deterministic): + def EncodeField(write, value, unused_deterministic=None): write(tag_bytes) if value: return write(true_byte) diff --git a/python/google/protobuf/internal/json_format_test.py b/python/google/protobuf/internal/json_format_test.py index 19182b7f..7f649406 100644 --- a/python/google/protobuf/internal/json_format_test.py +++ b/python/google/protobuf/internal/json_format_test.py @@ -791,9 +791,6 @@ class JsonFormatTest(JsonFormatBase): json_format.Parse(text, parsed_message, ignore_unknown_fields=True) def testDuplicateField(self): - # Duplicate key check is not supported for python2.6 - if sys.version_info < (2, 7): - return self.CheckError('{"int32Value": 1,\n"int32Value":2}', 'Failed to load JSON: duplicate key int32Value.') @@ -983,6 +980,18 @@ class JsonFormatTest(JsonFormatBase): self.assertEqual('{\n"int32Value": 12345\n}', json_format.MessageToJson(message, indent=0)) + def testFormatEnumsAsInts(self): + message = json_format_proto3_pb2.TestMessage() + message.enum_value = json_format_proto3_pb2.BAR + message.repeated_enum_value.append(json_format_proto3_pb2.FOO) + message.repeated_enum_value.append(json_format_proto3_pb2.BAR) + self.assertEqual(json.loads('{\n' + ' "enumValue": 1,\n' + ' "repeatedEnumValue": [0, 1]\n' + '}\n'), + json.loads(json_format.MessageToJson( + message, use_integers_for_enums=True))) + def testParseDict(self): expected = 12345 js_dict = {'int32Value': expected} diff --git a/python/google/protobuf/internal/message_test.py b/python/google/protobuf/internal/message_test.py index a303b1aa..61a56a67 100755 --- a/python/google/protobuf/internal/message_test.py +++ b/python/google/protobuf/internal/message_test.py @@ -99,7 +99,7 @@ def IsNegInf(val): BaseTestCase = testing_refleaks.BaseTestCase -@_parameterized.NamedParameters( +@_parameterized.named_parameters( ('_proto2', unittest_pb2), ('_proto3', unittest_proto3_arena_pb2)) class MessageTest(BaseTestCase): @@ -1480,12 +1480,8 @@ class Proto3Test(BaseTestCase): submsg = msg.map_int32_foreign_message[5] self.assertIs(submsg, msg.map_int32_foreign_message.get(5)) - # TODO(jieluo): Fix python and cpp extension diff. - if api_implementation.Type() == 'cpp': - with self.assertRaises(TypeError): - msg.map_int32_foreign_message.get('') - else: - self.assertEqual(None, msg.map_int32_foreign_message.get('')) + with self.assertRaises(TypeError): + msg.map_int32_foreign_message.get('') def testScalarMap(self): msg = map_unittest_pb2.TestMap() @@ -1695,12 +1691,35 @@ class Proto3Test(BaseTestCase): del msg2.map_int32_foreign_message[222] self.assertFalse(222 in msg2.map_int32_foreign_message) - if api_implementation.Type() == 'cpp': - with self.assertRaises(TypeError): - del msg2.map_int32_foreign_message[''] - else: - with self.assertRaises(KeyError): - del msg2.map_int32_foreign_message[''] + with self.assertRaises(TypeError): + del msg2.map_int32_foreign_message[''] + + def testMapMergeFrom(self): + msg = map_unittest_pb2.TestMap() + msg.map_int32_int32[12] = 34 + msg.map_int32_int32[56] = 78 + msg.map_int64_int64[22] = 33 + msg.map_int32_foreign_message[111].c = 5 + msg.map_int32_foreign_message[222].c = 10 + + msg2 = map_unittest_pb2.TestMap() + msg2.map_int32_int32[12] = 55 + msg2.map_int64_int64[88] = 99 + msg2.map_int32_foreign_message[222].c = 15 + msg2.map_int32_foreign_message[222].d = 20 + + msg2.map_int32_int32.MergeFrom(msg.map_int32_int32) + self.assertEqual(34, msg2.map_int32_int32[12]) + self.assertEqual(78, msg2.map_int32_int32[56]) + + msg2.map_int64_int64.MergeFrom(msg.map_int64_int64) + self.assertEqual(33, msg2.map_int64_int64[22]) + self.assertEqual(99, msg2.map_int64_int64[88]) + + msg2.map_int32_foreign_message.MergeFrom(msg.map_int32_foreign_message) + self.assertEqual(5, msg2.map_int32_foreign_message[111].c) + self.assertEqual(10, msg2.map_int32_foreign_message[222].c) + self.assertFalse(msg2.map_int32_foreign_message[222].HasField('d')) def testMergeFromBadType(self): msg = map_unittest_pb2.TestMap() diff --git a/python/google/protobuf/internal/no_package.proto b/python/google/protobuf/internal/no_package.proto new file mode 100644 index 00000000..3546dcc3 --- /dev/null +++ b/python/google/protobuf/internal/no_package.proto @@ -0,0 +1,10 @@ +syntax = "proto2"; + +enum NoPackageEnum { + NO_PACKAGE_VALUE_0 = 0; + NO_PACKAGE_VALUE_1 = 1; +} + +message NoPackageMessage { + optional NoPackageEnum no_package_enum = 1; +} diff --git a/python/google/protobuf/internal/text_format_test.py b/python/google/protobuf/internal/text_format_test.py index a52f133f..237a2d50 100755 --- a/python/google/protobuf/internal/text_format_test.py +++ b/python/google/protobuf/internal/text_format_test.py @@ -48,6 +48,7 @@ except ImportError: from google.protobuf.internal import _parameterized +from google.protobuf import any_pb2 from google.protobuf import any_test_pb2 from google.protobuf import map_unittest_pb2 from google.protobuf import unittest_mset_pb2 @@ -99,7 +100,7 @@ class TextFormatBase(unittest.TestCase): return text -@_parameterized.Parameters((unittest_pb2), (unittest_proto3_arena_pb2)) +@_parameterized.parameters((unittest_pb2), (unittest_proto3_arena_pb2)) class TextFormatTest(TextFormatBase): def testPrintExotic(self, message_module): @@ -369,6 +370,7 @@ class TextFormatTest(TextFormatBase): def testParseRepeatedScalarShortFormat(self, message_module): message = message_module.TestAllTypes() text = ('repeated_int64: [100, 200];\n' + 'repeated_int64: []\n' 'repeated_int64: 300,\n' 'repeated_string: ["one", "two"];\n') text_format.Parse(text, message) @@ -524,20 +526,68 @@ class OnlyWorksWithProto2RightNowTests(TextFormatBase): def testPrintInIndexOrder(self): message = unittest_pb2.TestFieldOrderings() - message.my_string = '115' + # Fields are listed in index order instead of field number. + message.my_string = 'str' message.my_int = 101 message.my_float = 111 message.optional_nested_message.oo = 0 message.optional_nested_message.bb = 1 + message.Extensions[unittest_pb2.my_extension_string] = 'ext_str0' + # Extensions are listed based on the order of extension number. + # Extension number 12. + message.Extensions[unittest_pb2.TestExtensionOrderings2. + test_ext_orderings2].my_string = 'ext_str2' + # Extension number 13. + message.Extensions[unittest_pb2.TestExtensionOrderings1. + test_ext_orderings1].my_string = 'ext_str1' + # Extension number 14. + message.Extensions[ + unittest_pb2.TestExtensionOrderings2.TestExtensionOrderings3. + test_ext_orderings3].my_string = 'ext_str3' + + # Print in index order. self.CompareToGoldenText( - self.RemoveRedundantZeros(text_format.MessageToString( - message, use_index_order=True)), - 'my_string: \"115\"\nmy_int: 101\nmy_float: 111\n' - 'optional_nested_message {\n oo: 0\n bb: 1\n}\n') + self.RemoveRedundantZeros( + text_format.MessageToString(message, use_index_order=True)), + 'my_string: "str"\n' + 'my_int: 101\n' + 'my_float: 111\n' + 'optional_nested_message {\n' + ' oo: 0\n' + ' bb: 1\n' + '}\n' + '[protobuf_unittest.TestExtensionOrderings2.test_ext_orderings2] {\n' + ' my_string: "ext_str2"\n' + '}\n' + '[protobuf_unittest.TestExtensionOrderings1.test_ext_orderings1] {\n' + ' my_string: "ext_str1"\n' + '}\n' + '[protobuf_unittest.TestExtensionOrderings2.TestExtensionOrderings3' + '.test_ext_orderings3] {\n' + ' my_string: "ext_str3"\n' + '}\n' + '[protobuf_unittest.my_extension_string]: "ext_str0"\n') + # By default, print in field number order. self.CompareToGoldenText( self.RemoveRedundantZeros(text_format.MessageToString(message)), - 'my_int: 101\nmy_string: \"115\"\nmy_float: 111\n' - 'optional_nested_message {\n bb: 1\n oo: 0\n}\n') + 'my_int: 101\n' + 'my_string: "str"\n' + '[protobuf_unittest.TestExtensionOrderings2.test_ext_orderings2] {\n' + ' my_string: "ext_str2"\n' + '}\n' + '[protobuf_unittest.TestExtensionOrderings1.test_ext_orderings1] {\n' + ' my_string: "ext_str1"\n' + '}\n' + '[protobuf_unittest.TestExtensionOrderings2.TestExtensionOrderings3' + '.test_ext_orderings3] {\n' + ' my_string: "ext_str3"\n' + '}\n' + '[protobuf_unittest.my_extension_string]: "ext_str0"\n' + 'my_float: 111\n' + 'optional_nested_message {\n' + ' bb: 1\n' + ' oo: 0\n' + '}\n') def testMergeLinesGolden(self): opened = self.ReadGolden('text_format_unittest_data_oneof_implemented.txt') @@ -828,6 +878,10 @@ class Proto2Tests(TextFormatBase): ' }\n' ' }\n' ' [unknown_extension]: 5\n' + ' [unknown_extension_with_number_field] {\n' + ' 1: "some_field"\n' + ' 2: -0.451\n' + ' }\n' '}\n') text_format.Parse(text, message, allow_unknown_extension=True) golden = 'message_set {\n}\n' @@ -894,7 +948,6 @@ class Proto2Tests(TextFormatBase): message = unittest_mset_pb2.TestMessageSetContainer() malformed = ('message_set {\n' ' unknown_field: true\n' - ' \n' # Missing '>' here. '}\n') six.assertRaisesRegex(self, text_format.ParseError, @@ -906,7 +959,7 @@ class Proto2Tests(TextFormatBase): message, allow_unknown_extension=True) - # Parse known extension correcty. + # Parse known extension correctly. message = unittest_mset_pb2.TestMessageSetContainer() text = ('message_set {\n' ' [protobuf_unittest.TestMessageSetExtension1] {\n' @@ -967,15 +1020,26 @@ class Proto2Tests(TextFormatBase): '"protobuf_unittest.optional_int32_extension" extensions.'), text_format.Parse, text, message) - def testParseDuplicateNestedMessageScalars(self): + def testParseDuplicateMessages(self): message = unittest_pb2.TestAllTypes() text = ('optional_nested_message { bb: 1 } ' 'optional_nested_message { bb: 2 }') six.assertRaisesRegex(self, text_format.ParseError, ( - '1:65 : Message type "protobuf_unittest.TestAllTypes.NestedMessage" ' - 'should not have multiple "bb" fields.'), text_format.Parse, text, + '1:59 : Message type "protobuf_unittest.TestAllTypes" ' + 'should not have multiple "optional_nested_message" fields.'), + text_format.Parse, text, message) + def testParseDuplicateExtensionMessages(self): + message = unittest_pb2.TestAllExtensions() + text = ('[protobuf_unittest.optional_nested_message_extension]: {} ' + '[protobuf_unittest.optional_nested_message_extension]: {}') + six.assertRaisesRegex(self, text_format.ParseError, ( + '1:114 : Message type "protobuf_unittest.TestAllExtensions" ' + 'should not have multiple ' + '"protobuf_unittest.optional_nested_message_extension" extensions.'), + text_format.Parse, text, message) + def testParseDuplicateScalars(self): message = unittest_pb2.TestAllTypes() text = ('optional_int32: 42 ' 'optional_int32: 67') @@ -1062,6 +1126,14 @@ class Proto3Tests(unittest.TestCase): ' }\n' '}\n') + def testTopAnyMessage(self): + packed_msg = unittest_pb2.OneString() + msg = any_pb2.Any() + msg.Pack(packed_msg) + text = text_format.MessageToString(msg) + other_msg = text_format.Parse(text, any_pb2.Any()) + self.assertEqual(msg, other_msg) + def testPrintMessageExpandAnyRepeated(self): packed_message = unittest_pb2.OneString() message = any_test_pb2.TestAny() @@ -1486,7 +1558,7 @@ class TokenizerTest(unittest.TestCase): # Tests for pretty printer functionality. -@_parameterized.Parameters((unittest_pb2), (unittest_proto3_arena_pb2)) +@_parameterized.parameters((unittest_pb2), (unittest_proto3_arena_pb2)) class PrettyPrinterTest(TextFormatBase): def testPrettyPrintNoMatch(self, message_module): diff --git a/python/google/protobuf/internal/well_known_types.py b/python/google/protobuf/internal/well_known_types.py index 3573770b..37a65cfa 100644 --- a/python/google/protobuf/internal/well_known_types.py +++ b/python/google/protobuf/internal/well_known_types.py @@ -375,6 +375,9 @@ def _CheckDurationValid(seconds, nanos): raise Error( 'Duration is not valid: Nanos {0} must be in range ' '[-999999999, 999999999].'.format(nanos)) + if (nanos < 0 and seconds > 0) or (nanos > 0 and seconds < 0): + raise Error( + 'Duration is not valid: Sign mismatch.') def _RoundTowardZero(value, divider): @@ -649,9 +652,10 @@ def _MergeMessage( raise ValueError('Error: Field {0} in message {1} is not a singular ' 'message field and cannot have sub-fields.'.format( name, source_descriptor.full_name)) - _MergeMessage( - child, getattr(source, name), getattr(destination, name), - replace_message, replace_repeated) + if source.HasField(name): + _MergeMessage( + child, getattr(source, name), getattr(destination, name), + replace_message, replace_repeated) continue if field.label == FieldDescriptor.LABEL_REPEATED: if replace_repeated: diff --git a/python/google/protobuf/internal/well_known_types_test.py b/python/google/protobuf/internal/well_known_types_test.py index 291fe4e8..965940b2 100644 --- a/python/google/protobuf/internal/well_known_types_test.py +++ b/python/google/protobuf/internal/well_known_types_test.py @@ -101,7 +101,7 @@ class TimeUtilTest(TimeUtilTestBase): message.FromJsonString('1970-01-01T00:00:00.1Z') self.assertEqual(0, message.seconds) self.assertEqual(100000000, message.nanos) - # Parsing accpets offsets. + # Parsing accepts offsets. message.FromJsonString('1970-01-01T00:00:00-08:00') self.assertEqual(8 * 3600, message.seconds) self.assertEqual(0, message.nanos) @@ -345,6 +345,12 @@ class TimeUtilTest(TimeUtilTestBase): r'Duration is not valid\: Nanos 1000000000 must be in range' r' \[-999999999\, 999999999\].', message.ToJsonString) + message.seconds = -1 + message.nanos = 1 + self.assertRaisesRegexp( + well_known_types.Error, + r'Duration is not valid\: Sign mismatch.', + message.ToJsonString) class FieldMaskTest(unittest.TestCase): @@ -599,6 +605,16 @@ class FieldMaskTest(unittest.TestCase): self.assertEqual(1, len(nested_dst.payload.repeated_int32)) self.assertEqual(1234, nested_dst.payload.repeated_int32[0]) + # Test Merge oneof field. + new_msg = unittest_pb2.TestOneof2() + dst = unittest_pb2.TestOneof2() + dst.foo_message.qux_int = 1 + mask = field_mask_pb2.FieldMask() + mask.FromJsonString('fooMessage,fooLazyMessage.quxInt') + mask.MergeMessage(new_msg, dst) + self.assertTrue(dst.HasField('foo_message')) + self.assertFalse(dst.HasField('foo_lazy_message')) + def testMergeErrors(self): src = unittest_pb2.TestAllTypes() dst = unittest_pb2.TestAllTypes() |