Fixed or accounted for nearly all errors in message_test.
diff --git a/python/message.c b/python/message.c index b0df698..fd0d37e 100644 --- a/python/message.c +++ b/python/message.c
@@ -847,9 +847,12 @@ PyObject* ret = PyObject_GenericGetAttr(_self, attr); if (ret) return ret; - // If the attribute wasn't found, look for attributes on the class. But if a - // different kind of error (other than AttributeError) was found, return that. - if (PyErr_ExceptionMatches(PyExc_AttributeError)) { + // Swallow AttributeError if it occurred and try again on the metaclass + // to pick up class attributes. But we have to special-case "Extensions" + // which affirmatively returns AttributeError when a message is not + // extendable. + if (PyErr_ExceptionMatches(PyExc_AttributeError) && + strcmp(PyUpb_GetStrData(attr), "Extensions") != 0) { PyErr_Clear(); return PyUpb_MessageMeta_GetAttr((PyObject*)Py_TYPE(_self), attr); } @@ -986,11 +989,14 @@ const upb_extreg* extreg = upb_symtab_extreg(upb_filedef_symtab(file)); const upb_msglayout* layout = upb_msgdef_layout(msgdef); upb_arena* arena = PyUpb_Arena_Get(self->arena); + int options = 0; + PyUpb_ModuleState* state = PyUpb_ModuleState_Get(); + options |= + UPB_DECODE_MAXDEPTH(state->allow_oversize_protos ? UINT32_MAX : 100); upb_DecodeStatus status = - _upb_decode(buf, size, self->ptr.msg, layout, extreg, 0, arena); + _upb_decode(buf, size, self->ptr.msg, layout, extreg, options, arena); Py_XDECREF(bytes); if (status != kUpb_DecodeStatus_Ok) { - PyUpb_ModuleState* state = PyUpb_ModuleState_Get(); PyErr_Format(state->decode_error_class, "Error parsing message"); return NULL; } @@ -1183,6 +1189,8 @@ int options = 0; if (check_required) options |= UPB_ENCODE_CHECKREQUIRED; if (deterministic) options |= UPB_ENCODE_DETERMINISTIC; + // Python does not currently have any effective limit on serialization depth. + options |= UPB_ENCODE_MAXDEPTH(UINT32_MAX); char* pb = upb_encode_ex(self->ptr.msg, layout, options, arena, &size); PyObject* ret = NULL; @@ -1248,10 +1256,6 @@ static PyGetSetDef PyUpb_CMessage_Getters[] = { {"Extensions", PyUpb_CMessage_GetExtensionDict, NULL, "Extension dict"}, - /* - {"_extensions_by_name", (getter)GetExtensionsByName, NULL}, - {"_extensions_by_number", (getter)GetExtensionsByNumber, NULL}, - */ {NULL}}; static PyMethodDef PyUpb_CMessage_Methods[] = {
diff --git a/python/minimal_test.py b/python/minimal_test.py index 26d6553..01eef6e 100644 --- a/python/minimal_test.py +++ b/python/minimal_test.py
@@ -32,6 +32,9 @@ from google.protobuf.internal import api_implementation from google.protobuf import unittest_pb2 from google.protobuf import descriptor_pool +from google.protobuf import text_format +from google.protobuf import message_factory +from google.protobuf import message from google.protobuf.internal import factory_test1_pb2 from google.protobuf.internal import factory_test2_pb2 from google.protobuf import descriptor_pb2 @@ -72,9 +75,35 @@ test_slice(11, 3, -2) test_slice(11, 3, -3) test_slice(10, 25, 4) + + def testExtensionsErrors(self): + msg = unittest_pb2.TestAllTypes() + self.assertRaises(AttributeError, getattr, msg, 'Extensions') #TestMessageExtension.test_descriptor_pool.__unittest_expecting_failure__ = True +class OversizeProtosTest(unittest.TestCase): + def setUp(self): + msg = unittest_pb2.NestedTestAllTypes() + m = msg + for i in range(101): + m = m.child + m.Clear() + self.p_serialized = msg.SerializeToString() + + def testAssertOversizeProto(self): + from google.protobuf.pyext._message import SetAllowOversizeProtos + SetAllowOversizeProtos(False) + q = unittest_pb2.NestedTestAllTypes() + with self.assertRaises(message.DecodeError): + q.ParseFromString(self.p_serialized) + print(q) + + def testSucceedOversizeProto(self): + from google.protobuf.pyext._message import SetAllowOversizeProtos + SetAllowOversizeProtos(True) + q = unittest_pb2.NestedTestAllTypes() + q.ParseFromString(self.p_serialized) if __name__ == '__main__': unittest.main(verbosity=2)
diff --git a/python/pb_unit_tests/message_test_wrapper.py b/python/pb_unit_tests/message_test_wrapper.py index 993f8e0..d73997b 100644 --- a/python/pb_unit_tests/message_test_wrapper.py +++ b/python/pb_unit_tests/message_test_wrapper.py
@@ -26,28 +26,29 @@ from google.protobuf.internal import message_test import unittest -message_test.MessageTest.testBadUtf8String_proto2.__unittest_expecting_failure__ = True -message_test.MessageTest.testBadUtf8String_proto3.__unittest_expecting_failure__ = True +# We don't want to support extending repeated fields with nothing; this behavior +# is marked for deprecation in the existing library. message_test.MessageTest.testExtendFloatWithNothing_proto2.__unittest_expecting_failure__ = True message_test.MessageTest.testExtendFloatWithNothing_proto3.__unittest_expecting_failure__ = True message_test.MessageTest.testExtendInt32WithNothing_proto2.__unittest_expecting_failure__ = True message_test.MessageTest.testExtendInt32WithNothing_proto3.__unittest_expecting_failure__ = True message_test.MessageTest.testExtendStringWithNothing_proto2.__unittest_expecting_failure__ = True message_test.MessageTest.testExtendStringWithNothing_proto3.__unittest_expecting_failure__ = True + +# Our float printing suffers from not having dtoa(). message_test.MessageTest.testFloatPrinting_proto2.__unittest_expecting_failure__ = True message_test.MessageTest.testFloatPrinting_proto3.__unittest_expecting_failure__ = True message_test.MessageTest.testHighPrecisionDoublePrinting_proto2.__unittest_expecting_failure__ = True message_test.MessageTest.testHighPrecisionDoublePrinting_proto3.__unittest_expecting_failure__ = True -message_test.MessageTest.testInsertRepeatedCompositeField_proto2.__unittest_expecting_failure__ = True -message_test.MessageTest.testInsertRepeatedCompositeField_proto3.__unittest_expecting_failure__ = True -message_test.Proto2Test.testExtensionsErrors.__unittest_expecting_failure__ = True + +# For these tests we are throwing the correct error, only the text of the error +# message is a mismatch. For technical reasons around the limited API, matching +# the existing error message exactly is not feasible. +message_test.Proto3Test.testCopyFromBadType.__unittest_expecting_failure__ = True +message_test.Proto3Test.testMergeFromBadType.__unittest_expecting_failure__ = True + message_test.Proto2Test.testPythonicInit.__unittest_expecting_failure__ = True message_test.Proto2Test.test_documentation.__unittest_expecting_failure__ = True -message_test.Proto3Test.testCopyFromBadType.__unittest_expecting_failure__ = True -message_test.Proto3Test.testMapDeterministicSerialization.__unittest_expecting_failure__ = True -message_test.Proto3Test.testMergeFromBadType.__unittest_expecting_failure__ = True -message_test.OversizeProtosTest.testAssertOversizeProto.__unittest_expecting_failure__ = True -message_test.OversizeProtosTest.testSucceedOversizeProto.__unittest_expecting_failure__ = True if __name__ == '__main__': unittest.main(module=message_test, verbosity=2)
diff --git a/python/pb_unit_tests/well_known_types_test_wrapper.py b/python/pb_unit_tests/well_known_types_test_wrapper.py index 22a35d5..6ded8d4 100644 --- a/python/pb_unit_tests/well_known_types_test_wrapper.py +++ b/python/pb_unit_tests/well_known_types_test_wrapper.py
@@ -26,7 +26,5 @@ from google.protobuf.internal import well_known_types_test import unittest -well_known_types_test.AnyTest.testPackDeterministic.__unittest_expecting_failure__ = True - if __name__ == '__main__': unittest.main(module=well_known_types_test, verbosity=2)
diff --git a/python/protobuf.c b/python/protobuf.c index c577281..8eb49a4 100644 --- a/python/protobuf.c +++ b/python/protobuf.c
@@ -40,11 +40,28 @@ PyUpb_WeakMap_Free(s->obj_cache); } +PyObject* PyUpb_SetAllowOversizeProtos(PyObject* m, PyObject* arg) { + if (!arg || !PyBool_Check(arg)) { + PyErr_SetString(PyExc_TypeError, + "Argument to SetAllowOversizeProtos must be boolean"); + return NULL; + } + PyUpb_ModuleState* state = PyUpb_ModuleState_Get(); + state->allow_oversize_protos = PyObject_IsTrue(arg); + Py_INCREF(arg); + return arg; +} + +static PyMethodDef PyUpb_ModuleMethods[] = { + {"SetAllowOversizeProtos", PyUpb_SetAllowOversizeProtos, METH_O, + "Enable/disable oversize proto parsing."}, + {NULL, NULL}}; + static struct PyModuleDef module_def = {PyModuleDef_HEAD_INIT, PYUPB_MODULE_NAME, "Protobuf Module", sizeof(PyUpb_ModuleState), - NULL, // m_methods + PyUpb_ModuleMethods, // m_methods NULL, // m_slots NULL, // m_traverse NULL, // m_clear @@ -302,6 +319,7 @@ PyUpb_ModuleState *state = PyUpb_ModuleState_GetFromModule(m); + state->allow_oversize_protos = false; state->wkt_bases = NULL; state->obj_cache = PyUpb_WeakMap_New();
diff --git a/python/protobuf.h b/python/protobuf.h index fa9785a..75c8e01 100644 --- a/python/protobuf.h +++ b/python/protobuf.h
@@ -83,6 +83,7 @@ PyTypeObject *message_meta_type; // From protobuf.c + bool allow_oversize_protos; PyObject *wkt_bases; PyTypeObject *arena_type; PyUpb_WeakMap *obj_cache;