mirror of https://github.com/grpc/grpc.git
The C based gRPC (C++, Python, Ruby, Objective-C, PHP, C#)
https://grpc.io/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1858 lines
66 KiB
1858 lines
66 KiB
/* |
|
* 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/message.h" |
|
|
|
#include "python/convert.h" |
|
#include "python/descriptor.h" |
|
#include "python/extension_dict.h" |
|
#include "python/map.h" |
|
#include "python/repeated.h" |
|
#include "upb/def.h" |
|
#include "upb/reflection.h" |
|
#include "upb/text_encode.h" |
|
#include "upb/util/required_fields.h" |
|
|
|
static const upb_MessageDef* PyUpb_MessageMeta_GetMsgdef(PyObject* cls); |
|
static PyObject* PyUpb_MessageMeta_GetAttr(PyObject* self, PyObject* name); |
|
|
|
// ----------------------------------------------------------------------------- |
|
// CPythonBits |
|
// ----------------------------------------------------------------------------- |
|
|
|
// This struct contains a few things that are not exposed directly through the |
|
// limited API, but that we can get at in somewhat more roundabout ways. The |
|
// roundabout ways are slower, so we cache the values here. |
|
// |
|
// These values are valid to cache in a global, even across sub-interpreters, |
|
// because they are not pointers to interpreter state. They are process |
|
// globals that will be the same for any interpreter in this process. |
|
typedef struct { |
|
// For each member, we note the equivalent expression that we could use in the |
|
// full (non-limited) API. |
|
newfunc type_new; // PyTypeObject.tp_new |
|
destructor type_dealloc; // PyTypeObject.tp_dealloc |
|
getattrofunc type_getattro; // PyTypeObject.tp_getattro |
|
setattrofunc type_setattro; // PyTypeObject.tp_setattro |
|
size_t type_basicsize; // sizeof(PyHeapTypeObject) |
|
|
|
// While we can refer to PY_VERSION_HEX in the limited API, this will give us |
|
// the version of Python we were compiled against, which may be different |
|
// than the version we are dynamically linked against. Here we want the |
|
// version that is actually running in this process. |
|
long python_version_hex; // PY_VERSION_HEX |
|
} PyUpb_CPythonBits; |
|
|
|
// A global containing the values for this process. |
|
PyUpb_CPythonBits cpython_bits; |
|
|
|
destructor upb_Pre310_PyType_GetDeallocSlot(PyTypeObject* type_subclass) { |
|
// This is a bit desperate. We need type_dealloc(), but PyType_GetSlot(type, |
|
// Py_tp_dealloc) will return subtype_dealloc(). There appears to be no way |
|
// whatsoever to fetch type_dealloc() through the limited API until Python |
|
// 3.10. |
|
// |
|
// To work around this so we attempt to find it by looking for the offset of |
|
// tp_dealloc in PyTypeObject, then memcpy() it directly. This should always |
|
// work in practice. |
|
// |
|
// Starting with Python 3.10 on you can call PyType_GetSlot() on non-heap |
|
// types. We will be able to replace all this hack with just: |
|
// |
|
// PyType_GetSlot(&PyType_Type, Py_tp_dealloc) |
|
// |
|
destructor subtype_dealloc = PyType_GetSlot(type_subclass, Py_tp_dealloc); |
|
for (size_t i = 0; i < 2000; i += sizeof(uintptr_t)) { |
|
destructor maybe_subtype_dealloc; |
|
memcpy(&maybe_subtype_dealloc, (char*)type_subclass + i, |
|
sizeof(destructor)); |
|
if (maybe_subtype_dealloc == subtype_dealloc) { |
|
destructor type_dealloc; |
|
memcpy(&type_dealloc, (char*)&PyType_Type + i, sizeof(destructor)); |
|
return type_dealloc; |
|
} |
|
} |
|
assert(false); |
|
return NULL; |
|
} |
|
|
|
static bool PyUpb_CPythonBits_Init(PyUpb_CPythonBits* bits) { |
|
PyObject* bases = NULL; |
|
PyTypeObject* type = NULL; |
|
PyObject* size = NULL; |
|
PyObject* sys = NULL; |
|
PyObject* hex_version = NULL; |
|
bool ret = false; |
|
|
|
// PyType_GetSlot() only works on heap types, so we cannot use it on |
|
// &PyType_Type directly. Instead we create our own (temporary) type derived |
|
// from PyType_Type: this will inherit all of the slots from PyType_Type, but |
|
// as a heap type it can be queried with PyType_GetSlot(). |
|
static PyType_Slot dummy_slots[] = {{0, NULL}}; |
|
|
|
static PyType_Spec dummy_spec = { |
|
"module.DummyClass", // tp_name |
|
0, // To be filled in by size of base // tp_basicsize |
|
0, // tp_itemsize |
|
Py_TPFLAGS_DEFAULT, // tp_flags |
|
dummy_slots, |
|
}; |
|
|
|
bases = Py_BuildValue("(O)", &PyType_Type); |
|
if (!bases) goto err; |
|
type = (PyTypeObject*)PyType_FromSpecWithBases(&dummy_spec, bases); |
|
if (!type) goto err; |
|
|
|
bits->type_new = PyType_GetSlot(type, Py_tp_new); |
|
bits->type_dealloc = upb_Pre310_PyType_GetDeallocSlot(type); |
|
bits->type_getattro = PyType_GetSlot(type, Py_tp_getattro); |
|
bits->type_setattro = PyType_GetSlot(type, Py_tp_setattro); |
|
|
|
size = PyObject_GetAttrString((PyObject*)&PyType_Type, "__basicsize__"); |
|
if (!size) goto err; |
|
bits->type_basicsize = PyLong_AsLong(size); |
|
if (bits->type_basicsize == -1) goto err; |
|
|
|
assert(bits->type_new); |
|
assert(bits->type_dealloc); |
|
assert(bits->type_getattro); |
|
assert(bits->type_setattro); |
|
|
|
#ifndef Py_LIMITED_API |
|
assert(bits->type_new == PyType_Type.tp_new); |
|
assert(bits->type_dealloc == PyType_Type.tp_dealloc); |
|
assert(bits->type_getattro == PyType_Type.tp_getattro); |
|
assert(bits->type_setattro == PyType_Type.tp_setattro); |
|
assert(bits->type_basicsize == sizeof(PyHeapTypeObject)); |
|
#endif |
|
|
|
sys = PyImport_ImportModule("sys"); |
|
hex_version = PyObject_GetAttrString(sys, "hexversion"); |
|
bits->python_version_hex = PyLong_AsLong(hex_version); |
|
ret = true; |
|
|
|
err: |
|
Py_XDECREF(bases); |
|
Py_XDECREF(type); |
|
Py_XDECREF(size); |
|
Py_XDECREF(sys); |
|
Py_XDECREF(hex_version); |
|
return ret; |
|
} |
|
|
|
// ----------------------------------------------------------------------------- |
|
// CMessage |
|
// ----------------------------------------------------------------------------- |
|
|
|
// The main message object. The type of the object (PyUpb_CMessage.ob_type) |
|
// will be an instance of the PyUpb_MessageMeta type (defined below). So the |
|
// chain is: |
|
// FooMessage = MessageMeta(...) |
|
// foo = FooMessage() |
|
// |
|
// Which becomes: |
|
// Object C Struct Type Python type (ob_type) |
|
// ----------------- ----------------- --------------------- |
|
// foo PyUpb_CMessage FooMessage |
|
// FooMessage PyUpb_MessageMeta message_meta_type |
|
// message_meta_type PyTypeObject 'type' in Python |
|
// |
|
// A message object can be in one of two states: present or non-present. When |
|
// a message is non-present, it stores a reference to its parent, and a write |
|
// to any attribute will trigger the message to become present in its parent. |
|
// The parent may also be non-present, in which case a mutation will trigger a |
|
// chain reaction. |
|
typedef struct PyUpb_CMessage { |
|
PyObject_HEAD; |
|
PyObject* arena; |
|
uintptr_t def; // Tagged, low bit 1 == upb_FieldDef*, else upb_MessageDef* |
|
union { |
|
// when def is msgdef, the data for this msg. |
|
upb_Message* msg; |
|
// when def is fielddef, owning pointer to parent |
|
struct PyUpb_CMessage* parent; |
|
} ptr; |
|
PyObject* ext_dict; // Weak pointer to extension dict, if any. |
|
// name->obj dict for non-present msg/map/repeated, NULL if none. |
|
PyUpb_WeakMap* unset_subobj_map; |
|
int version; |
|
} PyUpb_CMessage; |
|
|
|
static PyObject* PyUpb_CMessage_GetAttr(PyObject* _self, PyObject* attr); |
|
|
|
bool PyUpb_CMessage_IsStub(PyUpb_CMessage* msg) { return msg->def & 1; } |
|
|
|
const upb_FieldDef* PyUpb_CMessage_GetFieldDef(PyUpb_CMessage* msg) { |
|
assert(PyUpb_CMessage_IsStub(msg)); |
|
return (void*)(msg->def & ~(uintptr_t)1); |
|
} |
|
|
|
static const upb_MessageDef* _PyUpb_CMessage_GetMsgdef(PyUpb_CMessage* msg) { |
|
return PyUpb_CMessage_IsStub(msg) |
|
? upb_FieldDef_MessageSubDef(PyUpb_CMessage_GetFieldDef(msg)) |
|
: (void*)msg->def; |
|
} |
|
|
|
const upb_MessageDef* PyUpb_CMessage_GetMsgdef(PyObject* self) { |
|
return _PyUpb_CMessage_GetMsgdef((PyUpb_CMessage*)self); |
|
} |
|
|
|
static upb_Message* PyUpb_CMessage_GetMsg(PyUpb_CMessage* self) { |
|
assert(!PyUpb_CMessage_IsStub(self)); |
|
return self->ptr.msg; |
|
} |
|
|
|
bool PyUpb_CMessage_TryCheck(PyObject* self) { |
|
PyUpb_ModuleState* state = PyUpb_ModuleState_Get(); |
|
PyObject* type = (PyObject*)Py_TYPE(self); |
|
return Py_TYPE(type) == state->message_meta_type; |
|
} |
|
|
|
bool PyUpb_CMessage_Verify(PyObject* self) { |
|
if (!PyUpb_CMessage_TryCheck(self)) { |
|
PyErr_Format(PyExc_TypeError, "Expected a message object, but got %R.", |
|
self); |
|
return false; |
|
} |
|
return true; |
|
} |
|
|
|
// If the message is reified, returns it. Otherwise, returns NULL. |
|
// If NULL is returned, the object is empty and has no underlying data. |
|
upb_Message* PyUpb_CMessage_GetIfReified(PyObject* _self) { |
|
PyUpb_CMessage* self = (void*)_self; |
|
return PyUpb_CMessage_IsStub(self) ? NULL : self->ptr.msg; |
|
} |
|
|
|
static PyObject* PyUpb_CMessage_New(PyObject* cls, PyObject* unused_args, |
|
PyObject* unused_kwargs) { |
|
const upb_MessageDef* msgdef = PyUpb_MessageMeta_GetMsgdef(cls); |
|
PyUpb_CMessage* msg = (void*)PyType_GenericAlloc((PyTypeObject*)cls, 0); |
|
msg->def = (uintptr_t)msgdef; |
|
msg->arena = PyUpb_Arena_New(); |
|
msg->ptr.msg = upb_Message_New(msgdef, PyUpb_Arena_Get(msg->arena)); |
|
msg->unset_subobj_map = NULL; |
|
msg->ext_dict = NULL; |
|
msg->version = 0; |
|
|
|
PyObject* ret = &msg->ob_base; |
|
PyUpb_ObjCache_Add(msg->ptr.msg, ret); |
|
return ret; |
|
} |
|
|
|
/* |
|
* PyUpb_CMessage_LookupName() |
|
* |
|
* Tries to find a field or oneof named `py_name` in the message object `self`. |
|
* The user must pass `f` and/or `o` to indicate whether a field or a oneof name |
|
* is expected. If the name is found and it has an expected type, the function |
|
* sets `*f` or `*o` respectively and returns true. Otherwise returns false |
|
* and sets an exception of type `exc_type` if provided. |
|
*/ |
|
static bool PyUpb_CMessage_LookupName(PyUpb_CMessage* self, PyObject* py_name, |
|
const upb_FieldDef** f, |
|
const upb_OneofDef** o, |
|
PyObject* exc_type) { |
|
assert(f || o); |
|
Py_ssize_t size; |
|
const char* name = NULL; |
|
if (PyUnicode_Check(py_name)) { |
|
name = PyUnicode_AsUTF8AndSize(py_name, &size); |
|
} else if (PyBytes_Check(py_name)) { |
|
PyBytes_AsStringAndSize(py_name, (char**)&name, &size); |
|
} |
|
if (!name) { |
|
PyErr_Format(exc_type, |
|
"Expected a field name, but got non-string argument %S.", |
|
py_name); |
|
return false; |
|
} |
|
const upb_MessageDef* msgdef = _PyUpb_CMessage_GetMsgdef(self); |
|
|
|
if (!upb_MessageDef_FindByNameWithSize(msgdef, name, size, f, o)) { |
|
if (exc_type) { |
|
PyErr_Format(exc_type, "Protocol message %s has no \"%s\" field.", |
|
upb_MessageDef_Name(msgdef), name); |
|
} |
|
return false; |
|
} |
|
|
|
if (!o && !*f) { |
|
if (exc_type) { |
|
PyErr_Format(exc_type, "Expected a field name, but got oneof name %s.", |
|
name); |
|
} |
|
return false; |
|
} |
|
|
|
if (!f && !*o) { |
|
if (exc_type) { |
|
PyErr_Format(exc_type, "Expected a oneof name, but got field name %s.", |
|
name); |
|
} |
|
return false; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
static bool PyUpb_CMessage_InitMessageMapEntry(PyObject* dst, PyObject* src) { |
|
if (!src || !dst) return false; |
|
|
|
// TODO(haberman): Currently we are doing Clear()+MergeFrom(). Replace with |
|
// CopyFrom() once that is implemented. |
|
PyObject* ok = PyObject_CallMethod(dst, "Clear", NULL); |
|
if (!ok) return false; |
|
Py_DECREF(ok); |
|
ok = PyObject_CallMethod(dst, "MergeFrom", "O", src); |
|
if (!ok) return false; |
|
Py_DECREF(ok); |
|
|
|
return true; |
|
} |
|
|
|
int PyUpb_CMessage_InitMapAttributes(PyObject* map, PyObject* value, |
|
const upb_FieldDef* f) { |
|
const upb_MessageDef* entry_m = upb_FieldDef_MessageSubDef(f); |
|
const upb_FieldDef* val_f = upb_MessageDef_Field(entry_m, 1); |
|
PyObject* it = NULL; |
|
PyObject* tmp = NULL; |
|
int ret = -1; |
|
if (upb_FieldDef_IsSubMessage(val_f)) { |
|
it = PyObject_GetIter(value); |
|
if (it == NULL) { |
|
PyErr_Format(PyExc_TypeError, "Argument for field %s is not iterable", |
|
upb_FieldDef_FullName(f)); |
|
goto err; |
|
} |
|
PyObject* e; |
|
while ((e = PyIter_Next(it)) != NULL) { |
|
PyObject* src = PyObject_GetItem(value, e); |
|
PyObject* dst = PyObject_GetItem(map, e); |
|
Py_DECREF(e); |
|
bool ok = PyUpb_CMessage_InitMessageMapEntry(dst, src); |
|
Py_XDECREF(src); |
|
Py_XDECREF(dst); |
|
if (!ok) goto err; |
|
} |
|
} else { |
|
tmp = PyObject_CallMethod(map, "update", "O", value); |
|
if (!tmp) goto err; |
|
} |
|
ret = 0; |
|
|
|
err: |
|
Py_XDECREF(it); |
|
Py_XDECREF(tmp); |
|
return ret; |
|
} |
|
|
|
void PyUpb_CMessage_EnsureReified(PyUpb_CMessage* self); |
|
|
|
static bool PyUpb_CMessage_InitMapAttribute(PyObject* _self, PyObject* name, |
|
const upb_FieldDef* f, |
|
PyObject* value) { |
|
PyObject* map = PyUpb_CMessage_GetAttr(_self, name); |
|
int ok = PyUpb_CMessage_InitMapAttributes(map, value, f); |
|
Py_DECREF(map); |
|
return ok >= 0; |
|
} |
|
|
|
static bool PyUpb_CMessage_InitRepeatedAttribute(PyObject* _self, |
|
PyObject* name, |
|
PyObject* value) { |
|
bool ok = false; |
|
PyObject* tmp = NULL; |
|
PyObject* repeated = PyUpb_CMessage_GetAttr(_self, name); |
|
if (!repeated) goto err; |
|
tmp = PyUpb_RepeatedContainer_Extend(repeated, value); |
|
if (!tmp) goto err; |
|
ok = true; |
|
|
|
err: |
|
Py_XDECREF(repeated); |
|
Py_XDECREF(tmp); |
|
return ok; |
|
} |
|
|
|
static bool PyUpb_CMessage_InitMessageAttribute(PyObject* _self, PyObject* name, |
|
PyObject* value) { |
|
PyObject* submsg = PyUpb_CMessage_GetAttr(_self, name); |
|
if (!submsg) return -1; |
|
assert(!PyErr_Occurred()); |
|
bool ok; |
|
if (PyUpb_CMessage_TryCheck(value)) { |
|
PyObject* tmp = PyUpb_CMessage_MergeFrom(submsg, value); |
|
ok = tmp != NULL; |
|
Py_DECREF(tmp); |
|
} else if (PyDict_Check(value)) { |
|
assert(!PyErr_Occurred()); |
|
ok = PyUpb_CMessage_InitAttributes(submsg, NULL, value) >= 0; |
|
} else { |
|
const upb_MessageDef* m = PyUpb_CMessage_GetMsgdef(_self); |
|
PyErr_Format(PyExc_TypeError, "Message must be initialized with a dict: %s", |
|
upb_MessageDef_FullName(m)); |
|
ok = false; |
|
} |
|
Py_DECREF(submsg); |
|
return ok; |
|
} |
|
|
|
static bool PyUpb_CMessage_InitScalarAttribute(upb_Message* msg, |
|
const upb_FieldDef* f, |
|
PyObject* value, |
|
upb_Arena* arena) { |
|
upb_MessageValue msgval; |
|
assert(!PyErr_Occurred()); |
|
if (!PyUpb_PyToUpb(value, f, &msgval, arena)) return false; |
|
upb_Message_Set(msg, f, msgval, arena); |
|
return true; |
|
} |
|
|
|
int PyUpb_CMessage_InitAttributes(PyObject* _self, PyObject* args, |
|
PyObject* kwargs) { |
|
assert(!PyErr_Occurred()); |
|
|
|
if (args != NULL && PyTuple_Size(args) != 0) { |
|
PyErr_SetString(PyExc_TypeError, "No positional arguments allowed"); |
|
return -1; |
|
} |
|
|
|
if (kwargs == NULL) return 0; |
|
|
|
PyUpb_CMessage* self = (void*)_self; |
|
Py_ssize_t pos = 0; |
|
PyObject* name; |
|
PyObject* value; |
|
PyUpb_CMessage_EnsureReified(self); |
|
upb_Message* msg = PyUpb_CMessage_GetMsg(self); |
|
upb_Arena* arena = PyUpb_Arena_Get(self->arena); |
|
|
|
while (PyDict_Next(kwargs, &pos, &name, &value)) { |
|
assert(!PyErr_Occurred()); |
|
const upb_FieldDef* f; |
|
assert(!PyErr_Occurred()); |
|
if (!PyUpb_CMessage_LookupName(self, name, &f, NULL, PyExc_ValueError)) { |
|
return -1; |
|
} |
|
|
|
if (value == Py_None) continue; // Ignored. |
|
|
|
assert(!PyErr_Occurred()); |
|
|
|
if (upb_FieldDef_IsMap(f)) { |
|
if (!PyUpb_CMessage_InitMapAttribute(_self, name, f, value)) return -1; |
|
} else if (upb_FieldDef_IsRepeated(f)) { |
|
if (!PyUpb_CMessage_InitRepeatedAttribute(_self, name, value)) return -1; |
|
} else if (upb_FieldDef_IsSubMessage(f)) { |
|
if (!PyUpb_CMessage_InitMessageAttribute(_self, name, value)) return -1; |
|
} else { |
|
if (!PyUpb_CMessage_InitScalarAttribute(msg, f, value, arena)) return -1; |
|
} |
|
if (PyErr_Occurred()) return -1; |
|
} |
|
|
|
if (PyErr_Occurred()) return -1; |
|
return 0; |
|
} |
|
|
|
static int PyUpb_CMessage_Init(PyObject* _self, PyObject* args, |
|
PyObject* kwargs) { |
|
if (args != NULL && PyTuple_Size(args) != 0) { |
|
PyErr_SetString(PyExc_TypeError, "No positional arguments allowed"); |
|
return -1; |
|
} |
|
|
|
return PyUpb_CMessage_InitAttributes(_self, args, kwargs); |
|
} |
|
|
|
static PyObject* PyUpb_CMessage_NewStub(PyObject* parent, const upb_FieldDef* f, |
|
PyObject* arena) { |
|
const upb_MessageDef* sub_m = upb_FieldDef_MessageSubDef(f); |
|
PyObject* cls = PyUpb_Descriptor_GetClass(sub_m); |
|
|
|
PyUpb_CMessage* msg = (void*)PyType_GenericAlloc((PyTypeObject*)cls, 0); |
|
msg->def = (uintptr_t)f | 1; |
|
msg->arena = arena; |
|
msg->ptr.parent = (PyUpb_CMessage*)parent; |
|
msg->unset_subobj_map = NULL; |
|
msg->ext_dict = NULL; |
|
msg->version = 0; |
|
|
|
Py_DECREF(cls); |
|
Py_INCREF(parent); |
|
Py_INCREF(arena); |
|
return &msg->ob_base; |
|
} |
|
|
|
static bool PyUpb_CMessage_IsEqual(PyUpb_CMessage* m1, PyObject* _m2) { |
|
PyUpb_CMessage* m2 = (void*)_m2; |
|
if (m1 == m2) return true; |
|
if (!PyObject_TypeCheck(_m2, m1->ob_base.ob_type)) { |
|
return false; |
|
} |
|
const upb_MessageDef* m1_msgdef = _PyUpb_CMessage_GetMsgdef(m1); |
|
#ifndef NDEBUG |
|
const upb_MessageDef* m2_msgdef = _PyUpb_CMessage_GetMsgdef(m2); |
|
assert(m1_msgdef == m2_msgdef); |
|
#endif |
|
const upb_Message* m1_msg = PyUpb_CMessage_GetIfReified((PyObject*)m1); |
|
const upb_Message* m2_msg = PyUpb_CMessage_GetIfReified(_m2); |
|
return PyUpb_Message_IsEqual(m1_msg, m2_msg, m1_msgdef); |
|
} |
|
|
|
static const upb_FieldDef* PyUpb_CMessage_InitAsMsg(PyUpb_CMessage* m, |
|
upb_Arena* arena) { |
|
const upb_FieldDef* f = PyUpb_CMessage_GetFieldDef(m); |
|
m->ptr.msg = upb_Message_New(upb_FieldDef_MessageSubDef(f), arena); |
|
m->def = (uintptr_t)upb_FieldDef_MessageSubDef(f); |
|
PyUpb_ObjCache_Add(m->ptr.msg, &m->ob_base); |
|
return f; |
|
} |
|
|
|
static void PyUpb_CMessage_SetField(PyUpb_CMessage* parent, |
|
const upb_FieldDef* f, |
|
PyUpb_CMessage* child, upb_Arena* arena) { |
|
upb_MessageValue msgval = {.msg_val = PyUpb_CMessage_GetMsg(child)}; |
|
upb_Message_Set(PyUpb_CMessage_GetMsg(parent), f, msgval, arena); |
|
PyUpb_WeakMap_Delete(parent->unset_subobj_map, f); |
|
// Releases a ref previously owned by child->ptr.parent of our child. |
|
Py_DECREF(child); |
|
} |
|
|
|
/* |
|
* PyUpb_CMessage_EnsureReified() |
|
* |
|
* This implements the "expando" behavior of Python protos: |
|
* foo = FooProto() |
|
* |
|
* # The intermediate messages don't really exist, and won't be serialized. |
|
* x = foo.bar.bar.bar.bar.bar.baz |
|
* |
|
* # Now all the intermediate objects are created. |
|
* foo.bar.bar.bar.bar.bar.baz = 5 |
|
* |
|
* This function should be called before performing any mutation of a protobuf |
|
* object. |
|
* |
|
* Post-condition: |
|
* PyUpb_CMessage_IsStub(self) is false |
|
*/ |
|
void PyUpb_CMessage_EnsureReified(PyUpb_CMessage* self) { |
|
if (!PyUpb_CMessage_IsStub(self)) return; |
|
upb_Arena* arena = PyUpb_Arena_Get(self->arena); |
|
|
|
// This is a non-present message. We need to create a real upb_Message for |
|
// this object and every parent until we reach a present message. |
|
PyUpb_CMessage* child = self; |
|
PyUpb_CMessage* parent = self->ptr.parent; |
|
const upb_FieldDef* child_f = PyUpb_CMessage_InitAsMsg(child, arena); |
|
Py_INCREF(child); // To avoid a special-case in PyUpb_CMessage_SetField(). |
|
|
|
do { |
|
PyUpb_CMessage* next_parent = parent->ptr.parent; |
|
const upb_FieldDef* parent_f = NULL; |
|
if (PyUpb_CMessage_IsStub(parent)) { |
|
parent_f = PyUpb_CMessage_InitAsMsg(parent, arena); |
|
} |
|
PyUpb_CMessage_SetField(parent, child_f, child, arena); |
|
child = parent; |
|
child_f = parent_f; |
|
parent = next_parent; |
|
} while (child_f); |
|
|
|
// Releases ref previously owned by child->ptr.parent of our child. |
|
Py_DECREF(child); |
|
self->version++; |
|
} |
|
|
|
static void PyUpb_CMessage_SyncSubobjs(PyUpb_CMessage* self); |
|
|
|
/* |
|
* PyUpb_CMessage_Reify() |
|
* |
|
* The message equivalent of PyUpb_*Container_Reify(), this transitions |
|
* the wrapper from the unset state (owning a reference on self->ptr.parent) to |
|
* the set state (having a non-owning pointer to self->ptr.msg). |
|
*/ |
|
static void PyUpb_CMessage_Reify(PyUpb_CMessage* self, const upb_FieldDef* f, |
|
upb_Message* msg) { |
|
assert(f == PyUpb_CMessage_GetFieldDef(self)); |
|
if (!msg) { |
|
const upb_MessageDef* msgdef = PyUpb_CMessage_GetMsgdef((PyObject*)self); |
|
msg = upb_Message_New(msgdef, PyUpb_Arena_Get(self->arena)); |
|
} |
|
PyUpb_ObjCache_Add(msg, &self->ob_base); |
|
Py_DECREF(&self->ptr.parent->ob_base); |
|
self->ptr.msg = msg; // Overwrites self->ptr.parent |
|
self->def = (uintptr_t)upb_FieldDef_MessageSubDef(f); |
|
PyUpb_CMessage_SyncSubobjs(self); |
|
} |
|
|
|
/* |
|
* PyUpb_CMessage_SyncSubobjs() |
|
* |
|
* This operation must be invoked whenever the underlying upb_Message has been |
|
* mutated directly in C. This will attach any newly-present field data |
|
* to previously returned stub wrapper objects. |
|
* |
|
* For example: |
|
* foo = FooMessage() |
|
* sub = foo.submsg # Empty, unset sub-message |
|
* |
|
* # SyncSubobjs() is required to connect our existing 'sub' wrapper to the |
|
* # newly created foo.submsg data in C. |
|
* foo.MergeFrom(FooMessage(submsg={})) |
|
* |
|
* This requires that all of the new sub-objects that have appeared are owned |
|
* by `self`'s arena. |
|
*/ |
|
static void PyUpb_CMessage_SyncSubobjs(PyUpb_CMessage* self) { |
|
PyUpb_WeakMap* subobj_map = self->unset_subobj_map; |
|
if (!subobj_map) return; |
|
|
|
upb_Message* msg = PyUpb_CMessage_GetMsg(self); |
|
intptr_t iter = PYUPB_WEAKMAP_BEGIN; |
|
const void* key; |
|
PyObject* obj; |
|
|
|
// The last ref to this message could disappear during iteration. |
|
// When we call PyUpb_*Container_Reify() below, the container will drop |
|
// its ref on `self`. If that was the last ref on self, the object will be |
|
// deleted, and `subobj_map` along with it. We need it to live until we are |
|
// done iterating. |
|
Py_INCREF(&self->ob_base); |
|
|
|
while (PyUpb_WeakMap_Next(subobj_map, &key, &obj, &iter)) { |
|
const upb_FieldDef* f = key; |
|
if (upb_FieldDef_HasPresence(f) && !upb_Message_Has(msg, f)) continue; |
|
upb_MessageValue msgval = upb_Message_Get(msg, f); |
|
PyUpb_WeakMap_DeleteIter(subobj_map, &iter); |
|
if (upb_FieldDef_IsMap(f)) { |
|
if (!msgval.map_val) continue; |
|
PyUpb_MapContainer_Reify(obj, (upb_Map*)msgval.map_val); |
|
} else if (upb_FieldDef_IsRepeated(f)) { |
|
if (!msgval.array_val) continue; |
|
PyUpb_RepeatedContainer_Reify(obj, (upb_Array*)msgval.array_val); |
|
} else { |
|
PyUpb_CMessage* sub = (void*)obj; |
|
assert(self == sub->ptr.parent); |
|
PyUpb_CMessage_Reify(sub, f, (upb_Message*)msgval.msg_val); |
|
} |
|
} |
|
|
|
Py_DECREF(&self->ob_base); |
|
|
|
// TODO(haberman): present fields need to be iterated too if they can reach |
|
// a WeakMap. |
|
} |
|
|
|
static PyObject* PyUpb_CMessage_ToString(PyUpb_CMessage* self) { |
|
if (PyUpb_CMessage_IsStub(self)) { |
|
return PyUnicode_FromStringAndSize(NULL, 0); |
|
} |
|
upb_Message* msg = PyUpb_CMessage_GetMsg(self); |
|
const upb_MessageDef* msgdef = _PyUpb_CMessage_GetMsgdef(self); |
|
const upb_DefPool* symtab = upb_FileDef_Pool(upb_MessageDef_File(msgdef)); |
|
char buf[1024]; |
|
int options = UPB_TXTENC_SKIPUNKNOWN; |
|
size_t size = upb_TextEncode(msg, msgdef, symtab, options, buf, sizeof(buf)); |
|
if (size < sizeof(buf)) { |
|
return PyUnicode_FromStringAndSize(buf, size); |
|
} else { |
|
char* buf2 = malloc(size + 1); |
|
size_t size2 = upb_TextEncode(msg, msgdef, symtab, options, buf2, size + 1); |
|
assert(size == size2); |
|
PyObject* ret = PyUnicode_FromStringAndSize(buf2, size2); |
|
free(buf2); |
|
return ret; |
|
} |
|
} |
|
|
|
static PyObject* PyUpb_CMessage_RichCompare(PyObject* _self, PyObject* other, |
|
int opid) { |
|
PyUpb_CMessage* self = (void*)_self; |
|
if (opid != Py_EQ && opid != Py_NE) { |
|
Py_INCREF(Py_NotImplemented); |
|
return Py_NotImplemented; |
|
} |
|
bool ret = PyUpb_CMessage_IsEqual(self, other); |
|
if (opid == Py_NE) ret = !ret; |
|
return PyBool_FromLong(ret); |
|
} |
|
|
|
void PyUpb_CMessage_CacheDelete(PyObject* _self, const upb_FieldDef* f) { |
|
PyUpb_CMessage* self = (void*)_self; |
|
PyUpb_WeakMap_Delete(self->unset_subobj_map, f); |
|
} |
|
|
|
void PyUpb_CMessage_SetConcreteSubobj(PyObject* _self, const upb_FieldDef* f, |
|
upb_MessageValue subobj) { |
|
PyUpb_CMessage* self = (void*)_self; |
|
PyUpb_CMessage_EnsureReified(self); |
|
PyUpb_CMessage_CacheDelete(_self, f); |
|
upb_Message_Set(self->ptr.msg, f, subobj, PyUpb_Arena_Get(self->arena)); |
|
} |
|
|
|
static void PyUpb_CMessage_Dealloc(PyObject* _self) { |
|
PyUpb_CMessage* self = (void*)_self; |
|
|
|
if (PyUpb_CMessage_IsStub(self)) { |
|
PyUpb_CMessage_CacheDelete((PyObject*)self->ptr.parent, |
|
PyUpb_CMessage_GetFieldDef(self)); |
|
Py_DECREF(self->ptr.parent); |
|
} else { |
|
PyUpb_ObjCache_Delete(self->ptr.msg); |
|
} |
|
|
|
if (self->unset_subobj_map) { |
|
PyUpb_WeakMap_Free(self->unset_subobj_map); |
|
} |
|
|
|
Py_DECREF(self->arena); |
|
|
|
// We do not use PyUpb_Dealloc() here because CMessage is a base type and for |
|
// base types there is a bug we have to work around in this case (see below). |
|
PyTypeObject* tp = Py_TYPE(self); |
|
freefunc tp_free = PyType_GetSlot(tp, Py_tp_free); |
|
tp_free(self); |
|
|
|
if (cpython_bits.python_version_hex >= 0x03080000) { |
|
// Prior to Python 3.8 there is a bug where deallocating the type here would |
|
// lead to a double-decref: https://bugs.python.org/issue37879 |
|
Py_DECREF(tp); |
|
} |
|
} |
|
|
|
PyObject* PyUpb_CMessage_Get(upb_Message* u_msg, const upb_MessageDef* m, |
|
PyObject* arena) { |
|
PyObject* ret = PyUpb_ObjCache_Get(u_msg); |
|
if (ret) return ret; |
|
|
|
PyObject* cls = PyUpb_Descriptor_GetClass(m); |
|
// It is not safe to use PyObject_{,GC}_New() due to: |
|
// https://bugs.python.org/issue35810 |
|
PyUpb_CMessage* py_msg = (void*)PyType_GenericAlloc((PyTypeObject*)cls, 0); |
|
py_msg->arena = arena; |
|
py_msg->def = (uintptr_t)m; |
|
py_msg->ptr.msg = u_msg; |
|
py_msg->unset_subobj_map = NULL; |
|
py_msg->ext_dict = NULL; |
|
py_msg->version = 0; |
|
ret = &py_msg->ob_base; |
|
Py_DECREF(cls); |
|
Py_INCREF(arena); |
|
PyUpb_ObjCache_Add(u_msg, ret); |
|
return ret; |
|
} |
|
|
|
/* PyUpb_CMessage_GetStub() |
|
* |
|
* Non-present messages return "stub" objects that point to their parent, but |
|
* will materialize into real upb objects if they are mutated. |
|
* |
|
* Note: we do *not* create stubs for repeated/map fields unless the parent |
|
* is a stub: |
|
* |
|
* msg = TestMessage() |
|
* msg.submessage # (A) Creates a stub |
|
* msg.repeated_foo # (B) Does *not* create a stub |
|
* msg.submessage.repeated_bar # (C) Creates a stub |
|
* |
|
* In case (B) we have some freedom: we could either create a stub, or create |
|
* a reified object with underlying data. It appears that either could work |
|
* equally well, with no observable change to users. There isn't a clear |
|
* advantage to either choice. We choose to follow the behavior of the |
|
* pre-existing C++ behavior for consistency, but if it becomes apparent that |
|
* there would be some benefit to reversing this decision, it should be totally |
|
* within the realm of possibility. |
|
*/ |
|
PyObject* PyUpb_CMessage_GetStub(PyUpb_CMessage* self, |
|
const upb_FieldDef* field) { |
|
PyObject* _self = (void*)self; |
|
if (!self->unset_subobj_map) { |
|
self->unset_subobj_map = PyUpb_WeakMap_New(); |
|
} |
|
PyObject* subobj = PyUpb_WeakMap_Get(self->unset_subobj_map, field); |
|
|
|
if (subobj) return subobj; |
|
|
|
if (upb_FieldDef_IsMap(field)) { |
|
subobj = PyUpb_MapContainer_NewStub(_self, field, self->arena); |
|
} else if (upb_FieldDef_IsRepeated(field)) { |
|
subobj = PyUpb_RepeatedContainer_NewStub(_self, field, self->arena); |
|
} else { |
|
subobj = PyUpb_CMessage_NewStub(&self->ob_base, field, self->arena); |
|
} |
|
PyUpb_WeakMap_Add(self->unset_subobj_map, field, subobj); |
|
|
|
assert(!PyErr_Occurred()); |
|
return subobj; |
|
} |
|
|
|
PyObject* PyUpb_CMessage_GetPresentWrapper(PyUpb_CMessage* self, |
|
const upb_FieldDef* field) { |
|
assert(!PyUpb_CMessage_IsStub(self)); |
|
upb_MutableMessageValue mutval = |
|
upb_Message_Mutable(self->ptr.msg, field, PyUpb_Arena_Get(self->arena)); |
|
if (upb_FieldDef_IsMap(field)) { |
|
return PyUpb_MapContainer_GetOrCreateWrapper(mutval.map, field, |
|
self->arena); |
|
} else { |
|
return PyUpb_RepeatedContainer_GetOrCreateWrapper(mutval.array, field, |
|
self->arena); |
|
} |
|
} |
|
|
|
PyObject* PyUpb_CMessage_GetScalarValue(PyUpb_CMessage* self, |
|
const upb_FieldDef* field) { |
|
upb_MessageValue val; |
|
if (PyUpb_CMessage_IsStub(self)) { |
|
// Unset message always returns default values. |
|
val = upb_FieldDef_Default(field); |
|
} else { |
|
val = upb_Message_Get(self->ptr.msg, field); |
|
} |
|
return PyUpb_UpbToPy(val, field, self->arena); |
|
} |
|
|
|
/* |
|
* PyUpb_CMessage_GetFieldValue() |
|
* |
|
* Implements the equivalent of getattr(msg, field), once `field` has |
|
* already been resolved to a `upb_FieldDef*`. |
|
* |
|
* This may involve constructing a wrapper object for the given field, or |
|
* returning one that was previously constructed. If the field is not actually |
|
* set, the wrapper object will be an "unset" object that is not actually |
|
* connected to any C data. |
|
*/ |
|
PyObject* PyUpb_CMessage_GetFieldValue(PyObject* _self, |
|
const upb_FieldDef* field) { |
|
PyUpb_CMessage* self = (void*)_self; |
|
assert(upb_FieldDef_ContainingType(field) == PyUpb_CMessage_GetMsgdef(_self)); |
|
bool submsg = upb_FieldDef_IsSubMessage(field); |
|
bool seq = upb_FieldDef_IsRepeated(field); |
|
|
|
if ((PyUpb_CMessage_IsStub(self) && (submsg || seq)) || |
|
(submsg && !seq && !upb_Message_Has(self->ptr.msg, field))) { |
|
return PyUpb_CMessage_GetStub(self, field); |
|
} else if (seq) { |
|
return PyUpb_CMessage_GetPresentWrapper(self, field); |
|
} else { |
|
return PyUpb_CMessage_GetScalarValue(self, field); |
|
} |
|
} |
|
|
|
int PyUpb_CMessage_SetFieldValue(PyObject* _self, const upb_FieldDef* field, |
|
PyObject* value, PyObject* exc) { |
|
PyUpb_CMessage* self = (void*)_self; |
|
assert(value); |
|
|
|
if (upb_FieldDef_IsSubMessage(field) || upb_FieldDef_IsRepeated(field)) { |
|
PyErr_Format(exc, |
|
"Assignment not allowed to message, map, or repeated " |
|
"field \"%s\" in protocol message object.", |
|
upb_FieldDef_Name(field)); |
|
return -1; |
|
} |
|
|
|
PyUpb_CMessage_EnsureReified(self); |
|
|
|
upb_MessageValue val; |
|
upb_Arena* arena = PyUpb_Arena_Get(self->arena); |
|
if (!PyUpb_PyToUpb(value, field, &val, arena)) { |
|
return -1; |
|
} |
|
|
|
upb_Message_Set(self->ptr.msg, field, val, arena); |
|
return 0; |
|
} |
|
|
|
int PyUpb_CMessage_GetVersion(PyObject* _self) { |
|
PyUpb_CMessage* self = (void*)_self; |
|
return self->version; |
|
} |
|
|
|
/* |
|
* PyUpb_CMessage_GetAttr() |
|
* |
|
* Implements: |
|
* foo = msg.foo |
|
* |
|
* Attribute lookup must find both message fields and base class methods like |
|
* msg.SerializeToString(). |
|
*/ |
|
__attribute__((flatten)) static PyObject* PyUpb_CMessage_GetAttr( |
|
PyObject* _self, PyObject* attr) { |
|
PyUpb_CMessage* self = (void*)_self; |
|
|
|
// Lookup field by name. |
|
const upb_FieldDef* field; |
|
if (PyUpb_CMessage_LookupName(self, attr, &field, NULL, NULL)) { |
|
return PyUpb_CMessage_GetFieldValue(_self, field); |
|
} |
|
|
|
// Check base class attributes. |
|
assert(!PyErr_Occurred()); |
|
PyObject* ret = PyObject_GenericGetAttr(_self, attr); |
|
if (ret) return ret; |
|
|
|
// 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. |
|
const char* name; |
|
if (PyErr_ExceptionMatches(PyExc_AttributeError) && |
|
(name = PyUpb_GetStrData(attr)) && strcmp(name, "Extensions") != 0) { |
|
PyErr_Clear(); |
|
return PyUpb_MessageMeta_GetAttr((PyObject*)Py_TYPE(_self), attr); |
|
} |
|
|
|
return NULL; |
|
} |
|
|
|
/* |
|
* PyUpb_CMessage_SetAttr() |
|
* |
|
* Implements: |
|
* msg.foo = foo |
|
*/ |
|
static int PyUpb_CMessage_SetAttr(PyObject* _self, PyObject* attr, |
|
PyObject* value) { |
|
PyUpb_CMessage* self = (void*)_self; |
|
const upb_FieldDef* field; |
|
if (!PyUpb_CMessage_LookupName(self, attr, &field, NULL, |
|
PyExc_AttributeError)) { |
|
return -1; |
|
} |
|
|
|
return PyUpb_CMessage_SetFieldValue(_self, field, value, |
|
PyExc_AttributeError); |
|
} |
|
|
|
static PyObject* PyUpb_CMessage_HasField(PyObject* _self, PyObject* arg) { |
|
PyUpb_CMessage* self = (void*)_self; |
|
const upb_FieldDef* field; |
|
const upb_OneofDef* oneof; |
|
|
|
if (!PyUpb_CMessage_LookupName(self, arg, &field, &oneof, PyExc_ValueError)) { |
|
return NULL; |
|
} |
|
|
|
if (field && !upb_FieldDef_HasPresence(field)) { |
|
PyErr_Format(PyExc_ValueError, "Field %s does not have presence.", |
|
upb_FieldDef_FullName(field)); |
|
return NULL; |
|
} |
|
|
|
if (PyUpb_CMessage_IsStub(self)) Py_RETURN_FALSE; |
|
|
|
return PyBool_FromLong(field ? upb_Message_Has(self->ptr.msg, field) |
|
: upb_Message_WhichOneof(self->ptr.msg, oneof) != |
|
NULL); |
|
} |
|
|
|
static PyObject* PyUpb_CMessage_FindInitializationErrors(PyObject* _self, |
|
PyObject* arg); |
|
|
|
static PyObject* PyUpb_CMessage_IsInitializedAppendErrors(PyObject* _self, |
|
PyObject* errors) { |
|
PyObject* list = PyUpb_CMessage_FindInitializationErrors(_self, NULL); |
|
if (!list) return NULL; |
|
bool ok = PyList_Size(list) == 0; |
|
PyObject* ret = NULL; |
|
PyObject* extend_result = NULL; |
|
if (!ok) { |
|
extend_result = PyObject_CallMethod(errors, "extend", "O", list); |
|
if (!extend_result) goto done; |
|
} |
|
ret = PyBool_FromLong(ok); |
|
|
|
done: |
|
Py_XDECREF(list); |
|
Py_XDECREF(extend_result); |
|
return ret; |
|
} |
|
|
|
static PyObject* PyUpb_CMessage_IsInitialized(PyObject* _self, PyObject* args) { |
|
PyObject* errors = NULL; |
|
if (!PyArg_ParseTuple(args, "|O", &errors)) { |
|
return NULL; |
|
} |
|
if (errors) { |
|
// We need to collect a list of unset required fields and append it to |
|
// `errors`. |
|
return PyUpb_CMessage_IsInitializedAppendErrors(_self, errors); |
|
} else { |
|
// We just need to return a boolean "true" or "false" for whether all |
|
// required fields are set. |
|
upb_Message* msg = PyUpb_CMessage_GetIfReified(_self); |
|
const upb_MessageDef* m = PyUpb_CMessage_GetMsgdef(_self); |
|
const upb_DefPool* symtab = upb_FileDef_Pool(upb_MessageDef_File(m)); |
|
bool initialized = !upb_util_HasUnsetRequired(msg, m, symtab, NULL); |
|
return PyBool_FromLong(initialized); |
|
} |
|
} |
|
|
|
static PyObject* PyUpb_CMessage_ListFieldsItemKey(PyObject* self, |
|
PyObject* val) { |
|
assert(PyTuple_Check(val)); |
|
PyObject* field = PyTuple_GetItem(val, 0); |
|
const upb_FieldDef* f = PyUpb_FieldDescriptor_GetDef(field); |
|
return PyLong_FromLong(upb_FieldDef_Number(f)); |
|
} |
|
|
|
static bool PyUpb_CMessage_SortFieldList(PyObject* list) { |
|
PyUpb_ModuleState* state = PyUpb_ModuleState_Get(); |
|
bool ok = false; |
|
PyObject* args = PyTuple_New(0); |
|
PyObject* kwargs = PyDict_New(); |
|
PyObject* method = PyObject_GetAttrString(list, "sort"); |
|
PyObject* call_result = NULL; |
|
if (!args || !kwargs || !method) goto err; |
|
if (PyDict_SetItemString(kwargs, "key", state->listfields_item_key) < 0) { |
|
goto err; |
|
} |
|
call_result = PyObject_Call(method, args, kwargs); |
|
if (!call_result) goto err; |
|
ok = true; |
|
|
|
err: |
|
Py_XDECREF(method); |
|
Py_XDECREF(args); |
|
Py_XDECREF(kwargs); |
|
Py_XDECREF(call_result); |
|
return ok; |
|
} |
|
|
|
static PyObject* PyUpb_CMessage_ListFields(PyObject* _self, PyObject* arg) { |
|
PyObject* list = PyList_New(0); |
|
upb_Message* msg = PyUpb_CMessage_GetIfReified(_self); |
|
if (!msg) return list; |
|
|
|
size_t iter1 = kUpb_Message_Begin; |
|
const upb_MessageDef* m = PyUpb_CMessage_GetMsgdef(_self); |
|
const upb_DefPool* symtab = upb_FileDef_Pool(upb_MessageDef_File(m)); |
|
const upb_FieldDef* f; |
|
PyObject* field_desc = NULL; |
|
PyObject* py_val = NULL; |
|
PyObject* tuple = NULL; |
|
upb_MessageValue val; |
|
uint32_t last_field = 0; |
|
bool in_order = true; |
|
while (upb_Message_Next(msg, m, symtab, &f, &val, &iter1)) { |
|
const uint32_t field_number = upb_FieldDef_Number(f); |
|
if (field_number < last_field) in_order = false; |
|
last_field = field_number; |
|
PyObject* field_desc = PyUpb_FieldDescriptor_Get(f); |
|
PyObject* py_val = PyUpb_CMessage_GetFieldValue(_self, f); |
|
if (!field_desc || !py_val) goto err; |
|
PyObject* tuple = Py_BuildValue("(NN)", field_desc, py_val); |
|
field_desc = NULL; |
|
py_val = NULL; |
|
if (!tuple) goto err; |
|
if (PyList_Append(list, tuple)) goto err; |
|
Py_DECREF(tuple); |
|
tuple = NULL; |
|
} |
|
|
|
// Users rely on fields being returned in field number order. |
|
if (!in_order && !PyUpb_CMessage_SortFieldList(list)) goto err; |
|
|
|
return list; |
|
|
|
err: |
|
Py_XDECREF(field_desc); |
|
Py_XDECREF(py_val); |
|
Py_XDECREF(tuple); |
|
Py_DECREF(list); |
|
return NULL; |
|
} |
|
|
|
PyObject* PyUpb_CMessage_MergeFrom(PyObject* self, PyObject* arg) { |
|
if (self->ob_type != arg->ob_type) { |
|
PyErr_Format(PyExc_TypeError, |
|
"Parameter to MergeFrom() must be instance of same class: " |
|
"expected %S got %S.", |
|
Py_TYPE(self), Py_TYPE(arg)); |
|
return NULL; |
|
} |
|
// OPT: exit if src is empty. |
|
PyObject* subargs = PyTuple_New(0); |
|
PyObject* serialized = PyUpb_CMessage_SerializeToString(arg, subargs, NULL); |
|
Py_DECREF(subargs); |
|
if (!serialized) return NULL; |
|
PyObject* ret = PyUpb_CMessage_MergeFromString(self, serialized); |
|
Py_DECREF(serialized); |
|
Py_DECREF(ret); |
|
Py_RETURN_NONE; |
|
} |
|
|
|
static PyObject* PyUpb_CMessage_SetInParent(PyObject* _self, PyObject* arg) { |
|
PyUpb_CMessage* self = (void*)_self; |
|
PyUpb_CMessage_EnsureReified(self); |
|
Py_RETURN_NONE; |
|
} |
|
|
|
static PyObject* PyUpb_CMessage_UnknownFields(PyObject* _self, PyObject* arg) { |
|
// TODO(haberman): re-enable when unknown fields are added. |
|
// return PyUpb_UnknownFields_New(_self); |
|
PyErr_SetString(PyExc_NotImplementedError, "unknown field accessor"); |
|
return NULL; |
|
} |
|
|
|
PyObject* PyUpb_CMessage_MergeFromString(PyObject* _self, PyObject* arg) { |
|
PyUpb_CMessage* self = (void*)_self; |
|
char* buf; |
|
Py_ssize_t size; |
|
PyObject* bytes = NULL; |
|
|
|
if (PyMemoryView_Check(arg)) { |
|
bytes = PyBytes_FromObject(arg); |
|
// Cannot fail when passed something of the correct type. |
|
int err = PyBytes_AsStringAndSize(bytes, &buf, &size); |
|
(void)err; |
|
assert(err >= 0); |
|
} else if (PyBytes_AsStringAndSize(arg, &buf, &size) < 0) { |
|
return NULL; |
|
} |
|
|
|
PyUpb_CMessage_EnsureReified(self); |
|
const upb_MessageDef* msgdef = _PyUpb_CMessage_GetMsgdef(self); |
|
const upb_FileDef* file = upb_MessageDef_File(msgdef); |
|
const upb_ExtensionRegistry* extreg = |
|
upb_DefPool_ExtensionRegistry(upb_FileDef_Pool(file)); |
|
const upb_MiniTable* layout = upb_MessageDef_MiniTable(msgdef); |
|
upb_Arena* arena = PyUpb_Arena_Get(self->arena); |
|
PyUpb_ModuleState* state = PyUpb_ModuleState_Get(); |
|
int options = |
|
UPB_DECODE_MAXDEPTH(state->allow_oversize_protos ? UINT32_MAX : 100); |
|
upb_DecodeStatus status = |
|
upb_Decode(buf, size, self->ptr.msg, layout, extreg, options, arena); |
|
Py_XDECREF(bytes); |
|
if (status != kUpb_DecodeStatus_Ok) { |
|
PyErr_Format(state->decode_error_class, "Error parsing message"); |
|
return NULL; |
|
} |
|
PyUpb_CMessage_SyncSubobjs(self); |
|
return PyLong_FromSsize_t(size); |
|
} |
|
|
|
static PyObject* PyUpb_CMessage_Clear(PyUpb_CMessage* self, PyObject* args); |
|
|
|
static PyObject* PyUpb_CMessage_ParseFromString(PyObject* self, PyObject* arg) { |
|
PyObject* tmp = PyUpb_CMessage_Clear((PyUpb_CMessage*)self, NULL); |
|
Py_DECREF(tmp); |
|
return PyUpb_CMessage_MergeFromString(self, arg); |
|
} |
|
|
|
static PyObject* PyUpb_CMessage_ByteSize(PyObject* self, PyObject* args) { |
|
// TODO(https://github.com/protocolbuffers/upb/issues/462): At the moment upb |
|
// does not have a "byte size" function, so we just serialize to string and |
|
// get the size of the string. |
|
PyObject* subargs = PyTuple_New(0); |
|
PyObject* serialized = PyUpb_CMessage_SerializeToString(self, subargs, NULL); |
|
Py_DECREF(subargs); |
|
if (!serialized) return NULL; |
|
size_t size = PyBytes_Size(serialized); |
|
Py_DECREF(serialized); |
|
return PyLong_FromSize_t(size); |
|
} |
|
|
|
static PyObject* PyUpb_CMessage_Clear(PyUpb_CMessage* self, PyObject* args) { |
|
PyUpb_CMessage_EnsureReified(self); |
|
const upb_MessageDef* msgdef = _PyUpb_CMessage_GetMsgdef(self); |
|
PyUpb_WeakMap* subobj_map = self->unset_subobj_map; |
|
|
|
if (subobj_map) { |
|
upb_Message* msg = PyUpb_CMessage_GetMsg(self); |
|
intptr_t iter = PYUPB_WEAKMAP_BEGIN; |
|
const void* key; |
|
PyObject* obj; |
|
|
|
while (PyUpb_WeakMap_Next(subobj_map, &key, &obj, &iter)) { |
|
const upb_FieldDef* f = key; |
|
PyUpb_WeakMap_DeleteIter(subobj_map, &iter); |
|
if (upb_FieldDef_IsMap(f)) { |
|
assert(upb_Message_Get(msg, f).map_val == NULL); |
|
PyUpb_MapContainer_Reify(obj, NULL); |
|
} else if (upb_FieldDef_IsRepeated(f)) { |
|
assert(upb_Message_Get(msg, f).array_val == NULL); |
|
PyUpb_RepeatedContainer_Reify(obj, NULL); |
|
} else { |
|
assert(!upb_Message_Has(msg, f)); |
|
PyUpb_CMessage* sub = (void*)obj; |
|
assert(self == sub->ptr.parent); |
|
PyUpb_CMessage_Reify(sub, f, NULL); |
|
} |
|
} |
|
} |
|
|
|
upb_Message_Clear(self->ptr.msg, msgdef); |
|
Py_RETURN_NONE; |
|
} |
|
|
|
void PyUpb_CMessage_DoClearField(PyObject* _self, const upb_FieldDef* f) { |
|
PyUpb_CMessage* self = (void*)_self; |
|
PyUpb_CMessage_EnsureReified((PyUpb_CMessage*)self); |
|
|
|
// We must ensure that any stub object is reified so its parent no longer |
|
// points to us. |
|
PyObject* sub = self->unset_subobj_map |
|
? PyUpb_WeakMap_Get(self->unset_subobj_map, f) |
|
: NULL; |
|
|
|
if (upb_FieldDef_IsMap(f)) { |
|
// For maps we additionally have to invalidate any iterators. So we need |
|
// to get an object even if it's reified. |
|
if (!sub) { |
|
sub = PyUpb_CMessage_GetFieldValue(_self, f); |
|
} |
|
PyUpb_MapContainer_EnsureReified(sub); |
|
PyUpb_MapContainer_Invalidate(sub); |
|
} else if (upb_FieldDef_IsRepeated(f)) { |
|
if (sub) { |
|
PyUpb_RepeatedContainer_EnsureReified(sub); |
|
} |
|
} else if (upb_FieldDef_IsSubMessage(f)) { |
|
if (sub) { |
|
PyUpb_CMessage_EnsureReified((PyUpb_CMessage*)sub); |
|
} |
|
} |
|
|
|
Py_XDECREF(sub); |
|
upb_Message_ClearField(self->ptr.msg, f); |
|
} |
|
|
|
static PyObject* PyUpb_CMessage_ClearExtension(PyObject* _self, PyObject* arg) { |
|
PyUpb_CMessage* self = (void*)_self; |
|
PyUpb_CMessage_EnsureReified(self); |
|
const upb_FieldDef* f = PyUpb_CMessage_GetExtensionDef(_self, arg); |
|
if (!f) return NULL; |
|
PyUpb_CMessage_DoClearField(_self, f); |
|
Py_RETURN_NONE; |
|
} |
|
|
|
static PyObject* PyUpb_CMessage_ClearField(PyObject* _self, PyObject* arg) { |
|
PyUpb_CMessage* self = (void*)_self; |
|
|
|
// We always need EnsureReified() here (even for an unset message) to |
|
// preserve behavior like: |
|
// msg = FooMessage() |
|
// msg.foo.Clear() |
|
// assert msg.HasField("foo") |
|
PyUpb_CMessage_EnsureReified(self); |
|
|
|
const upb_FieldDef* f; |
|
const upb_OneofDef* o; |
|
if (!PyUpb_CMessage_LookupName(self, arg, &f, &o, PyExc_ValueError)) { |
|
return NULL; |
|
} |
|
|
|
if (o) f = upb_Message_WhichOneof(self->ptr.msg, o); |
|
PyUpb_CMessage_DoClearField(_self, f); |
|
Py_RETURN_NONE; |
|
} |
|
|
|
static PyObject* PyUpb_CMessage_DiscardUnknownFields(PyUpb_CMessage* self, |
|
PyObject* arg) { |
|
PyUpb_CMessage_EnsureReified(self); |
|
const upb_MessageDef* msgdef = _PyUpb_CMessage_GetMsgdef(self); |
|
upb_Message_DiscardUnknown(self->ptr.msg, msgdef, 64); |
|
Py_RETURN_NONE; |
|
} |
|
|
|
static PyObject* PyUpb_CMessage_FindInitializationErrors(PyObject* _self, |
|
PyObject* arg) { |
|
PyUpb_CMessage* self = (void*)_self; |
|
upb_Message* msg = PyUpb_CMessage_GetIfReified(_self); |
|
const upb_MessageDef* msgdef = _PyUpb_CMessage_GetMsgdef(self); |
|
const upb_DefPool* ext_pool = upb_FileDef_Pool(upb_MessageDef_File(msgdef)); |
|
upb_FieldPathEntry* fields; |
|
PyObject* ret = PyList_New(0); |
|
if (upb_util_HasUnsetRequired(msg, msgdef, ext_pool, &fields)) { |
|
char* buf = NULL; |
|
size_t size = 0; |
|
assert(fields->field); |
|
while (fields->field) { |
|
upb_FieldPathEntry* field = fields; |
|
size_t need = upb_FieldPath_ToText(&fields, buf, size); |
|
if (need >= size) { |
|
fields = field; |
|
size = size ? size * 2 : 16; |
|
while (size <= need) size *= 2; |
|
buf = realloc(buf, size); |
|
need = upb_FieldPath_ToText(&fields, buf, size); |
|
assert(size > need); |
|
} |
|
PyObject* str = PyUnicode_FromString(buf); |
|
PyList_Append(ret, str); |
|
Py_DECREF(str); |
|
} |
|
free(buf); |
|
} |
|
return ret; |
|
} |
|
|
|
static PyObject* PyUpb_CMessage_FromString(PyObject* cls, |
|
PyObject* serialized) { |
|
PyObject* ret = NULL; |
|
PyObject* length = NULL; |
|
|
|
ret = PyObject_CallObject(cls, NULL); |
|
if (ret == NULL) goto err; |
|
length = PyUpb_CMessage_MergeFromString(ret, serialized); |
|
if (length == NULL) goto err; |
|
|
|
done: |
|
Py_XDECREF(length); |
|
return ret; |
|
|
|
err: |
|
Py_XDECREF(ret); |
|
ret = NULL; |
|
goto done; |
|
} |
|
|
|
const upb_FieldDef* PyUpb_CMessage_GetExtensionDef(PyObject* _self, |
|
PyObject* key) { |
|
const upb_FieldDef* f = PyUpb_FieldDescriptor_GetDef(key); |
|
if (!f) { |
|
PyErr_Clear(); |
|
PyErr_Format(PyExc_KeyError, "Object %R is not a field descriptor\n", key); |
|
return NULL; |
|
} |
|
if (!upb_FieldDef_IsExtension(f)) { |
|
PyErr_Format(PyExc_KeyError, "Field %s is not an extension\n", |
|
upb_FieldDef_FullName(f)); |
|
return NULL; |
|
} |
|
const upb_MessageDef* msgdef = PyUpb_CMessage_GetMsgdef(_self); |
|
if (upb_FieldDef_ContainingType(f) != msgdef) { |
|
PyErr_Format(PyExc_KeyError, "Extension doesn't match (%s vs %s)", |
|
upb_MessageDef_FullName(msgdef), upb_FieldDef_FullName(f)); |
|
return NULL; |
|
} |
|
return f; |
|
} |
|
|
|
static PyObject* PyUpb_CMessage_HasExtension(PyObject* _self, |
|
PyObject* ext_desc) { |
|
upb_Message* msg = PyUpb_CMessage_GetIfReified(_self); |
|
const upb_FieldDef* f = PyUpb_CMessage_GetExtensionDef(_self, ext_desc); |
|
if (!f) return NULL; |
|
if (upb_FieldDef_IsRepeated(f)) { |
|
PyErr_SetString(PyExc_KeyError, |
|
"Field is repeated. A singular method is required."); |
|
return NULL; |
|
} |
|
if (!msg) Py_RETURN_FALSE; |
|
return PyBool_FromLong(upb_Message_Has(msg, f)); |
|
} |
|
|
|
void PyUpb_CMessage_ReportInitializationErrors(const upb_MessageDef* msgdef, |
|
PyObject* errors, |
|
PyObject* exc) { |
|
PyObject* comma = PyUnicode_FromString(","); |
|
PyObject* missing_fields = NULL; |
|
if (!comma) goto done; |
|
missing_fields = PyUnicode_Join(comma, errors); |
|
if (!missing_fields) goto done; |
|
PyErr_Format(exc, "Message %s is missing required fields: %U", |
|
upb_MessageDef_FullName(msgdef), missing_fields); |
|
done: |
|
Py_XDECREF(comma); |
|
Py_XDECREF(missing_fields); |
|
Py_DECREF(errors); |
|
} |
|
|
|
PyObject* PyUpb_CMessage_SerializeInternal(PyObject* _self, PyObject* args, |
|
PyObject* kwargs, |
|
bool check_required) { |
|
PyUpb_CMessage* self = (void*)_self; |
|
if (!PyUpb_CMessage_Verify((PyObject*)self)) return NULL; |
|
static const char* kwlist[] = {"deterministic", NULL}; |
|
int deterministic = 0; |
|
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|p", (char**)(kwlist), |
|
&deterministic)) { |
|
return NULL; |
|
} |
|
|
|
const upb_MessageDef* msgdef = _PyUpb_CMessage_GetMsgdef(self); |
|
if (PyUpb_CMessage_IsStub(self)) { |
|
// Nothing to serialize, but we do have to check whether the message is |
|
// initialized. |
|
PyUpb_ModuleState* state = PyUpb_ModuleState_Get(); |
|
PyObject* errors = PyUpb_CMessage_FindInitializationErrors(_self, NULL); |
|
if (!errors) return NULL; |
|
if (PyList_Size(errors) == 0) { |
|
Py_DECREF(errors); |
|
return PyBytes_FromStringAndSize(NULL, 0); |
|
} |
|
PyUpb_CMessage_ReportInitializationErrors(msgdef, errors, |
|
state->encode_error_class); |
|
return NULL; |
|
} |
|
|
|
upb_Arena* arena = upb_Arena_New(); |
|
const upb_MiniTable* layout = upb_MessageDef_MiniTable(msgdef); |
|
size_t size = 0; |
|
// Python does not currently have any effective limit on serialization depth. |
|
int options = UPB_ENCODE_MAXDEPTH(UINT32_MAX); |
|
if (check_required) options |= kUpb_Encode_CheckRequired; |
|
if (deterministic) options |= kUpb_Encode_Deterministic; |
|
char* pb = upb_Encode(self->ptr.msg, layout, options, arena, &size); |
|
PyObject* ret = NULL; |
|
|
|
if (!pb) { |
|
PyUpb_ModuleState* state = PyUpb_ModuleState_Get(); |
|
PyObject* errors = PyUpb_CMessage_FindInitializationErrors(_self, NULL); |
|
if (PyList_Size(errors) != 0) { |
|
PyUpb_CMessage_ReportInitializationErrors(msgdef, errors, |
|
state->encode_error_class); |
|
} else { |
|
PyErr_Format(state->encode_error_class, "Failed to serialize proto"); |
|
} |
|
goto done; |
|
} |
|
|
|
ret = PyBytes_FromStringAndSize(pb, size); |
|
|
|
done: |
|
upb_Arena_Free(arena); |
|
return ret; |
|
} |
|
|
|
PyObject* PyUpb_CMessage_SerializeToString(PyObject* _self, PyObject* args, |
|
PyObject* kwargs) { |
|
return PyUpb_CMessage_SerializeInternal(_self, args, kwargs, true); |
|
} |
|
|
|
PyObject* PyUpb_CMessage_SerializePartialToString(PyObject* _self, |
|
PyObject* args, |
|
PyObject* kwargs) { |
|
return PyUpb_CMessage_SerializeInternal(_self, args, kwargs, false); |
|
} |
|
|
|
static PyObject* PyUpb_CMessage_WhichOneof(PyObject* _self, PyObject* name) { |
|
PyUpb_CMessage* self = (void*)_self; |
|
const upb_OneofDef* o; |
|
if (!PyUpb_CMessage_LookupName(self, name, NULL, &o, PyExc_ValueError)) { |
|
return NULL; |
|
} |
|
upb_Message* msg = PyUpb_CMessage_GetIfReified(_self); |
|
if (!msg) Py_RETURN_NONE; |
|
const upb_FieldDef* f = upb_Message_WhichOneof(msg, o); |
|
if (!f) Py_RETURN_NONE; |
|
return PyUnicode_FromString(upb_FieldDef_Name(f)); |
|
} |
|
|
|
void PyUpb_CMessage_ClearExtensionDict(PyObject* _self) { |
|
PyUpb_CMessage* self = (void*)_self; |
|
assert(self->ext_dict); |
|
self->ext_dict = NULL; |
|
} |
|
|
|
static PyObject* PyUpb_CMessage_GetExtensionDict(PyObject* _self, |
|
void* closure) { |
|
PyUpb_CMessage* self = (void*)_self; |
|
if (self->ext_dict) { |
|
return self->ext_dict; |
|
} |
|
|
|
const upb_MessageDef* m = _PyUpb_CMessage_GetMsgdef(self); |
|
if (upb_MessageDef_ExtensionRangeCount(m) == 0) { |
|
PyErr_SetNone(PyExc_AttributeError); |
|
return NULL; |
|
} |
|
|
|
self->ext_dict = PyUpb_ExtensionDict_New(_self); |
|
return self->ext_dict; |
|
} |
|
|
|
static PyGetSetDef PyUpb_CMessage_Getters[] = { |
|
{"Extensions", PyUpb_CMessage_GetExtensionDict, NULL, "Extension dict"}, |
|
{NULL}}; |
|
|
|
static PyMethodDef PyUpb_CMessage_Methods[] = { |
|
// TODO(https://github.com/protocolbuffers/upb/issues/459) |
|
//{ "__deepcopy__", (PyCFunction)DeepCopy, METH_VARARGS, |
|
// "Makes a deep copy of the class." }, |
|
//{ "__unicode__", (PyCFunction)ToUnicode, METH_NOARGS, |
|
// "Outputs a unicode representation of the message." }, |
|
{"ByteSize", (PyCFunction)PyUpb_CMessage_ByteSize, METH_NOARGS, |
|
"Returns the size of the message in bytes."}, |
|
{"Clear", (PyCFunction)PyUpb_CMessage_Clear, METH_NOARGS, |
|
"Clears the message."}, |
|
{"ClearExtension", PyUpb_CMessage_ClearExtension, METH_O, |
|
"Clears a message field."}, |
|
{"ClearField", PyUpb_CMessage_ClearField, METH_O, |
|
"Clears a message field."}, |
|
// TODO(https://github.com/protocolbuffers/upb/issues/459) |
|
//{ "CopyFrom", (PyCFunction)CopyFrom, METH_O, |
|
// "Copies a protocol message into the current message." }, |
|
{"DiscardUnknownFields", (PyCFunction)PyUpb_CMessage_DiscardUnknownFields, |
|
METH_NOARGS, "Discards the unknown fields."}, |
|
{"FindInitializationErrors", PyUpb_CMessage_FindInitializationErrors, |
|
METH_NOARGS, "Finds unset required fields."}, |
|
{"FromString", PyUpb_CMessage_FromString, METH_O | METH_CLASS, |
|
"Creates new method instance from given serialized data."}, |
|
{"HasExtension", PyUpb_CMessage_HasExtension, METH_O, |
|
"Checks if a message field is set."}, |
|
{"HasField", PyUpb_CMessage_HasField, METH_O, |
|
"Checks if a message field is set."}, |
|
{"IsInitialized", PyUpb_CMessage_IsInitialized, METH_VARARGS, |
|
"Checks if all required fields of a protocol message are set."}, |
|
{"ListFields", PyUpb_CMessage_ListFields, METH_NOARGS, |
|
"Lists all set fields of a message."}, |
|
{"MergeFrom", PyUpb_CMessage_MergeFrom, METH_O, |
|
"Merges a protocol message into the current message."}, |
|
{"MergeFromString", PyUpb_CMessage_MergeFromString, METH_O, |
|
"Merges a serialized message into the current message."}, |
|
{"ParseFromString", PyUpb_CMessage_ParseFromString, METH_O, |
|
"Parses a serialized message into the current message."}, |
|
// TODO(https://github.com/protocolbuffers/upb/issues/459) |
|
//{ "RegisterExtension", (PyCFunction)RegisterExtension, METH_O | |
|
// METH_CLASS, |
|
// "Registers an extension with the current message." }, |
|
{"SerializePartialToString", |
|
(PyCFunction)PyUpb_CMessage_SerializePartialToString, |
|
METH_VARARGS | METH_KEYWORDS, |
|
"Serializes the message to a string, even if it isn't initialized."}, |
|
{"SerializeToString", (PyCFunction)PyUpb_CMessage_SerializeToString, |
|
METH_VARARGS | METH_KEYWORDS, |
|
"Serializes the message to a string, only for initialized messages."}, |
|
{"SetInParent", (PyCFunction)PyUpb_CMessage_SetInParent, METH_NOARGS, |
|
"Sets the has bit of the given field in its parent message."}, |
|
{"UnknownFields", (PyCFunction)PyUpb_CMessage_UnknownFields, METH_NOARGS, |
|
"Parse unknown field set"}, |
|
{"WhichOneof", PyUpb_CMessage_WhichOneof, METH_O, |
|
"Returns the name of the field set inside a oneof, " |
|
"or None if no field is set."}, |
|
{"_ListFieldsItemKey", PyUpb_CMessage_ListFieldsItemKey, |
|
METH_O | METH_STATIC, |
|
"Compares ListFields() list entries by field number"}, |
|
{NULL, NULL}}; |
|
|
|
static PyType_Slot PyUpb_CMessage_Slots[] = { |
|
{Py_tp_dealloc, PyUpb_CMessage_Dealloc}, |
|
{Py_tp_doc, "A ProtocolMessage"}, |
|
{Py_tp_getattro, PyUpb_CMessage_GetAttr}, |
|
{Py_tp_getset, PyUpb_CMessage_Getters}, |
|
{Py_tp_hash, PyObject_HashNotImplemented}, |
|
{Py_tp_methods, PyUpb_CMessage_Methods}, |
|
{Py_tp_new, PyUpb_CMessage_New}, |
|
{Py_tp_str, PyUpb_CMessage_ToString}, |
|
{Py_tp_repr, PyUpb_CMessage_ToString}, |
|
{Py_tp_richcompare, PyUpb_CMessage_RichCompare}, |
|
{Py_tp_setattro, PyUpb_CMessage_SetAttr}, |
|
{Py_tp_init, PyUpb_CMessage_Init}, |
|
{0, NULL}}; |
|
|
|
PyType_Spec PyUpb_CMessage_Spec = { |
|
PYUPB_MODULE_NAME ".CMessage", // tp_name |
|
sizeof(PyUpb_CMessage), // tp_basicsize |
|
0, // tp_itemsize |
|
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, // tp_flags |
|
PyUpb_CMessage_Slots, |
|
}; |
|
|
|
// ----------------------------------------------------------------------------- |
|
// MessageMeta |
|
// ----------------------------------------------------------------------------- |
|
|
|
// MessageMeta is the metaclass for message objects. The generated code uses it |
|
// to construct message classes, ie. |
|
// |
|
// FooMessage = _message.MessageMeta('FooMessage', (_message.Message), {...}) |
|
// |
|
// (This is not quite true: at the moment the Python library subclasses |
|
// MessageMeta, and uses that subclass as the metaclass. There is a TODO below |
|
// to simplify this, so that the illustration above is indeed accurate). |
|
|
|
typedef struct { |
|
const upb_MiniTable* layout; |
|
PyObject* py_message_descriptor; |
|
} PyUpb_MessageMeta; |
|
|
|
// The PyUpb_MessageMeta struct is trailing data tacked onto the end of |
|
// MessageMeta instances. This means that we get our instances of this struct |
|
// by adding the appropriate number of bytes. |
|
static PyUpb_MessageMeta* PyUpb_GetMessageMeta(PyObject* cls) { |
|
#ifndef NDEBUG |
|
PyUpb_ModuleState* state = PyUpb_ModuleState_MaybeGet(); |
|
assert(!state || cls->ob_type == state->message_meta_type); |
|
#endif |
|
return (PyUpb_MessageMeta*)((char*)cls + cpython_bits.type_basicsize); |
|
} |
|
|
|
static const upb_MessageDef* PyUpb_MessageMeta_GetMsgdef(PyObject* cls) { |
|
PyUpb_MessageMeta* self = PyUpb_GetMessageMeta(cls); |
|
return PyUpb_Descriptor_GetDef(self->py_message_descriptor); |
|
} |
|
|
|
PyObject* PyUpb_MessageMeta_DoCreateClass(PyObject* py_descriptor, |
|
const char* name, PyObject* dict) { |
|
PyUpb_ModuleState* state = PyUpb_ModuleState_Get(); |
|
PyTypeObject* descriptor_type = state->descriptor_types[kPyUpb_Descriptor]; |
|
if (!PyObject_TypeCheck(py_descriptor, descriptor_type)) { |
|
return PyErr_Format(PyExc_TypeError, "Expected a message Descriptor"); |
|
} |
|
|
|
const upb_MessageDef* msgdef = PyUpb_Descriptor_GetDef(py_descriptor); |
|
assert(msgdef); |
|
assert(!PyUpb_ObjCache_Get(upb_MessageDef_MiniTable(msgdef))); |
|
|
|
PyObject* slots = PyTuple_New(0); |
|
if (!slots) return NULL; |
|
int status = PyDict_SetItemString(dict, "__slots__", slots); |
|
Py_DECREF(slots); |
|
if (status < 0) return NULL; |
|
|
|
// Bases are either: |
|
// (CMessage, Message) # for regular messages |
|
// (CMessage, Message, WktBase) # For well-known types |
|
PyObject* wkt_bases = PyUpb_GetWktBases(state); |
|
PyObject* wkt_base = |
|
PyDict_GetItemString(wkt_bases, upb_MessageDef_FullName(msgdef)); |
|
PyObject* args; |
|
if (wkt_base == NULL) { |
|
args = Py_BuildValue("s(OO)O", name, state->cmessage_type, |
|
state->message_class, dict); |
|
} else { |
|
args = Py_BuildValue("s(OOO)O", name, state->cmessage_type, |
|
state->message_class, wkt_base, dict); |
|
} |
|
|
|
PyObject* ret = cpython_bits.type_new(state->message_meta_type, args, NULL); |
|
Py_DECREF(args); |
|
if (!ret) return NULL; |
|
|
|
PyUpb_MessageMeta* meta = PyUpb_GetMessageMeta(ret); |
|
meta->py_message_descriptor = py_descriptor; |
|
meta->layout = upb_MessageDef_MiniTable(msgdef); |
|
Py_INCREF(meta->py_message_descriptor); |
|
|
|
PyUpb_ObjCache_Add(meta->layout, ret); |
|
|
|
return ret; |
|
} |
|
|
|
static PyObject* PyUpb_MessageMeta_New(PyTypeObject* type, PyObject* args, |
|
PyObject* kwargs) { |
|
PyUpb_ModuleState* state = PyUpb_ModuleState_Get(); |
|
static const char* kwlist[] = {"name", "bases", "dict", 0}; |
|
PyObject *bases, *dict; |
|
const char* name; |
|
|
|
// Check arguments: (name, bases, dict) |
|
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sO!O!:type", (char**)kwlist, |
|
&name, &PyTuple_Type, &bases, &PyDict_Type, |
|
&dict)) { |
|
return NULL; |
|
} |
|
|
|
// Check bases: only (), or (message.Message,) are allowed |
|
Py_ssize_t size = PyTuple_Size(bases); |
|
if (!(size == 0 || |
|
(size == 1 && PyTuple_GetItem(bases, 0) == state->message_class))) { |
|
PyErr_Format(PyExc_TypeError, |
|
"A Message class can only inherit from Message, not %S", |
|
bases); |
|
return NULL; |
|
} |
|
|
|
// Check dict['DESCRIPTOR'] |
|
PyObject* py_descriptor = PyDict_GetItemString(dict, "DESCRIPTOR"); |
|
if (py_descriptor == NULL) { |
|
PyErr_SetString(PyExc_TypeError, "Message class has no DESCRIPTOR"); |
|
return NULL; |
|
} |
|
|
|
const upb_MessageDef* m = PyUpb_Descriptor_GetDef(py_descriptor); |
|
PyObject* ret = PyUpb_ObjCache_Get(upb_MessageDef_MiniTable(m)); |
|
if (ret) return ret; |
|
return PyUpb_MessageMeta_DoCreateClass(py_descriptor, name, dict); |
|
} |
|
|
|
static void PyUpb_MessageMeta_Dealloc(PyObject* self) { |
|
PyUpb_MessageMeta* meta = PyUpb_GetMessageMeta(self); |
|
PyUpb_ObjCache_Delete(meta->layout); |
|
Py_DECREF(meta->py_message_descriptor); |
|
PyTypeObject* tp = Py_TYPE(self); |
|
cpython_bits.type_dealloc(self); |
|
Py_DECREF(tp); |
|
} |
|
|
|
void PyUpb_MessageMeta_AddFieldNumber(PyObject* self, const upb_FieldDef* f) { |
|
PyObject* name = |
|
PyUnicode_FromFormat("%s_FIELD_NUMBER", upb_FieldDef_Name(f)); |
|
PyObject* upper = PyObject_CallMethod(name, "upper", ""); |
|
PyObject_SetAttr(self, upper, PyLong_FromLong(upb_FieldDef_Number(f))); |
|
Py_DECREF(name); |
|
Py_DECREF(upper); |
|
} |
|
|
|
static PyObject* PyUpb_MessageMeta_GetDynamicAttr(PyObject* self, |
|
PyObject* name) { |
|
const char* name_buf = PyUpb_GetStrData(name); |
|
if (!name_buf) return NULL; |
|
const upb_MessageDef* msgdef = PyUpb_MessageMeta_GetMsgdef(self); |
|
const upb_FileDef* filedef = upb_MessageDef_File(msgdef); |
|
const upb_DefPool* symtab = upb_FileDef_Pool(filedef); |
|
|
|
PyObject* py_key = |
|
PyBytes_FromFormat("%s.%s", upb_MessageDef_FullName(msgdef), name_buf); |
|
const char* key = PyUpb_GetStrData(py_key); |
|
PyObject* ret = NULL; |
|
const upb_MessageDef* nested = upb_DefPool_FindMessageByName(symtab, key); |
|
const upb_EnumDef* enumdef; |
|
const upb_EnumValueDef* enumval; |
|
const upb_FieldDef* ext; |
|
|
|
if (nested) { |
|
ret = PyUpb_Descriptor_GetClass(nested); |
|
} else if ((enumdef = upb_DefPool_FindEnumByName(symtab, key))) { |
|
PyUpb_ModuleState* state = PyUpb_ModuleState_Get(); |
|
PyObject* klass = state->enum_type_wrapper_class; |
|
ret = PyUpb_EnumDescriptor_Get(enumdef); |
|
ret = PyObject_CallFunctionObjArgs(klass, ret, NULL); |
|
} else if ((enumval = upb_DefPool_FindEnumByNameval(symtab, key))) { |
|
ret = PyLong_FromLong(upb_EnumValueDef_Number(enumval)); |
|
} else if ((ext = upb_DefPool_FindExtensionByName(symtab, key))) { |
|
ret = PyUpb_FieldDescriptor_Get(ext); |
|
} |
|
|
|
Py_DECREF(py_key); |
|
|
|
const char* suffix = "_FIELD_NUMBER"; |
|
size_t n = strlen(name_buf); |
|
size_t suffix_n = strlen(suffix); |
|
if (n > suffix_n && memcmp(suffix, name_buf + n - suffix_n, suffix_n) == 0) { |
|
// We can't look up field names dynamically, because the <NAME>_FIELD_NUMBER |
|
// naming scheme upper-cases the field name and is therefore non-reversible. |
|
// So we just add all field numbers. |
|
int n = upb_MessageDef_FieldCount(msgdef); |
|
for (int i = 0; i < n; i++) { |
|
PyUpb_MessageMeta_AddFieldNumber(self, upb_MessageDef_Field(msgdef, i)); |
|
} |
|
n = upb_MessageDef_NestedExtensionCount(msgdef); |
|
for (int i = 0; i < n; i++) { |
|
PyUpb_MessageMeta_AddFieldNumber( |
|
self, upb_MessageDef_NestedExtension(msgdef, i)); |
|
} |
|
ret = PyObject_GenericGetAttr(self, name); |
|
} |
|
|
|
return ret; |
|
} |
|
|
|
static PyObject* PyUpb_MessageMeta_GetAttr(PyObject* self, PyObject* name) { |
|
// We want to first delegate to the type's tp_dict to retrieve any attributes |
|
// that were previously calculated and cached in the type's dict. |
|
PyObject* ret = cpython_bits.type_getattro(self, name); |
|
if (ret) return ret; |
|
|
|
// We did not find a cached attribute. Try to calculate the attribute |
|
// dynamically, using the descriptor as an argument. |
|
PyErr_Clear(); |
|
ret = PyUpb_MessageMeta_GetDynamicAttr(self, name); |
|
|
|
if (ret) { |
|
PyObject_SetAttr(self, name, ret); |
|
PyErr_Clear(); |
|
return ret; |
|
} |
|
|
|
PyErr_SetObject(PyExc_AttributeError, name); |
|
return NULL; |
|
} |
|
|
|
static PyType_Slot PyUpb_MessageMeta_Slots[] = { |
|
{Py_tp_new, PyUpb_MessageMeta_New}, |
|
{Py_tp_dealloc, PyUpb_MessageMeta_Dealloc}, |
|
{Py_tp_getattro, PyUpb_MessageMeta_GetAttr}, |
|
{0, NULL}}; |
|
|
|
static PyType_Spec PyUpb_MessageMeta_Spec = { |
|
PYUPB_MODULE_NAME ".MessageMeta", // tp_name |
|
0, // To be filled in by size of base // tp_basicsize |
|
0, // tp_itemsize |
|
// TODO(haberman): remove BASETYPE, Python should just use MessageMeta |
|
// directly instead of subclassing it. |
|
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, // tp_flags |
|
PyUpb_MessageMeta_Slots, |
|
}; |
|
|
|
static PyObject* PyUpb_MessageMeta_CreateType(void) { |
|
PyObject* bases = Py_BuildValue("(O)", &PyType_Type); |
|
if (!bases) return NULL; |
|
PyUpb_MessageMeta_Spec.basicsize = |
|
cpython_bits.type_basicsize + sizeof(PyUpb_MessageMeta); |
|
PyObject* type = PyType_FromSpecWithBases(&PyUpb_MessageMeta_Spec, bases); |
|
Py_DECREF(bases); |
|
return type; |
|
} |
|
|
|
bool PyUpb_InitMessage(PyObject* m) { |
|
if (!PyUpb_CPythonBits_Init(&cpython_bits)) return false; |
|
PyObject* message_meta_type = PyUpb_MessageMeta_CreateType(); |
|
|
|
PyUpb_ModuleState* state = PyUpb_ModuleState_GetFromModule(m); |
|
state->cmessage_type = PyUpb_AddClass(m, &PyUpb_CMessage_Spec); |
|
state->message_meta_type = (PyTypeObject*)message_meta_type; |
|
|
|
if (!state->cmessage_type || !state->message_meta_type) return false; |
|
if (PyModule_AddObject(m, "MessageMeta", message_meta_type)) return false; |
|
state->listfields_item_key = PyObject_GetAttrString( |
|
(PyObject*)state->cmessage_type, "_ListFieldsItemKey"); |
|
|
|
PyObject* mod = PyImport_ImportModule("google.protobuf.message"); |
|
if (mod == NULL) return false; |
|
|
|
state->encode_error_class = PyObject_GetAttrString(mod, "EncodeError"); |
|
state->decode_error_class = PyObject_GetAttrString(mod, "DecodeError"); |
|
state->message_class = PyObject_GetAttrString(mod, "Message"); |
|
Py_DECREF(mod); |
|
|
|
PyObject* enum_type_wrapper = |
|
PyImport_ImportModule("google.protobuf.internal.enum_type_wrapper"); |
|
if (enum_type_wrapper == NULL) return false; |
|
|
|
state->enum_type_wrapper_class = |
|
PyObject_GetAttrString(enum_type_wrapper, "EnumTypeWrapper"); |
|
Py_DECREF(enum_type_wrapper); |
|
|
|
if (!state->encode_error_class || !state->decode_error_class || |
|
!state->message_class || !state->listfields_item_key || |
|
!state->enum_type_wrapper_class) { |
|
return false; |
|
} |
|
|
|
return true; |
|
}
|
|
|