Initial commit of Python+upb.
diff --git a/python/BUILD b/python/BUILD new file mode 100644 index 0000000..7c4088f --- /dev/null +++ b/python/BUILD
@@ -0,0 +1,30 @@ +load( + "//bazel:build_defs.bzl", + "UPB_DEFAULT_COPTS", +) + +cc_binary( + name = "message", + srcs = [ + "descriptor.c", + "descriptor.h", + "descriptor_pool.c", + "descriptor_pool.h", + "protobuf.c", + "protobuf.h", + ], + copts = UPB_DEFAULT_COPTS + [ + # The Python API requires patterns that are ISO C incompatible, like + # casts between function pointers and object pointers. + "-Wno-pedantic", + ], + linkopts = ["-Wl,--version-script,$(location :version_script.lds)"], + linkshared = True, + linkstatic = True, + deps = [ + ":version_script.lds", + "//:reflection", + "//:upb", + "@python_headers", + ], +)
diff --git a/python/descriptor.c b/python/descriptor.c new file mode 100644 index 0000000..b700fbb --- /dev/null +++ b/python/descriptor.c
@@ -0,0 +1,249 @@ +/* + * Copyright (c) 2009-2021, Google LLC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Google LLC nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "python/descriptor.h" + +#include "python/protobuf.h" +#include "upb/def.h" + +// ----------------------------------------------------------------------------- +// DescriptorBase +// ----------------------------------------------------------------------------- + +// This representation is used by all concrete descriptors. + +typedef struct { + PyObject_HEAD + PyObject *pool; // We own a ref. + const void *def; // Type depends on the class. Kept alive by "pool". +} PyUpb_DescriptorBase; + +PyObject *PyUpb_AnyDescriptor_GetPool(PyObject *desc) { + PyUpb_DescriptorBase *base = (void*)desc; + return base->pool; +} + +static PyObject *PyUpb_DescriptorBase_New(PyTypeObject *subtype, PyObject *args, + PyObject *kwds) { + return PyErr_Format(PyExc_RuntimeError, + "Creating descriptors directly is not allowed."); +} + +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); + return &base->ob_base; +} + +static void PyUpb_DescriptorBase_Dealloc(PyUpb_DescriptorBase *self) { + PyUpb_DescriptorBase *base = (PyUpb_DescriptorBase*)self; + PyUpb_ObjCache_Delete(base->def); + Py_CLEAR(base->pool); + PyObject_Del(self); +} + +#define DESCRIPTOR_BASE_SLOTS \ + {Py_tp_new, (void*)&PyUpb_DescriptorBase_New}, \ + {Py_tp_dealloc, (void*)&PyUpb_DescriptorBase_Dealloc} + +// ----------------------------------------------------------------------------- +// 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)); +} + +static PyObject *PyUpb_FieldDescriptor_GetLabel(PyUpb_DescriptorBase *self, + void *closure) { + return PyLong_FromLong(upb_fielddef_label(self->def)); +} + +static PyGetSetDef PyUpb_FieldDescriptor_Getters[] = { + /* + { "full_name", (getter)GetFullName, NULL, "Full name"}, + { "name", (getter)GetName, NULL, "Unqualified name"}, + { "camelcase_name", (getter)GetCamelcaseName, NULL, "Camelcase name"}, + { "json_name", (getter)GetJsonName, NULL, "Json name"}, + { "file", (getter)GetFile, NULL, "File Descriptor"}, + */ + { "type", (getter)PyUpb_FieldDescriptor_GetType, NULL, "Type"}, + /* + { "cpp_type", (getter)PyUpb_FieldDescriptor_GetCppType, NULL, "C++ Type"}, + */ + { "label", (getter)PyUpb_FieldDescriptor_GetLabel, NULL, "Label"}, + /* + { "number", (getter)GetNumber, NULL, "Number"}, + { "index", (getter)GetIndex, NULL, "Index"}, + { "default_value", (getter)GetDefaultValue, NULL, "Default Value"}, + { "has_default_value", (getter)HasDefaultValue}, + { "is_extension", (getter)IsExtension, NULL, "ID"}, + { "id", (getter)GetID, NULL, "ID"}, + { "_cdescriptor", (getter)GetCDescriptor, NULL, "HAACK REMOVE ME"}, + + { "message_type", (getter)GetMessageType, (setter)SetMessageType, + "Message type"}, + { "enum_type", (getter)GetEnumType, (setter)SetEnumType, "Enum type"}, + { "containing_type", (getter)GetContainingType, (setter)SetContainingType, + "Containing type"}, + { "extension_scope", (getter)GetExtensionScope, (setter)NULL, + "Extension scope"}, + { "containing_oneof", (getter)GetContainingOneof, (setter)SetContainingOneof, + "Containing oneof"}, + { "has_options", (getter)GetHasOptions, (setter)SetHasOptions, "Has Options"}, + { "_options", (getter)NULL, (setter)SetOptions, "Options"}, + { "_serialized_options", (getter)NULL, (setter)SetSerializedOptions, + "Serialized Options"}, +*/ + {NULL} +}; + +static PyMethodDef PyUpb_FieldDescriptor_Methods[] = { + /* + { "GetOptions", (PyCFunction)GetOptions, METH_NOARGS, }, + */ + {NULL} +}; + +static PyType_Slot PyUpb_FieldDescriptor_Slots[] = { + DESCRIPTOR_BASE_SLOTS, + {Py_tp_methods, PyUpb_FieldDescriptor_Methods}, + {Py_tp_getset, PyUpb_FieldDescriptor_Getters}, + {0, NULL} +}; + +static PyType_Spec PyUpb_FieldDescriptor_Spec = { + PYUPB_MODULE_NAME ".FieldDescriptor", // tp_name + sizeof(PyUpb_FieldDescriptor), // tp_basicsize + 0, // tp_itemsize + Py_TPFLAGS_DEFAULT, // tp_flags + PyUpb_FieldDescriptor_Slots, +}; + +PyObject *PyUpb_FieldDescriptor_GetOrCreateWrapper(const upb_fielddef *field, + PyObject *pool) { + PyUpb_ModuleState *state = PyUpb_ModuleState_Get(); + return PyUpb_DescriptorBase_NewInternal(state->field_descriptor_type, field, + pool); +} + +// ----------------------------------------------------------------------------- +// FileDescriptor +// ----------------------------------------------------------------------------- +// +static PyObject *PyUpb_FileDescriptor_GetName(PyUpb_DescriptorBase *self, + void *closure) { + return PyUnicode_FromString(upb_filedef_name(self->def)); +} + +static PyGetSetDef PyUpb_FileDescriptor_Getters[] = { + /* + { "pool", (getter)GetPool, NULL, "pool"}, + */ + { "name", (getter)PyUpb_FileDescriptor_GetName, NULL, "name"}, + /* + { "package", (getter)GetPackage, NULL, "package"}, + { "serialized_pb", (getter)GetSerializedPb}, + { "message_types_by_name", PyUpb_FileDescriptor_GetMessageTypesByName, NULL, + "Messages by name"}, + { "enum_types_by_name", PyUpb_FileDescriptor_GetEnumTypesByName, NULL, + "Enums by name"}, + { "extensions_by_name", (getter)GetExtensionsByName, NULL, + "Extensions by name"}, + { "services_by_name", (getter)GetServicesByName, NULL, "Services by name"}, + { "dependencies", (getter)GetDependencies, NULL, "Dependencies"}, + { "public_dependencies", (getter)GetPublicDependencies, NULL, "Dependencies"}, + + { "has_options", (getter)GetHasOptions, (setter)SetHasOptions, "Has Options"}, + { "_options", (getter)NULL, (setter)SetOptions, "Options"}, + { "_serialized_options", (getter)NULL, (setter)SetSerializedOptions, + "Serialized Options"}, + { "syntax", (getter)GetSyntax, (setter)NULL, "Syntax"}, +*/ + {NULL} +}; + +static PyMethodDef PyUpb_FileDescriptor_Methods[] = { +/* + { "GetOptions", (PyCFunction)GetOptions, METH_NOARGS, }, + { "CopyToProto", (PyCFunction)CopyToProto, METH_O, }, +*/ + {NULL} +}; + +static PyType_Slot PyUpb_FileDescriptor_Slots[] = { + DESCRIPTOR_BASE_SLOTS, + {Py_tp_methods, PyUpb_FileDescriptor_Methods}, + {Py_tp_getset, PyUpb_FileDescriptor_Getters}, + {0, NULL} +}; + +static PyType_Spec PyUpb_FileDescriptor_Spec = { + PYUPB_MODULE_NAME ".FileDescriptor", // tp_name + sizeof(PyUpb_DescriptorBase), // tp_basicsize + 0, // tp_itemsize + Py_TPFLAGS_DEFAULT, // tp_flags + PyUpb_FileDescriptor_Slots, +}; + +PyObject *PyUpb_FileDescriptor_GetOrCreateWrapper(const upb_filedef *file, + PyObject *pool) { + PyUpb_ModuleState *state = PyUpb_ModuleState_Get(); + return PyUpb_DescriptorBase_NewInternal(state->file_descriptor_type, file, + pool); +} + +const upb_filedef *PyUpb_FileDescriptor_GetDef(PyObject *_self) { + PyUpb_DescriptorBase *self = (void*)_self; + return self->def; +} + +// ----------------------------------------------------------------------------- +// Top Level +// ----------------------------------------------------------------------------- + +bool PyUpb_InitDescriptor(PyObject* m) { + PyUpb_ModuleState *s = PyUpb_ModuleState_Get(); + + s->field_descriptor_type = + AddObject(m, "FieldDescriptor", &PyUpb_FieldDescriptor_Spec); + s->file_descriptor_type = + AddObject(m, "FileDescriptor", &PyUpb_FileDescriptor_Spec); + + return s->field_descriptor_type && s->file_descriptor_type; +}
diff --git a/python/descriptor.h b/python/descriptor.h new file mode 100644 index 0000000..4c30335 --- /dev/null +++ b/python/descriptor.h
@@ -0,0 +1,46 @@ +/* + * Copyright (c) 2009-2021, Google LLC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Google LLC nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PYPB_DESCRIPTOR_H__ +#define PYPB_DESCRIPTOR_H__ + +#include <stdbool.h> + +#include "upb/def.h" + +#include "protobuf.h" + +PyObject *PyUpb_FieldDescriptor_GetOrCreateWrapper(const upb_fielddef *field, + PyObject *pool); +PyObject *PyUpb_FileDescriptor_GetOrCreateWrapper(const upb_filedef *file, + PyObject *pool); + +const upb_filedef *PyUpb_FileDescriptor_GetDef(PyObject *file); + +bool PyUpb_InitDescriptor(PyObject* m); + +#endif // PYPB_DESCRIPTOR_H__
diff --git a/python/descriptor_pool.c b/python/descriptor_pool.c new file mode 100644 index 0000000..8dbae53 --- /dev/null +++ b/python/descriptor_pool.c
@@ -0,0 +1,215 @@ +/* + * Copyright (c) 2009-2021, Google LLC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Google LLC nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "python/descriptor_pool.h" + +#include "python/descriptor.h" +#include "python/protobuf.h" +#include "upb/def.h" + +// ----------------------------------------------------------------------------- +// DescriptorPool +// ----------------------------------------------------------------------------- + +typedef struct { + PyObject_HEAD + upb_symtab* symtab; + PyObject* db; +} PyUpb_DescriptorPool; + +static PyObject* PyUpb_DescriptorPool_DoCreate(PyTypeObject* type, PyObject* db) { + PyUpb_DescriptorPool* pool = PyObject_New(PyUpb_DescriptorPool, type); + pool->symtab = upb_symtab_new(); + pool->db = db; + return &pool->ob_base; +} + +static void PyUpb_DescriptorPool_Dealloc(PyUpb_DescriptorPool *self) { + upb_symtab_free(self->symtab); + Py_CLEAR(self->db); + PyObject_Del(self); +} + +/* + * DescriptorPool.__new__() + * + * Implements: + * DescriptorPool(descriptor_db=None) + */ +static PyObject* PyUpb_DescriptorPool_New(PyTypeObject* type, PyObject* args, + PyObject* kwargs) { + char* kwlist[] = {"descriptor_db", 0}; + PyObject* db = NULL; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O", kwlist, &db)) { + return NULL; + } + + return PyUpb_DescriptorPool_DoCreate(type, db); +} + +/* + * PyUpb_DescriptorPool_AddSerializedFile() + * + * Implements: + * DescriptorPool.AddSerializedFile(self, serialized_file_descriptor) + * + * Adds the given serialized FileDescriptorProto to the pool. + */ +static PyObject* PyUpb_DescriptorPool_AddSerializedFile( + PyObject* _self, PyObject* serialized_pb) { + PyUpb_DescriptorPool* self = (PyUpb_DescriptorPool*)_self; + char* buf; + Py_ssize_t size; + upb_arena* arena = upb_arena_new(); + PyObject* result = NULL; + + if (self->db) { + PyErr_SetString( + PyExc_ValueError, + "Cannot call Add on a DescriptorPool that uses a DescriptorDatabase. " + "Add your file to the underlying database."); + return NULL; + } + + if (PyBytes_AsStringAndSize(serialized_pb, &buf, &size) < 0) { + goto done; + } + + google_protobuf_FileDescriptorProto* proto = + google_protobuf_FileDescriptorProto_parse(buf, size, arena); + if (!proto) { + PyErr_SetString(PyExc_TypeError, "Couldn't parse file content!"); + return NULL; + } + + upb_status status; + upb_status_clear(&status); + + const upb_filedef* filedef = upb_symtab_addfile(self->symtab, proto, &status); + if (!filedef) { + PyErr_Format(PyExc_TypeError, + "Couldn't build proto file into descriptor pool: %s", + upb_status_errmsg(&status)); + return NULL; + } + + result = PyUpb_FileDescriptor_GetOrCreateWrapper(filedef, _self); + +done: + upb_arena_free(arena); + return result; +} + +/* + * PyUpb_DescriptorPool_FindExtensionByName() + * + * Implements: + * DescriptorPool.FindExtensionByName(self, name) + */ +static PyObject* PyUpb_DescriptorPool_FindExtensionByName(PyObject* _self, + PyObject* arg) { + PyUpb_DescriptorPool* self = (PyUpb_DescriptorPool*)_self; + + const char* name = PyUpb_GetStrData(arg); + if (!name) { + return NULL; + } + + const upb_fielddef* field = upb_symtab_lookupext(self->symtab, name); + if (field == NULL) { + return PyErr_Format(PyExc_KeyError, "Couldn't find extension %.200s", name); + } + + return PyUpb_FieldDescriptor_GetOrCreateWrapper(field, _self); +} + +static PyMethodDef PyUpb_DescriptorPool_Methods[] = { + /* + TODO: implement remaining methods. + { "Add", Add, METH_O, + "Adds the FileDescriptorProto and its types to this pool." }, + */ + {"AddSerializedFile", PyUpb_DescriptorPool_AddSerializedFile, METH_O, + "Adds a serialized FileDescriptorProto to this pool."}, + /* + { "FindFileByName", FindFileByName, METH_O, + "Searches for a file descriptor by its .proto name." }, + { "FindMessageTypeByName", FindMessageByName, METH_O, + "Searches for a message descriptor by full name." }, + { "FindFieldByName", FindFieldByNameMethod, METH_O, + "Searches for a field descriptor by full name." }, + */ + {"FindExtensionByName", PyUpb_DescriptorPool_FindExtensionByName, METH_O, + "Searches for extension descriptor by full name."}, + /* + { "FindEnumTypeByName", FindEnumTypeByNameMethod, METH_O, + "Searches for enum type descriptor by full name." }, + { "FindOneofByName", FindOneofByNameMethod, METH_O, + "Searches for oneof descriptor by full name." }, + { "FindServiceByName", FindServiceByName, METH_O, + "Searches for service descriptor by full name." }, + { "FindMethodByName", FindMethodByName, METH_O, + "Searches for method descriptor by full name." }, + { "FindFileContainingSymbol", FindFileContainingSymbol, METH_O, + "Gets the FileDescriptor containing the specified symbol." }, + { "FindExtensionByNumber", FindExtensionByNumber, METH_VARARGS, + "Gets the extension descriptor for the given number." }, + { "FindAllExtensions", FindAllExtensions, METH_O, + "Gets all known extensions of the given message descriptor." }, + */ + {NULL}}; + +static PyType_Slot PyUpb_DescriptorPool_Slots[] = { + {Py_tp_new, PyUpb_DescriptorPool_New}, + {Py_tp_dealloc, PyUpb_DescriptorPool_Dealloc}, + {Py_tp_methods, PyUpb_DescriptorPool_Methods}, + {0, NULL}}; + +static PyType_Spec PyUpb_DescriptorPool_Spec = { + PYUPB_MODULE_NAME ".DescriptorPool", // tp_name + sizeof(PyUpb_DescriptorPool), // tp_basicsize + 0, // tp_itemsize + Py_TPFLAGS_DEFAULT, // tp_flags + PyUpb_DescriptorPool_Slots, +}; + +// ----------------------------------------------------------------------------- +// Top Level +// ----------------------------------------------------------------------------- + +bool PyUpb_InitDescriptorPool(PyObject* m) { + PyTypeObject* descriptor_pool_type = + AddObject(m, "DescriptorPool", &PyUpb_DescriptorPool_Spec); + + if (!descriptor_pool_type) return false; + + PyObject* default_pool = + PyUpb_DescriptorPool_DoCreate(descriptor_pool_type, NULL); + return default_pool && + PyModule_AddObject(m, "default_pool", default_pool) == 0; +}
diff --git a/python/descriptor_pool.h b/python/descriptor_pool.h new file mode 100644 index 0000000..70e24d5 --- /dev/null +++ b/python/descriptor_pool.h
@@ -0,0 +1,37 @@ +/* + * Copyright (c) 2009-2021, Google LLC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Google LLC nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PYUPB_DESCRIPTOR_POOL_H__ +#define PYUPB_DESCRIPTOR_POOL_H__ + +#include <stdbool.h> + +#include "protobuf.h" + +bool PyUpb_InitDescriptorPool(PyObject* m); + +#endif // PYUPB_DESCRIPTOR_POOL_H__
diff --git a/python/protobuf.c b/python/protobuf.c new file mode 100644 index 0000000..62398cf --- /dev/null +++ b/python/protobuf.c
@@ -0,0 +1,123 @@ +/* + * Copyright (c) 2009-2021, Google LLC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Google LLC nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "protobuf.h" + +#include "descriptor_pool.h" + +static void PyUpb_ModuleDealloc(void *_s) { + PyUpb_ModuleState *s = _s; + upb_arena_free(s->obj_cache_arena); +} + +static struct PyModuleDef module_def = {PyModuleDef_HEAD_INIT, + PYUPB_MODULE_NAME, + "Protobuf Module", + sizeof(PyUpb_ModuleState), + NULL, // m_methods + NULL, // m_slots + NULL, // m_traverse + NULL, // m_clear + PyUpb_ModuleDealloc}; + +// ----------------------------------------------------------------------------- +// ModuleState +// ----------------------------------------------------------------------------- + +PyUpb_ModuleState *PyUpb_ModuleState_Get() { + PyObject *module = PyState_FindModule(&module_def); + return PyModule_GetState(module); +} + +// ----------------------------------------------------------------------------- +// ObjectCache +// ----------------------------------------------------------------------------- + +void PyUpb_ObjCache_Add(const void *key, PyObject *py_obj) { + PyUpb_ModuleState *s = PyUpb_ModuleState_Get(); + upb_inttable_insert(&s->obj_cache, (uintptr_t)key, upb_value_ptr(py_obj), + s->obj_cache_arena); +} + +void PyUpb_ObjCache_Delete(const void *key) { + PyUpb_ModuleState *s = PyUpb_ModuleState_Get(); + upb_value val; + upb_inttable_remove(&s->obj_cache, (uintptr_t)key, &val); + assert(upb_value_getptr(val)); +} + +PyObject *PyUpb_ObjCache_Get(const void *key) { + PyUpb_ModuleState *s = PyUpb_ModuleState_Get(); + upb_value val; + if (upb_inttable_lookup(&s->obj_cache, (uintptr_t)key, &val)) { + PyObject *ret = upb_value_getptr(val); + Py_INCREF(ret); + return ret; + } else { + return NULL; + } +} + +// ----------------------------------------------------------------------------- +// Utilities +// ----------------------------------------------------------------------------- + +PyTypeObject *AddObject(PyObject *m, const char *name, PyType_Spec *spec) { + PyObject *type = PyType_FromSpec(spec); + return type && PyModule_AddObject(m, name, type) == 0 ? (PyTypeObject *)type + : NULL; +} + +const char *PyUpb_GetStrData(PyObject *obj) { + if (PyUnicode_Check(obj)) { + return PyUnicode_AsUTF8AndSize(obj, NULL); + } else if (PyBytes_Check(obj)) { + return PyBytes_AsString(obj); + } else { + return NULL; + } +} + +// ----------------------------------------------------------------------------- +// Module Entry Point +// ----------------------------------------------------------------------------- + +PyMODINIT_FUNC PyInit__message(void) { + PyObject *m = PyModule_Create(&module_def); + PyState_AddModule(m, &module_def); + PyUpb_ModuleState *state = PyUpb_ModuleState_Get(); + + state->obj_cache_arena = upb_arena_new(); + upb_inttable_init(&state->obj_cache, state->obj_cache_arena); + + if (!PyUpb_InitDescriptorPool(m)) { + Py_DECREF(m); + return NULL; + } + + return m; +}
diff --git a/python/protobuf.h b/python/protobuf.h new file mode 100644 index 0000000..cf21b11 --- /dev/null +++ b/python/protobuf.h
@@ -0,0 +1,94 @@ +/* + * Copyright (c) 2009-2021, Google LLC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Google LLC nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PYUPB_PROTOBUF_H__ +#define PYUPB_PROTOBUF_H__ + +#include <stdbool.h> + +#define Py_LIMITED_API 0x03060000 +#include <Python.h> + +// This function was not officially added to the limited API until Python 3.10. +// But in practice it has been stable since Python 3.1. See: +// https://bugs.python.org/issue41784 +PyAPI_FUNC(const char *) PyUnicode_AsUTF8AndSize( + PyObject *unicode, + Py_ssize_t *size); + +#include "upb/table_internal.h" + +#define PYUPB_MODULE_NAME "google.protobuf.pyext._message" + +// ----------------------------------------------------------------------------- +// ModuleState +// ----------------------------------------------------------------------------- + +// We store all "global" state in this struct instead of using (C) global +// variables. This makes this extension compatible with sub-interpreters. + +typedef struct { + // From descriptor.c + PyTypeObject *field_descriptor_type; + PyTypeObject *file_descriptor_type; + + // From descriptor_pool.c + PyTypeObject *descriptor_pool_type; + + // From protobuf.c + upb_arena *obj_cache_arena; + upb_inttable obj_cache; +} PyUpb_ModuleState; + +// Returns the global state object from the current interpreter. The current +// interpreter is looked up from thread-local state. +PyUpb_ModuleState *PyUpb_ModuleState_Get(void); + +// ----------------------------------------------------------------------------- +// ObjectCache +// ----------------------------------------------------------------------------- + +// The ObjectCache is a weak map that maps C pointers to the corresponding +// Python wrapper object. We want a consistent Python wrapper object for each +// C object, both to save memory and to provide object stability (ie. x is x). +// +// Each wrapped object should add itself to the map when it is constructed and +// remove itself from the map when it is destroyed. The map is weak so it does +// not take references to the cached objects. + +void PyUpb_ObjCache_Add(const void *key, PyObject *py_obj); +void PyUpb_ObjCache_Delete(const void *key); +PyObject *PyUpb_ObjCache_Get(const void *key); // returns NULL if not present. + +// ----------------------------------------------------------------------------- +// Utilities +// ----------------------------------------------------------------------------- + +PyTypeObject *AddObject(PyObject *m, const char* name, PyType_Spec* spec); +const char *PyUpb_GetStrData(PyObject *obj); + +#endif // PYUPB_PROTOBUF_H__
diff --git a/python/version_script.lds b/python/version_script.lds new file mode 100644 index 0000000..7cb8300 --- /dev/null +++ b/python/version_script.lds
@@ -0,0 +1,6 @@ +message { + global: + PyInit__message; + local: + *; +};