|
|
|
/*
|
|
|
|
* 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/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_msgdef* 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
|
|
|
|
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;
|
|
|
|
|
|
|
|
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_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 && bits->type_getattro && bits->type_setattro);
|
|
|
|
|
|
|
|
#ifndef Py_LIMITED_API
|
|
|
|
assert(bits->type_new == PyType_Type.tp_new);
|
|
|
|
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_msgdef*
|
|
|
|
union {
|
|
|
|
// when def is msgdef, the data for this msg.
|
|
|
|
upb_msg* 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_IsUnset(PyUpb_CMessage* msg) { return msg->def & 1; }
|
|
|
|
|
|
|
|
const upb_fielddef* PyUpb_CMessage_GetFieldDef(PyUpb_CMessage* msg) {
|
|
|
|
assert(PyUpb_CMessage_IsUnset(msg));
|
|
|
|
return (void*)(msg->def & ~(uintptr_t)1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const upb_msgdef* _PyUpb_CMessage_GetMsgdef(PyUpb_CMessage* msg) {
|
|
|
|
return PyUpb_CMessage_IsUnset(msg)
|
|
|
|
? upb_fielddef_msgsubdef(PyUpb_CMessage_GetFieldDef(msg))
|
|
|
|
: (void*)msg->def;
|
|
|
|
}
|
|
|
|
|
|
|
|
const upb_msgdef* PyUpb_CMessage_GetMsgdef(PyObject* self) {
|
|
|
|
return _PyUpb_CMessage_GetMsgdef((PyUpb_CMessage*)self);
|
|
|
|
}
|
|
|
|
|
|
|
|
static upb_msg* PyUpb_CMessage_GetMsg(PyUpb_CMessage* self) {
|
|
|
|
assert(!PyUpb_CMessage_IsUnset(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_Check(PyObject* self) {
|
|
|
|
if (!PyUpb_CMessage_TryCheck(self)) {
|
|
|
|
PyErr_Format(PyExc_TypeError, "Expected a message object, but got %R.",
|
|
|
|
self);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
upb_msg* PyUpb_CMessage_GetIfWritable(PyObject* _self) {
|
|
|
|
PyUpb_CMessage* self = (void*)_self;
|
|
|
|
return PyUpb_CMessage_IsUnset(self) ? NULL : self->ptr.msg;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject* PyUpb_CMessage_New(PyObject* cls, PyObject* unused_args,
|
|
|
|
PyObject* unused_kwargs) {
|
|
|
|
const upb_msgdef* 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_msg_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 = PyUnicode_AsUTF8AndSize(py_name, &size);
|
|
|
|
if (!name) return NULL;
|
|
|
|
const upb_msgdef* msgdef = _PyUpb_CMessage_GetMsgdef(self);
|
|
|
|
|
|
|
|
if (!upb_msgdef_lookupname(msgdef, name, size, f, o)) {
|
|
|
|
if (exc_type) {
|
|
|
|
PyErr_Format(exc_type,
|
|
|
|
"Protocol message %s has no field or oneof named %s.",
|
|
|
|
upb_msgdef_fullname(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_msgdef* entry_m = upb_fielddef_msgsubdef(f);
|
|
|
|
const upb_fielddef* val_f = upb_msgdef_field(entry_m, 1);
|
|
|
|
PyObject* it = NULL;
|
|
|
|
PyObject* tmp = NULL;
|
|
|
|
int ret = -1;
|
|
|
|
if (upb_fielddef_issubmsg(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_AssureWritable(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) {
|
|
|
|
PyObject* repeated = PyUpb_CMessage_GetAttr(_self, name);
|
|
|
|
PyObject* tmp = PyUpb_RepeatedContainer_Extend(repeated, value);
|
|
|
|
if (!tmp) return false;
|
|
|
|
Py_DECREF(tmp);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
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 {
|
|
|
|
assert(!PyErr_Occurred());
|
|
|
|
ok = PyUpb_CMessage_InitAttributes(submsg, NULL, value) >= 0;
|
|
|
|
}
|
|
|
|
Py_DECREF(submsg);
|
|
|
|
return ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool PyUpb_CMessage_InitScalarAttribute(upb_msg* msg,
|
|
|
|
const upb_fielddef* f,
|
|
|
|
PyObject* value,
|
|
|
|
upb_arena* arena) {
|
|
|
|
upb_msgval msgval;
|
|
|
|
assert(!PyErr_Occurred());
|
|
|
|
if (!PyUpb_PyToUpb(value, f, &msgval, arena)) {
|
|
|
|
PyErr_Clear();
|
|
|
|
PyErr_Format(PyExc_ValueError, "Error initializing field %s",
|
|
|
|
upb_fielddef_fullname(f));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
upb_msg_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_AssureWritable(self);
|
|
|
|
upb_msg* 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_isseq(f)) {
|
|
|
|
if (!PyUpb_CMessage_InitRepeatedAttribute(_self, name, value)) return -1;
|
|
|
|
} else if (upb_fielddef_issubmsg(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_msgdef* sub_m = upb_fielddef_msgsubdef(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_msgdef* m1_msgdef = _PyUpb_CMessage_GetMsgdef(m1);
|
|
|
|
#ifndef NDEBUG
|
|
|
|
const upb_msgdef* m2_msgdef = _PyUpb_CMessage_GetMsgdef(m2);
|
|
|
|
assert(m1_msgdef == m2_msgdef);
|
|
|
|
#endif
|
|
|
|
const upb_msg* m1_msg = PyUpb_CMessage_GetIfWritable((PyObject*)m1);
|
|
|
|
const upb_msg* m2_msg = PyUpb_CMessage_GetIfWritable(_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_msg_new(upb_fielddef_msgsubdef(f), arena);
|
|
|
|
m->def = (uintptr_t)upb_fielddef_msgsubdef(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_msgval msgval = {.msg_val = PyUpb_CMessage_GetMsg(child)};
|
|
|
|
upb_msg_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_AssureWritable()
|
|
|
|
*
|
|
|
|
* 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_IsUnset(self) is false
|
|
|
|
*/
|
|
|
|
void PyUpb_CMessage_AssureWritable(PyUpb_CMessage* self) {
|
|
|
|
if (!PyUpb_CMessage_IsUnset(self)) return;
|
|
|
|
upb_arena* arena = PyUpb_Arena_Get(self->arena);
|
|
|
|
|
|
|
|
// This is a non-present message. We need to create a real upb_msg 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_IsUnset(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_msg* msg) {
|
|
|
|
assert(f == PyUpb_CMessage_GetFieldDef(self));
|
|
|
|
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_msgsubdef(f);
|
|
|
|
PyUpb_CMessage_SyncSubobjs(self);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* PyUpb_CMessage_SyncSubobjs()
|
|
|
|
*
|
|
|
|
* This operation must be invoked whenever the underlying upb_msg has been
|
|
|
|
* mutated directly in C. This will attach any newly-present field data
|
|
|
|
* to previously returned "empty" 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_msg* 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_SwitchToSet() 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_msg_has(msg, f)) continue;
|
|
|
|
upb_msgval msgval = upb_msg_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_isseq(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_msg*)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_IsUnset(self)) {
|
|
|
|
return PyUnicode_FromStringAndSize(NULL, 0);
|
|
|
|
}
|
|
|
|
upb_msg* msg = PyUpb_CMessage_GetMsg(self);
|
|
|
|
const upb_msgdef* msgdef = _PyUpb_CMessage_GetMsgdef(self);
|
|
|
|
const upb_symtab* symtab = upb_filedef_symtab(upb_msgdef_file(msgdef));
|
|
|
|
char buf[1024];
|
|
|
|
int options = UPB_TXTENC_SKIPUNKNOWN;
|
|
|
|
size_t size = upb_text_encode(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_text_encode(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_msgval subobj) {
|
|
|
|
PyUpb_CMessage* self = (void*)_self;
|
|
|
|
PyUpb_CMessage_AssureWritable(self);
|
|
|
|
PyUpb_CMessage_CacheDelete(_self, f);
|
|
|
|
upb_msg_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_IsUnset(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_msg* u_msg, const upb_msgdef* 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
PyObject* PyUpb_CMessage_GetUnsetWrapper(PyUpb_CMessage* self,
|
|
|
|
const upb_fielddef* field) {
|
|
|
|
PyObject* _self = (void*)self;
|
|
|
|
// Non-present messages return magical "empty" messages that point to their
|
|
|
|
// parent, but will materialize into real messages if any fields are
|
|
|
|
// assigned.
|
|
|
|
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_isseq(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_IsUnset(self));
|
|
|
|
upb_mutmsgval mutval =
|
|
|
|
upb_msg_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_msgval val;
|
|
|
|
if (PyUpb_CMessage_IsUnset(self)) {
|
|
|
|
// Unset message always returns default values.
|
|
|
|
val = upb_fielddef_default(field);
|
|
|
|
} else {
|
|
|
|
val = upb_msg_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_issubmsg(field);
|
|
|
|
bool seq = upb_fielddef_isseq(field);
|
|
|
|
|
|
|
|
if ((PyUpb_CMessage_IsUnset(self) && (submsg || seq)) ||
|
|
|
|
(submsg && !upb_msg_has(self->ptr.msg, field))) {
|
|
|
|
return PyUpb_CMessage_GetUnsetWrapper(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) {
|
|
|
|
PyUpb_CMessage* self = (void*)_self;
|
|
|
|
if (upb_fielddef_issubmsg(field) || upb_fielddef_isseq(field)) {
|
|
|
|
PyErr_Format(PyExc_AttributeError,
|
|
|
|
"Assignment not allowed to message, map, or repeated "
|
|
|
|
"field \"%s\" in protocol message object.",
|
|
|
|
upb_fielddef_name(field));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
PyUpb_CMessage_AssureWritable(self);
|
|
|
|
|
|
|
|
upb_msgval val;
|
|
|
|
upb_arena* arena = PyUpb_Arena_Get(self->arena);
|
|
|
|
if (!PyUpb_PyToUpb(value, field, &val, arena)) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
upb_msg_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;
|
|
|
|
|
|
|
|
// If the attribute wasn't found, look for attributes on the class. But if a
|
|
|
|
// different kind of error (other than AttributeError) was found, return that.
|
|
|
|
if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
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_IsUnset(self)) Py_RETURN_FALSE;
|
|
|
|
|
|
|
|
return PyBool_FromLong(field ? upb_msg_has(self->ptr.msg, field)
|
|
|
|
: upb_msg_whichoneof(self->ptr.msg, oneof) != NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject* PyUpb_CMessage_ListFields(PyObject* _self, PyObject* arg) {
|
|
|
|
PyObject* list = PyList_New(0);
|
|
|
|
upb_msg* msg = PyUpb_CMessage_GetIfWritable(_self);
|
|
|
|
if (!msg) return list;
|
|
|
|
|
|
|
|
size_t iter1 = UPB_MSG_BEGIN;
|
|
|
|
const upb_msgdef* m = PyUpb_CMessage_GetMsgdef(_self);
|
|
|
|
const upb_symtab* symtab = upb_filedef_symtab(upb_msgdef_file(m));
|
|
|
|
const upb_fielddef* f;
|
|
|
|
PyObject* field_desc = NULL;
|
|
|
|
PyObject* py_val = NULL;
|
|
|
|
PyObject* tuple = NULL;
|
|
|
|
upb_msgval val;
|
|
|
|
while (upb_msg_next(msg, m, symtab, &f, &val, &iter1)) {
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
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_AssureWritable(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_AssureWritable(self);
|
|
|
|
const upb_msgdef* msgdef = _PyUpb_CMessage_GetMsgdef(self);
|
|
|
|
const upb_filedef* file = upb_msgdef_file(msgdef);
|
|
|
|
const upb_extreg* extreg = upb_symtab_extreg(upb_filedef_symtab(file));
|
|
|
|
const upb_msglayout* layout = upb_msgdef_layout(msgdef);
|
|
|
|
upb_arena* arena = PyUpb_Arena_Get(self->arena);
|
|
|
|
upb_DecodeStatus status =
|
|
|
|
_upb_decode(buf, size, self->ptr.msg, layout, extreg, 0, arena);
|
|
|
|
Py_XDECREF(bytes);
|
|
|
|
if (status != kUpb_DecodeStatus_Ok) {
|
|
|
|
PyUpb_ModuleState* state = PyUpb_ModuleState_Get();
|
|
|
|
PyErr_Format(state->decode_error_class, "Error parsing message");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
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_AssureWritable(self);
|
|
|
|
const upb_msgdef* msgdef = _PyUpb_CMessage_GetMsgdef(self);
|
|
|
|
upb_msg_clear(self->ptr.msg, msgdef);
|
|
|
|
Py_RETURN_NONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void PyUpb_CMessage_AbandonField(PyUpb_CMessage* self,
|
|
|
|
const upb_fielddef* f) {
|
|
|
|
if (self->unset_subobj_map && upb_fielddef_issubmsg(f) &&
|
|
|
|
!upb_fielddef_isseq(f)) {
|
|
|
|
PyObject* sub = PyUpb_WeakMap_Get(self->unset_subobj_map, f);
|
|
|
|
if (sub) {
|
|
|
|
PyUpb_CMessage_AssureWritable((PyUpb_CMessage*)sub);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject* PyUpb_CMessage_ClearExtension(PyUpb_CMessage* self,
|
|
|
|
PyObject* arg) {
|
|
|
|
PyUpb_CMessage_AssureWritable(self);
|
|
|
|
const upb_msgdef* msgdef = _PyUpb_CMessage_GetMsgdef(self);
|
|
|
|
const upb_fielddef* f = PyUpb_FieldDescriptor_GetDef(arg);
|
|
|
|
if (!f) return NULL;
|
|
|
|
if (upb_fielddef_containingtype(f) != msgdef) {
|
|
|
|
PyErr_Format(PyExc_ValueError, "Extension doesn't match (%s vs %s)",
|
|
|
|
upb_msgdef_fullname(msgdef), upb_fielddef_fullname(f));
|
|
|
|
}
|
|
|
|
PyUpb_CMessage_AbandonField(self, f);
|
|
|
|
upb_msg_clearfield(self->ptr.msg, f);
|
|
|
|
Py_RETURN_NONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject* PyUpb_CMessage_ClearField(PyUpb_CMessage* self,
|
|
|
|
PyObject* arg) {
|
|
|
|
// We always need AssureWritable() here (even for an unset message) to
|
|
|
|
// preserve behavior like:
|
|
|
|
// msg = FooMessage()
|
|
|
|
// msg.foo.Clear()
|
|
|
|
// assert msg.HasField("foo")
|
|
|
|
PyUpb_CMessage_AssureWritable(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_msg_whichoneof(self->ptr.msg, o);
|
|
|
|
if (f) upb_msg_clearfield(self->ptr.msg, f);
|
|
|
|
|
|
|
|
if (upb_fielddef_ismap(f)) {
|
|
|
|
// We have to invalidate any existing iterator over this map.
|
|
|
|
PyObject* obj = NULL;
|
|
|
|
if (self->unset_subobj_map) {
|
|
|
|
obj = PyUpb_WeakMap_Get(self->unset_subobj_map, f);
|
|
|
|
}
|
|
|
|
if (!obj) {
|
|
|
|
upb_msg* msg = PyUpb_CMessage_GetMsg(self);
|
|
|
|
upb_msgval msgval = upb_msg_get(msg, f);
|
|
|
|
obj = PyUpb_ObjCache_Get(msgval.map_val);
|
|
|
|
}
|
|
|
|
if (obj) {
|
|
|
|
PyUpb_MapContainer_Invalidate(obj);
|
|
|
|
Py_DECREF(obj);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
PyUpb_CMessage_AbandonField(self, f);
|
|
|
|
|
|
|
|
Py_RETURN_NONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject* PyUpb_CMessage_DiscardUnknownFields(PyUpb_CMessage* self,
|
|
|
|
PyObject* arg) {
|
|
|
|
PyUpb_CMessage_AssureWritable(self);
|
|
|
|
const upb_msgdef* msgdef = _PyUpb_CMessage_GetMsgdef(self);
|
|
|
|
upb_msg_discardunknown(self->ptr.msg, msgdef, 64);
|
|
|
|
Py_RETURN_NONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject* PyUpb_CMessage_FindInitializationErrors(PyObject* _self,
|
|
|
|
PyObject* arg) {
|
|
|
|
PyUpb_CMessage* self = (void*)_self;
|
|
|
|
upb_msg* msg = PyUpb_CMessage_GetIfWritable(_self);
|
|
|
|
if (!msg) return PyList_New(0);
|
|
|
|
const upb_msgdef* msgdef = _PyUpb_CMessage_GetMsgdef(self);
|
|
|
|
const upb_symtab* ext_pool = NULL; // TODO
|
|
|
|
upb_FieldPathEntry* fields;
|
|
|
|
PyObject* ret = PyList_New(0);
|
|
|
|
if (upb_util_HasUnsetRequired(msg, msgdef, ext_pool, &fields)) {
|
|
|
|
char* buf = NULL;
|
|
|
|
size_t size = 0;
|
|
|
|
size_t i = 0;
|
|
|
|
while (fields) {
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
PyList_SetItem(ret, i, PyUnicode_FromString(buf));
|
|
|
|
}
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject* PyUpb_CMessage_HasExtension(PyObject* _self,
|
|
|
|
PyObject* ext_desc) {
|
|
|
|
upb_msg* msg = PyUpb_CMessage_GetIfWritable(_self);
|
|
|
|
const upb_fielddef* f = PyUpb_FieldDescriptor_GetDef(ext_desc);
|
|
|
|
if (!f) return NULL;
|
|
|
|
if (!msg) Py_RETURN_FALSE;
|
|
|
|
return PyBool_FromLong(upb_msg_has(msg, f));
|
|
|
|
}
|
|
|
|
|
|
|
|
PyObject* PyUpb_CMessage_SerializeInternal(PyObject* _self, PyObject* args,
|
|
|
|
PyObject* kwargs,
|
|
|
|
bool check_required) {
|
|
|
|
PyUpb_CMessage* self = (void*)_self;
|
|
|
|
if (!PyUpb_CMessage_Check((PyObject*)self)) return NULL;
|
|
|
|
static const char* kwlist[] = {"deterministic", NULL};
|
|
|
|
int deterministic = 0;
|
|
|
|
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|p", (char**)(kwlist),
|
|
|
|
&deterministic)) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (PyUpb_CMessage_IsUnset(self)) {
|
|
|
|
return PyBytes_FromStringAndSize(NULL, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
upb_arena* arena = upb_arena_new();
|
|
|
|
const upb_msgdef* msgdef = _PyUpb_CMessage_GetMsgdef(self);
|
|
|
|
const upb_msglayout* layout = upb_msgdef_layout(msgdef);
|
|
|
|
size_t size = 0;
|
|
|
|
int options = 0;
|
|
|
|
if (check_required) options |= UPB_ENCODE_CHECKREQUIRED;
|
|
|
|
if (deterministic) options |= UPB_ENCODE_DETERMINISTIC;
|
|
|
|
char* pb = upb_encode_ex(self->ptr.msg, layout, options, arena, &size);
|
|
|
|
PyObject* ret = NULL;
|
|
|
|
|
|
|
|
if (!pb) {
|
|
|
|
PyUpb_ModuleState* state = PyUpb_ModuleState_Get();
|
|
|
|
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_msg* msg = PyUpb_CMessage_GetIfWritable(_self);
|
|
|
|
if (!msg) Py_RETURN_NONE;
|
|
|
|
const upb_fielddef* f = upb_msg_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_msgdef* m = _PyUpb_CMessage_GetMsgdef(self);
|
|
|
|
if (upb_msgdef_extrangecount(m) == 0) {
|
|
|
|
PyErr_SetNone(PyExc_AttributeError);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO(haberman): re-enable when ExtensionDict is checked in.
|
|
|
|
// self->ext_dict = PyUpb_ExtensionDict_New(_self);
|
|
|
|
// return self->ext_dict;
|
|
|
|
PyErr_SetString(PyExc_NotImplementedError, "get extension dict");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PyGetSetDef PyUpb_CMessage_Getters[] = {
|
|
|
|
{"Extensions", PyUpb_CMessage_GetExtensionDict, NULL, "Extension dict"},
|
|
|
|
/*
|
|
|
|
{"_extensions_by_name", (getter)GetExtensionsByName, NULL},
|
|
|
|
{"_extensions_by_number", (getter)GetExtensionsByNumber, NULL},
|
|
|
|
*/
|
|
|
|
{NULL}};
|
|
|
|
|
|
|
|
static PyMethodDef PyUpb_CMessage_Methods[] = {
|
|
|
|
// 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", (PyCFunction)PyUpb_CMessage_ClearExtension, METH_O,
|
|
|
|
"Clears a message field."},
|
|
|
|
{"ClearField", (PyCFunction)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",
|
|
|
|
(PyCFunction)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."},
|
|
|
|
// TODO(https://github.com/protocolbuffers/upb/issues/459)
|
|
|
|
//{ "IsInitialized", (PyCFunction)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."},
|
|
|
|
// TODO(https://github.com/protocolbuffers/upb/issues/459)
|
|
|
|
//{ "_CheckCalledFromGeneratedFile",
|
|
|
|
//(PyCFunction)_CheckCalledFromGeneratedFile,
|
|
|
|
// METH_NOARGS | METH_STATIC,
|
|
|
|
// "Raises TypeError if the caller is not in a _pb2.py file."},
|
|
|
|
{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_msglayout* 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_msgdef* 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_msgdef* msgdef = PyUpb_Descriptor_GetDef(py_descriptor);
|
|
|
|
assert(msgdef);
|
|
|
|
assert(!PyUpb_ObjCache_Get(upb_msgdef_layout(msgdef)));
|
|
|
|
|
|
|
|
PyObject* slots = PyTuple_New(0);
|
|
|
|
if (PyDict_SetItemString(dict, "__slots__", slots) < 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_msgdef_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_msgdef_layout(msgdef);
|
|
|
|
Py_INCREF(meta->py_message_descriptor);
|
|
|
|
|
|
|
|
PyUpb_ObjCache_Add(upb_msgdef_layout(msgdef), 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_msgdef* m = PyUpb_Descriptor_GetDef(py_descriptor);
|
|
|
|
PyObject* ret = PyUpb_ObjCache_Get(upb_msgdef_layout(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);
|
|
|
|
PyUpb_Dealloc(self);
|
|
|
|
}
|
|
|
|
|
|
|
|
static PyObject* PyUpb_MessageMeta_GetDynamicAttr(PyObject* self,
|
|
|
|
PyObject* name) {
|
|
|
|
const char* name_buf = PyUpb_GetStrData(name);
|
|
|
|
const upb_msgdef* msgdef = PyUpb_MessageMeta_GetMsgdef(self);
|
|
|
|
const upb_filedef* filedef = upb_msgdef_file(msgdef);
|
|
|
|
const upb_symtab* symtab = upb_filedef_symtab(filedef);
|
|
|
|
|
|
|
|
PyObject* py_key =
|
|
|
|
PyBytes_FromFormat("%s.%s", upb_msgdef_fullname(msgdef), name_buf);
|
|
|
|
const char* key = PyUpb_GetStrData(py_key);
|
|
|
|
PyObject* ret = NULL;
|
|
|
|
const upb_msgdef* nested = upb_symtab_lookupmsg(symtab, key);
|
|
|
|
const upb_enumdef* enumdef;
|
|
|
|
const upb_enumvaldef* enumval;
|
|
|
|
const upb_fielddef* ext;
|
|
|
|
|
|
|
|
if (nested) {
|
|
|
|
ret = PyUpb_Descriptor_GetClass(nested);
|
|
|
|
} else if ((enumdef = upb_symtab_lookupenum(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_symtab_lookupenumval(symtab, key))) {
|
|
|
|
ret = PyLong_FromLong(upb_enumvaldef_number(enumval));
|
|
|
|
} else if ((ext = upb_symtab_lookupext(symtab, key))) {
|
|
|
|
ret = PyUpb_FieldDescriptor_Get(ext);
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO(haberman): *_FIELD_NUMBER attributes? Haven't seen any failing
|
|
|
|
// tests for these yet.
|
|
|
|
|
|
|
|
Py_DECREF(py_key);
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
if (!state->encode_error_class || !state->decode_error_class ||
|
|
|
|
!state->message_class) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|