diff --git a/python/BUILD b/python/BUILD index 7c4088fe22..4cbc17add1 100644 --- a/python/BUILD +++ b/python/BUILD @@ -28,3 +28,19 @@ cc_binary( "@python_headers", ], ) + +# Copy the extension into the location recognized by Python. +genrule( + name = "message_ext", + srcs = [":message"], + outs = ["google/protobuf/pyext/_message.cpython-39-x86_64-linux-gnu.so"], + cmd = "cp $< $@", +) + +py_test( + name = "minimal_test", + srcs = ["minimal_test.py"], + data = [":message_ext"], + imports = ["."], + legacy_create_init = False, +) diff --git a/python/descriptor.c b/python/descriptor.c index b700fbbcc2..767c368494 100644 --- a/python/descriptor.c +++ b/python/descriptor.c @@ -56,11 +56,16 @@ static PyObject *PyUpb_DescriptorBase_New(PyTypeObject *subtype, PyObject *args, static PyObject *PyUpb_DescriptorBase_NewInternal(PyTypeObject *type, const void *def, PyObject *pool) { - PyUpb_DescriptorBase *base = PyObject_New(PyUpb_DescriptorBase, type); - base->pool = pool; - base->def = def; - Py_INCREF(pool); - PyUpb_ObjCache_Add(def, &base->ob_base); + PyUpb_DescriptorBase *base = (PyUpb_DescriptorBase*)PyUpb_ObjCache_Get(def); + + if (!base) { + base = PyObject_New(PyUpb_DescriptorBase, type); + base->pool = pool; + base->def = def; + Py_INCREF(pool); + PyUpb_ObjCache_Add(def, &base->ob_base); + } + return &base->ob_base; } @@ -79,11 +84,6 @@ static void PyUpb_DescriptorBase_Dealloc(PyUpb_DescriptorBase *self) { // FieldDescriptor // ----------------------------------------------------------------------------- -typedef struct { - PyObject_HEAD - upb_fielddef *fielddef; -} PyUpb_FieldDescriptor; - static PyObject *PyUpb_FieldDescriptor_GetType(PyUpb_DescriptorBase *self, void *closure) { return PyLong_FromLong(upb_fielddef_descriptortype(self->def)); @@ -94,6 +94,11 @@ static PyObject *PyUpb_FieldDescriptor_GetLabel(PyUpb_DescriptorBase *self, return PyLong_FromLong(upb_fielddef_label(self->def)); } +static PyObject *PyUpb_FieldDescriptor_GetNumber(PyUpb_DescriptorBase *self, + void *closure) { + return PyLong_FromLong(upb_fielddef_number(self->def)); +} + static PyGetSetDef PyUpb_FieldDescriptor_Getters[] = { /* { "full_name", (getter)GetFullName, NULL, "Full name"}, @@ -107,8 +112,8 @@ static PyGetSetDef PyUpb_FieldDescriptor_Getters[] = { { "cpp_type", (getter)PyUpb_FieldDescriptor_GetCppType, NULL, "C++ Type"}, */ { "label", (getter)PyUpb_FieldDescriptor_GetLabel, NULL, "Label"}, + { "number", (getter)PyUpb_FieldDescriptor_GetNumber, NULL, "Number"}, /* - { "number", (getter)GetNumber, NULL, "Number"}, { "index", (getter)GetIndex, NULL, "Index"}, { "default_value", (getter)GetDefaultValue, NULL, "Default Value"}, { "has_default_value", (getter)HasDefaultValue}, @@ -148,10 +153,10 @@ static PyType_Slot PyUpb_FieldDescriptor_Slots[] = { }; static PyType_Spec PyUpb_FieldDescriptor_Spec = { - PYUPB_MODULE_NAME ".FieldDescriptor", // tp_name - sizeof(PyUpb_FieldDescriptor), // tp_basicsize + PYUPB_MODULE_NAME ".FieldDescriptor", + sizeof(PyUpb_DescriptorBase), 0, // tp_itemsize - Py_TPFLAGS_DEFAULT, // tp_flags + Py_TPFLAGS_DEFAULT, PyUpb_FieldDescriptor_Slots, }; diff --git a/python/minimal_test.py b/python/minimal_test.py new file mode 100644 index 0000000000..b9303f4911 --- /dev/null +++ b/python/minimal_test.py @@ -0,0 +1,29 @@ +"""A bare-bones unit test, to be removed once upb can pass existing unit tests.""" + + +import unittest +from google.protobuf.pyext import _message + +class TestMessageExtension(unittest.TestCase): + + def test_descriptor_pool(self): + serialized_desc = b'\n\ntest.proto\"\x0e\n\x02M1*\x08\x08\x01\x10\x80\x80\x80\x80\x02:\x15\n\x08test_ext\x12\x03.M1\x18\x01 \x01(\x05' + pool = _message.DescriptorPool() + file_desc = pool.AddSerializedFile(serialized_desc) + self.assertEqual("test.proto", file_desc.name) + ext_desc = pool.FindExtensionByName("test_ext") + self.assertEqual(1, ext_desc.number) + + # Test object cache: repeatedly retrieving the same descriptor + # should result in the same object + self.assertIs(ext_desc, pool.FindExtensionByName("test_ext")) + + + def test_lib_is_upb(self): + # Ensure we are not pulling in a different protobuf library on the + # system. + self.assertTrue(_message._IS_UPB) + + +if __name__ == '__main__': + unittest.main() diff --git a/python/protobuf.c b/python/protobuf.c index 62398cf471..eeea3b3e6a 100644 --- a/python/protobuf.c +++ b/python/protobuf.c @@ -27,10 +27,11 @@ #include "protobuf.h" +#include "descriptor.h" #include "descriptor_pool.h" -static void PyUpb_ModuleDealloc(void *_s) { - PyUpb_ModuleState *s = _s; +static void PyUpb_ModuleDealloc(void *module) { + PyUpb_ModuleState *s = PyModule_GetState(module); upb_arena_free(s->obj_cache_arena); } @@ -114,10 +115,14 @@ PyMODINIT_FUNC PyInit__message(void) { state->obj_cache_arena = upb_arena_new(); upb_inttable_init(&state->obj_cache, state->obj_cache_arena); - if (!PyUpb_InitDescriptorPool(m)) { + if (!PyUpb_InitDescriptorPool(m) || !PyUpb_InitDescriptor(m)) { Py_DECREF(m); return NULL; } + // Temporary: an cookie we can use in the tests to ensure we are testing upb + // and not another protobuf library on the system. + PyModule_AddObject(m, "_IS_UPB", Py_True); + return m; }