blob: 05751a0f894f1b15b5cc9cd700cf1ebbaab221b4 [file] [log] [blame]
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google LLC. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
#include "python/descriptor_containers.h"
#include "python/descriptor.h"
#include "python/protobuf.h"
#include "upb/reflection/def.h"
// Implements __repr__ as str(dict(self)).
static PyObject* PyUpb_DescriptorMap_Repr(PyObject* _self) {
PyObject* dict = PyDict_New();
PyObject* ret = NULL;
if (!dict) goto err;
if (PyDict_Merge(dict, _self, 1) != 0) goto err;
ret = PyObject_Str(dict);
err:
Py_XDECREF(dict);
return ret;
}
// -----------------------------------------------------------------------------
// ByNameIterator
// -----------------------------------------------------------------------------
typedef struct {
PyObject_HEAD;
const PyUpb_ByNameMap_Funcs* funcs;
const void* parent; // upb_MessageDef*, upb_DefPool*, etc.
PyObject* parent_obj; // Python object that keeps parent alive, we own a ref.
int index; // Current iterator index.
} PyUpb_ByNameIterator;
static PyUpb_ByNameIterator* PyUpb_ByNameIterator_Self(PyObject* obj) {
assert(Py_TYPE(obj) == PyUpb_ModuleState_Get()->by_name_iterator_type);
return (PyUpb_ByNameIterator*)obj;
}
static void PyUpb_ByNameIterator_Dealloc(PyObject* _self) {
PyUpb_ByNameIterator* self = PyUpb_ByNameIterator_Self(_self);
Py_DECREF(self->parent_obj);
PyUpb_Dealloc(self);
}
static PyObject* PyUpb_ByNameIterator_New(const PyUpb_ByNameMap_Funcs* funcs,
const void* parent,
PyObject* parent_obj) {
PyUpb_ModuleState* s = PyUpb_ModuleState_Get();
PyUpb_ByNameIterator* iter =
(void*)PyType_GenericAlloc(s->by_name_iterator_type, 0);
iter->funcs = funcs;
iter->parent = parent;
iter->parent_obj = parent_obj;
iter->index = 0;
Py_INCREF(iter->parent_obj);
return &iter->ob_base;
}
static PyObject* PyUpb_ByNameIterator_IterNext(PyObject* _self) {
PyUpb_ByNameIterator* self = PyUpb_ByNameIterator_Self(_self);
int size = self->funcs->base.get_elem_count(self->parent);
if (self->index >= size) return NULL;
const void* elem = self->funcs->base.index(self->parent, self->index);
self->index++;
return PyUnicode_FromString(self->funcs->get_elem_name(elem));
}
static PyType_Slot PyUpb_ByNameIterator_Slots[] = {
{Py_tp_dealloc, PyUpb_ByNameIterator_Dealloc},
{Py_tp_iter, PyObject_SelfIter},
{Py_tp_iternext, PyUpb_ByNameIterator_IterNext},
{0, NULL}};
static PyType_Spec PyUpb_ByNameIterator_Spec = {
PYUPB_MODULE_NAME "._ByNameIterator", // tp_name
sizeof(PyUpb_ByNameIterator), // tp_basicsize
0, // tp_itemsize
Py_TPFLAGS_DEFAULT, // tp_flags
PyUpb_ByNameIterator_Slots,
};
// -----------------------------------------------------------------------------
// ByNumberIterator
// -----------------------------------------------------------------------------
typedef struct {
PyObject_HEAD;
const PyUpb_ByNumberMap_Funcs* funcs;
const void* parent; // upb_MessageDef*, upb_DefPool*, etc.
PyObject* parent_obj; // Python object that keeps parent alive, we own a ref.
int index; // Current iterator index.
} PyUpb_ByNumberIterator;
static PyUpb_ByNumberIterator* PyUpb_ByNumberIterator_Self(PyObject* obj) {
assert(Py_TYPE(obj) == PyUpb_ModuleState_Get()->by_number_iterator_type);
return (PyUpb_ByNumberIterator*)obj;
}
static void PyUpb_ByNumberIterator_Dealloc(PyObject* _self) {
PyUpb_ByNumberIterator* self = PyUpb_ByNumberIterator_Self(_self);
Py_DECREF(self->parent_obj);
PyUpb_Dealloc(self);
}
static PyObject* PyUpb_ByNumberIterator_New(
const PyUpb_ByNumberMap_Funcs* funcs, const void* parent,
PyObject* parent_obj) {
PyUpb_ModuleState* s = PyUpb_ModuleState_Get();
PyUpb_ByNumberIterator* iter =
(void*)PyType_GenericAlloc(s->by_number_iterator_type, 0);
iter->funcs = funcs;
iter->parent = parent;
iter->parent_obj = parent_obj;
iter->index = 0;
Py_INCREF(iter->parent_obj);
return &iter->ob_base;
}
static PyObject* PyUpb_ByNumberIterator_IterNext(PyObject* _self) {
PyUpb_ByNumberIterator* self = PyUpb_ByNumberIterator_Self(_self);
int size = self->funcs->base.get_elem_count(self->parent);
if (self->index >= size) return NULL;
const void* elem = self->funcs->base.index(self->parent, self->index);
self->index++;
return PyLong_FromLong(self->funcs->get_elem_num(elem));
}
static PyType_Slot PyUpb_ByNumberIterator_Slots[] = {
{Py_tp_dealloc, PyUpb_ByNumberIterator_Dealloc},
{Py_tp_iter, PyObject_SelfIter},
{Py_tp_iternext, PyUpb_ByNumberIterator_IterNext},
{0, NULL}};
static PyType_Spec PyUpb_ByNumberIterator_Spec = {
PYUPB_MODULE_NAME "._ByNumberIterator", // tp_name
sizeof(PyUpb_ByNumberIterator), // tp_basicsize
0, // tp_itemsize
Py_TPFLAGS_DEFAULT, // tp_flags
PyUpb_ByNumberIterator_Slots,
};
// -----------------------------------------------------------------------------
// GenericSequence
// -----------------------------------------------------------------------------
typedef struct {
PyObject_HEAD;
const PyUpb_GenericSequence_Funcs* funcs;
const void* parent; // upb_MessageDef*, upb_DefPool*, etc.
PyObject* parent_obj; // Python object that keeps parent alive, we own a ref.
} PyUpb_GenericSequence;
PyUpb_GenericSequence* PyUpb_GenericSequence_Self(PyObject* obj) {
assert(Py_TYPE(obj) == PyUpb_ModuleState_Get()->generic_sequence_type);
return (PyUpb_GenericSequence*)obj;
}
static void PyUpb_GenericSequence_Dealloc(PyObject* _self) {
PyUpb_GenericSequence* self = PyUpb_GenericSequence_Self(_self);
Py_CLEAR(self->parent_obj);
PyUpb_Dealloc(self);
}
PyObject* PyUpb_GenericSequence_New(const PyUpb_GenericSequence_Funcs* funcs,
const void* parent, PyObject* parent_obj) {
PyUpb_ModuleState* s = PyUpb_ModuleState_Get();
PyUpb_GenericSequence* seq =
(PyUpb_GenericSequence*)PyType_GenericAlloc(s->generic_sequence_type, 0);
seq->funcs = funcs;
seq->parent = parent;
seq->parent_obj = parent_obj;
Py_INCREF(parent_obj);
return &seq->ob_base;
}
static Py_ssize_t PyUpb_GenericSequence_Length(PyObject* _self) {
PyUpb_GenericSequence* self = PyUpb_GenericSequence_Self(_self);
return self->funcs->get_elem_count(self->parent);
}
static PyObject* PyUpb_GenericSequence_GetItem(PyObject* _self,
Py_ssize_t index) {
PyUpb_GenericSequence* self = PyUpb_GenericSequence_Self(_self);
Py_ssize_t size = self->funcs->get_elem_count(self->parent);
if (index < 0) {
index += size;
}
if (index < 0 || index >= size) {
PyErr_Format(PyExc_IndexError, "list index (%zd) out of range", index);
return NULL;
}
const void* elem = self->funcs->index(self->parent, index);
return self->funcs->get_elem_wrapper(elem);
}
// A sequence container can only be equal to another sequence container, or (for
// backward compatibility) to a list containing the same items.
// Returns 1 if equal, 0 if unequal, -1 on error.
static int PyUpb_GenericSequence_IsEqual(PyUpb_GenericSequence* self,
PyObject* other) {
// Check the identity of C++ pointers.
if (PyObject_TypeCheck(other, Py_TYPE(self))) {
PyUpb_GenericSequence* other_seq = (void*)other;
return self->parent == other_seq->parent && self->funcs == other_seq->funcs;
}
if (!PyList_Check(other)) return 0;
// return list(self) == other
// We can clamp `i` to int because GenericSequence uses int for size (this
// is useful when we do int iteration below).
int n = PyUpb_GenericSequence_Length((PyObject*)self);
if ((Py_ssize_t)n != PyList_Size(other)) {
return false;
}
PyObject* item1;
for (int i = 0; i < n; i++) {
item1 = PyUpb_GenericSequence_GetItem((PyObject*)self, i);
PyObject* item2 = PyList_GetItem(other, i);
if (!item1 || !item2) goto error;
int cmp = PyObject_RichCompareBool(item1, item2, Py_EQ);
Py_DECREF(item1);
if (cmp != 1) return cmp;
}
// All items were found and equal
return 1;
error:
Py_XDECREF(item1);
return -1;
}
static PyObject* PyUpb_GenericSequence_RichCompare(PyObject* _self,
PyObject* other, int opid) {
PyUpb_GenericSequence* self = PyUpb_GenericSequence_Self(_self);
if (opid != Py_EQ && opid != Py_NE) {
Py_RETURN_NOTIMPLEMENTED;
}
bool ret = PyUpb_GenericSequence_IsEqual(self, other);
if (opid == Py_NE) ret = !ret;
return PyBool_FromLong(ret);
}
static PyObject* PyUpb_GenericSequence_Subscript(PyObject* _self,
PyObject* item) {
PyUpb_GenericSequence* self = PyUpb_GenericSequence_Self(_self);
Py_ssize_t size = self->funcs->get_elem_count(self->parent);
Py_ssize_t idx, count, step;
if (!PyUpb_IndexToRange(item, size, &idx, &count, &step)) return NULL;
if (step == 0) {
return PyUpb_GenericSequence_GetItem(_self, idx);
} else {
PyObject* list = PyList_New(count);
for (Py_ssize_t i = 0; i < count; i++, idx += step) {
const void* elem = self->funcs->index(self->parent, idx);
PyList_SetItem(list, i, self->funcs->get_elem_wrapper(elem));
}
return list;
}
}
// Linear search. Could optimize this in some cases (defs that have index),
// but not all (FileDescriptor.dependencies).
static int PyUpb_GenericSequence_Find(PyObject* _self, PyObject* item) {
PyUpb_GenericSequence* self = PyUpb_GenericSequence_Self(_self);
const void* item_ptr = PyUpb_AnyDescriptor_GetDef(item);
int count = self->funcs->get_elem_count(self->parent);
for (int i = 0; i < count; i++) {
if (self->funcs->index(self->parent, i) == item_ptr) {
return i;
}
}
return -1;
}
static PyObject* PyUpb_GenericSequence_Index(PyObject* self, PyObject* item) {
int position = PyUpb_GenericSequence_Find(self, item);
if (position < 0) {
PyErr_SetNone(PyExc_ValueError);
return NULL;
} else {
return PyLong_FromLong(position);
}
}
static PyObject* PyUpb_GenericSequence_Count(PyObject* _self, PyObject* item) {
PyUpb_GenericSequence* self = PyUpb_GenericSequence_Self(_self);
const void* item_ptr = PyUpb_AnyDescriptor_GetDef(item);
int n = self->funcs->get_elem_count(self->parent);
int count = 0;
for (int i = 0; i < n; i++) {
if (self->funcs->index(self->parent, i) == item_ptr) {
count++;
}
}
return PyLong_FromLong(count);
}
static PyObject* PyUpb_GenericSequence_Append(PyObject* self, PyObject* args) {
PyErr_Format(PyExc_TypeError, "'%R' is not a mutable sequence", self);
return NULL;
}
static PyMethodDef PyUpb_GenericSequence_Methods[] = {
{"index", PyUpb_GenericSequence_Index, METH_O},
{"count", PyUpb_GenericSequence_Count, METH_O},
{"append", PyUpb_GenericSequence_Append, METH_O},
// This was implemented for Python/C++ but so far has not been required.
//{ "__reversed__", (PyCFunction)Reversed, METH_NOARGS, },
{NULL}};
static PyType_Slot PyUpb_GenericSequence_Slots[] = {
{Py_tp_dealloc, &PyUpb_GenericSequence_Dealloc},
{Py_tp_methods, &PyUpb_GenericSequence_Methods},
{Py_sq_length, PyUpb_GenericSequence_Length},
{Py_sq_item, PyUpb_GenericSequence_GetItem},
{Py_tp_richcompare, &PyUpb_GenericSequence_RichCompare},
{Py_mp_subscript, PyUpb_GenericSequence_Subscript},
// These were implemented for Python/C++ but so far have not been required.
// {Py_tp_repr, &PyUpb_GenericSequence_Repr},
// {Py_sq_contains, PyUpb_GenericSequence_Contains},
// {Py_mp_ass_subscript, PyUpb_GenericSequence_AssignSubscript},
{0, NULL},
};
static PyType_Spec PyUpb_GenericSequence_Spec = {
PYUPB_MODULE_NAME "._GenericSequence", // tp_name
sizeof(PyUpb_GenericSequence), // tp_basicsize
0, // tp_itemsize
Py_TPFLAGS_DEFAULT, // tp_flags
PyUpb_GenericSequence_Slots,
};
// -----------------------------------------------------------------------------
// ByNameMap
// -----------------------------------------------------------------------------
typedef struct {
PyObject_HEAD;
const PyUpb_ByNameMap_Funcs* funcs;
const void* parent; // upb_MessageDef*, upb_DefPool*, etc.
PyObject* parent_obj; // Python object that keeps parent alive, we own a ref.
} PyUpb_ByNameMap;
PyUpb_ByNameMap* PyUpb_ByNameMap_Self(PyObject* obj) {
assert(Py_TYPE(obj) == PyUpb_ModuleState_Get()->by_name_map_type);
return (PyUpb_ByNameMap*)obj;
}
static void PyUpb_ByNameMap_Dealloc(PyObject* _self) {
PyUpb_ByNameMap* self = PyUpb_ByNameMap_Self(_self);
Py_DECREF(self->parent_obj);
PyUpb_Dealloc(self);
}
PyObject* PyUpb_ByNameMap_New(const PyUpb_ByNameMap_Funcs* funcs,
const void* parent, PyObject* parent_obj) {
PyUpb_ModuleState* s = PyUpb_ModuleState_Get();
PyUpb_ByNameMap* map = (void*)PyType_GenericAlloc(s->by_name_map_type, 0);
map->funcs = funcs;
map->parent = parent;
map->parent_obj = parent_obj;
Py_INCREF(parent_obj);
return &map->ob_base;
}
static Py_ssize_t PyUpb_ByNameMap_Length(PyObject* _self) {
PyUpb_ByNameMap* self = PyUpb_ByNameMap_Self(_self);
return self->funcs->base.get_elem_count(self->parent);
}
static PyObject* PyUpb_ByNameMap_Subscript(PyObject* _self, PyObject* key) {
PyUpb_ByNameMap* self = PyUpb_ByNameMap_Self(_self);
const char* name = PyUpb_GetStrData(key);
const void* elem = name ? self->funcs->lookup(self->parent, name) : NULL;
if (!name && PyObject_Hash(key) == -1) return NULL;
if (elem) {
return self->funcs->base.get_elem_wrapper(elem);
} else {
PyErr_SetObject(PyExc_KeyError, key);
return NULL;
}
}
static int PyUpb_ByNameMap_AssignSubscript(PyObject* self, PyObject* key,
PyObject* value) {
PyErr_Format(PyExc_TypeError, PYUPB_MODULE_NAME
".ByNameMap' object does not support item assignment");
return -1;
}
static int PyUpb_ByNameMap_Contains(PyObject* _self, PyObject* key) {
PyUpb_ByNameMap* self = PyUpb_ByNameMap_Self(_self);
const char* name = PyUpb_GetStrData(key);
const void* elem = name ? self->funcs->lookup(self->parent, name) : NULL;
if (!name && PyObject_Hash(key) == -1) return -1;
return elem ? 1 : 0;
}
static PyObject* PyUpb_ByNameMap_Get(PyObject* _self, PyObject* args) {
PyUpb_ByNameMap* self = PyUpb_ByNameMap_Self(_self);
PyObject* key;
PyObject* default_value = Py_None;
if (!PyArg_UnpackTuple(args, "get", 1, 2, &key, &default_value)) {
return NULL;
}
const char* name = PyUpb_GetStrData(key);
const void* elem = name ? self->funcs->lookup(self->parent, name) : NULL;
if (!name && PyObject_Hash(key) == -1) return NULL;
if (elem) {
return self->funcs->base.get_elem_wrapper(elem);
} else {
Py_INCREF(default_value);
return default_value;
}
}
static PyObject* PyUpb_ByNameMap_GetIter(PyObject* _self) {
PyUpb_ByNameMap* self = PyUpb_ByNameMap_Self(_self);
return PyUpb_ByNameIterator_New(self->funcs, self->parent, self->parent_obj);
}
static PyObject* PyUpb_ByNameMap_Keys(PyObject* _self, PyObject* args) {
PyUpb_ByNameMap* self = PyUpb_ByNameMap_Self(_self);
int n = self->funcs->base.get_elem_count(self->parent);
PyObject* ret = PyList_New(n);
if (!ret) return NULL;
for (int i = 0; i < n; i++) {
const void* elem = self->funcs->base.index(self->parent, i);
PyObject* key = PyUnicode_FromString(self->funcs->get_elem_name(elem));
if (!key) goto error;
PyList_SetItem(ret, i, key);
}
return ret;
error:
Py_XDECREF(ret);
return NULL;
}
static PyObject* PyUpb_ByNameMap_Values(PyObject* _self, PyObject* args) {
PyUpb_ByNameMap* self = PyUpb_ByNameMap_Self(_self);
int n = self->funcs->base.get_elem_count(self->parent);
PyObject* ret = PyList_New(n);
if (!ret) return NULL;
for (int i = 0; i < n; i++) {
const void* elem = self->funcs->base.index(self->parent, i);
PyObject* py_elem = self->funcs->base.get_elem_wrapper(elem);
if (!py_elem) goto error;
PyList_SetItem(ret, i, py_elem);
}
return ret;
error:
Py_XDECREF(ret);
return NULL;
}
static PyObject* PyUpb_ByNameMap_Items(PyObject* _self, PyObject* args) {
PyUpb_ByNameMap* self = (PyUpb_ByNameMap*)_self;
int n = self->funcs->base.get_elem_count(self->parent);
PyObject* ret = PyList_New(n);
PyObject* item;
PyObject* py_elem;
if (!ret) return NULL;
for (int i = 0; i < n; i++) {
const void* elem = self->funcs->base.index(self->parent, i);
item = PyTuple_New(2);
py_elem = self->funcs->base.get_elem_wrapper(elem);
if (!item || !py_elem) goto error;
PyTuple_SetItem(item, 0,
PyUnicode_FromString(self->funcs->get_elem_name(elem)));
PyTuple_SetItem(item, 1, py_elem);
PyList_SetItem(ret, i, item);
}
return ret;
error:
Py_XDECREF(py_elem);
Py_XDECREF(item);
Py_XDECREF(ret);
return NULL;
}
// A mapping container can only be equal to another mapping container, or (for
// backward compatibility) to a dict containing the same items.
// Returns 1 if equal, 0 if unequal, -1 on error.
static int PyUpb_ByNameMap_IsEqual(PyUpb_ByNameMap* self, PyObject* other) {
// Check the identity of C++ pointers.
if (PyObject_TypeCheck(other, Py_TYPE(self))) {
PyUpb_ByNameMap* other_map = (void*)other;
return self->parent == other_map->parent && self->funcs == other_map->funcs;
}
if (!PyDict_Check(other)) return 0;
PyObject* self_dict = PyDict_New();
PyDict_Merge(self_dict, (PyObject*)self, 0);
int eq = PyObject_RichCompareBool(self_dict, other, Py_EQ);
Py_DECREF(self_dict);
return eq;
}
static PyObject* PyUpb_ByNameMap_RichCompare(PyObject* _self, PyObject* other,
int opid) {
PyUpb_ByNameMap* self = PyUpb_ByNameMap_Self(_self);
if (opid != Py_EQ && opid != Py_NE) {
Py_RETURN_NOTIMPLEMENTED;
}
bool ret = PyUpb_ByNameMap_IsEqual(self, other);
if (opid == Py_NE) ret = !ret;
return PyBool_FromLong(ret);
}
static PyMethodDef PyUpb_ByNameMap_Methods[] = {
{"get", (PyCFunction)&PyUpb_ByNameMap_Get, METH_VARARGS},
{"keys", PyUpb_ByNameMap_Keys, METH_NOARGS},
{"values", PyUpb_ByNameMap_Values, METH_NOARGS},
{"items", PyUpb_ByNameMap_Items, METH_NOARGS},
{NULL}};
static PyType_Slot PyUpb_ByNameMap_Slots[] = {
{Py_mp_ass_subscript, PyUpb_ByNameMap_AssignSubscript},
{Py_mp_length, PyUpb_ByNameMap_Length},
{Py_mp_subscript, PyUpb_ByNameMap_Subscript},
{Py_sq_contains, &PyUpb_ByNameMap_Contains},
{Py_tp_dealloc, &PyUpb_ByNameMap_Dealloc},
{Py_tp_iter, PyUpb_ByNameMap_GetIter},
{Py_tp_methods, &PyUpb_ByNameMap_Methods},
{Py_tp_repr, &PyUpb_DescriptorMap_Repr},
{Py_tp_richcompare, &PyUpb_ByNameMap_RichCompare},
{0, NULL},
};
static PyType_Spec PyUpb_ByNameMap_Spec = {
PYUPB_MODULE_NAME "._ByNameMap", // tp_name
sizeof(PyUpb_ByNameMap), // tp_basicsize
0, // tp_itemsize
Py_TPFLAGS_DEFAULT, // tp_flags
PyUpb_ByNameMap_Slots,
};
// -----------------------------------------------------------------------------
// ByNumberMap
// -----------------------------------------------------------------------------
typedef struct {
PyObject_HEAD;
const PyUpb_ByNumberMap_Funcs* funcs;
const void* parent; // upb_MessageDef*, upb_DefPool*, etc.
PyObject* parent_obj; // Python object that keeps parent alive, we own a ref.
} PyUpb_ByNumberMap;
PyUpb_ByNumberMap* PyUpb_ByNumberMap_Self(PyObject* obj) {
assert(Py_TYPE(obj) == PyUpb_ModuleState_Get()->by_number_map_type);
return (PyUpb_ByNumberMap*)obj;
}
PyObject* PyUpb_ByNumberMap_New(const PyUpb_ByNumberMap_Funcs* funcs,
const void* parent, PyObject* parent_obj) {
PyUpb_ModuleState* s = PyUpb_ModuleState_Get();
PyUpb_ByNumberMap* map = (void*)PyType_GenericAlloc(s->by_number_map_type, 0);
map->funcs = funcs;
map->parent = parent;
map->parent_obj = parent_obj;
Py_INCREF(parent_obj);
return &map->ob_base;
}
static void PyUpb_ByNumberMap_Dealloc(PyObject* _self) {
PyUpb_ByNumberMap* self = PyUpb_ByNumberMap_Self(_self);
Py_DECREF(self->parent_obj);
PyUpb_Dealloc(self);
}
static Py_ssize_t PyUpb_ByNumberMap_Length(PyObject* _self) {
PyUpb_ByNumberMap* self = PyUpb_ByNumberMap_Self(_self);
return self->funcs->base.get_elem_count(self->parent);
}
static const void* PyUpb_ByNumberMap_LookupHelper(PyUpb_ByNumberMap* self,
PyObject* key) {
long num = PyLong_AsLong(key);
if (num == -1 && PyErr_Occurred()) {
PyErr_Clear();
// Ensure that the key is hashable (this will raise an error if not).
PyObject_Hash(key);
return NULL;
} else {
return self->funcs->lookup(self->parent, num);
}
}
static PyObject* PyUpb_ByNumberMap_Subscript(PyObject* _self, PyObject* key) {
PyUpb_ByNumberMap* self = PyUpb_ByNumberMap_Self(_self);
const void* elem = PyUpb_ByNumberMap_LookupHelper(self, key);
if (elem) {
return self->funcs->base.get_elem_wrapper(elem);
} else {
if (!PyErr_Occurred()) {
PyErr_SetObject(PyExc_KeyError, key);
}
return NULL;
}
}
static int PyUpb_ByNumberMap_AssignSubscript(PyObject* self, PyObject* key,
PyObject* value) {
PyErr_Format(PyExc_TypeError, PYUPB_MODULE_NAME
".ByNumberMap' object does not support item assignment");
return -1;
}
static PyObject* PyUpb_ByNumberMap_Get(PyObject* _self, PyObject* args) {
PyUpb_ByNumberMap* self = PyUpb_ByNumberMap_Self(_self);
PyObject* key;
PyObject* default_value = Py_None;
if (!PyArg_UnpackTuple(args, "get", 1, 2, &key, &default_value)) {
return NULL;
}
const void* elem = PyUpb_ByNumberMap_LookupHelper(self, key);
if (elem) {
return self->funcs->base.get_elem_wrapper(elem);
} else if (PyErr_Occurred()) {
return NULL;
} else {
return PyUpb_NewRef(default_value);
}
}
static PyObject* PyUpb_ByNumberMap_GetIter(PyObject* _self) {
PyUpb_ByNumberMap* self = PyUpb_ByNumberMap_Self(_self);
return PyUpb_ByNumberIterator_New(self->funcs, self->parent,
self->parent_obj);
}
static PyObject* PyUpb_ByNumberMap_Keys(PyObject* _self, PyObject* args) {
PyUpb_ByNumberMap* self = PyUpb_ByNumberMap_Self(_self);
int n = self->funcs->base.get_elem_count(self->parent);
PyObject* ret = PyList_New(n);
if (!ret) return NULL;
for (int i = 0; i < n; i++) {
const void* elem = self->funcs->base.index(self->parent, i);
PyObject* key = PyLong_FromLong(self->funcs->get_elem_num(elem));
if (!key) goto error;
PyList_SetItem(ret, i, key);
}
return ret;
error:
Py_XDECREF(ret);
return NULL;
}
static PyObject* PyUpb_ByNumberMap_Values(PyObject* _self, PyObject* args) {
PyUpb_ByNumberMap* self = PyUpb_ByNumberMap_Self(_self);
int n = self->funcs->base.get_elem_count(self->parent);
PyObject* ret = PyList_New(n);
if (!ret) return NULL;
for (int i = 0; i < n; i++) {
const void* elem = self->funcs->base.index(self->parent, i);
PyObject* py_elem = self->funcs->base.get_elem_wrapper(elem);
if (!py_elem) goto error;
PyList_SetItem(ret, i, py_elem);
}
return ret;
error:
Py_XDECREF(ret);
return NULL;
}
static PyObject* PyUpb_ByNumberMap_Items(PyObject* _self, PyObject* args) {
PyUpb_ByNumberMap* self = PyUpb_ByNumberMap_Self(_self);
int n = self->funcs->base.get_elem_count(self->parent);
PyObject* ret = PyList_New(n);
PyObject* item;
PyObject* py_elem;
if (!ret) return NULL;
for (int i = 0; i < n; i++) {
const void* elem = self->funcs->base.index(self->parent, i);
int number = self->funcs->get_elem_num(elem);
item = PyTuple_New(2);
py_elem = self->funcs->base.get_elem_wrapper(elem);
if (!item || !py_elem) goto error;
PyTuple_SetItem(item, 0, PyLong_FromLong(number));
PyTuple_SetItem(item, 1, py_elem);
PyList_SetItem(ret, i, item);
}
return ret;
error:
Py_XDECREF(py_elem);
Py_XDECREF(item);
Py_XDECREF(ret);
return NULL;
}
static int PyUpb_ByNumberMap_Contains(PyObject* _self, PyObject* key) {
PyUpb_ByNumberMap* self = PyUpb_ByNumberMap_Self(_self);
const void* elem = PyUpb_ByNumberMap_LookupHelper(self, key);
if (elem) return 1;
if (PyErr_Occurred()) return -1;
return 0;
}
// A mapping container can only be equal to another mapping container, or (for
// backward compatibility) to a dict containing the same items.
// Returns 1 if equal, 0 if unequal, -1 on error.
static int PyUpb_ByNumberMap_IsEqual(PyUpb_ByNumberMap* self, PyObject* other) {
// Check the identity of C++ pointers.
if (PyObject_TypeCheck(other, Py_TYPE(self))) {
PyUpb_ByNumberMap* other_map = (void*)other;
return self->parent == other_map->parent && self->funcs == other_map->funcs;
}
if (!PyDict_Check(other)) return 0;
PyObject* self_dict = PyDict_New();
PyDict_Merge(self_dict, (PyObject*)self, 0);
int eq = PyObject_RichCompareBool(self_dict, other, Py_EQ);
Py_DECREF(self_dict);
return eq;
}
static PyObject* PyUpb_ByNumberMap_RichCompare(PyObject* _self, PyObject* other,
int opid) {
PyUpb_ByNumberMap* self = PyUpb_ByNumberMap_Self(_self);
if (opid != Py_EQ && opid != Py_NE) {
Py_RETURN_NOTIMPLEMENTED;
}
bool ret = PyUpb_ByNumberMap_IsEqual(self, other);
if (opid == Py_NE) ret = !ret;
return PyBool_FromLong(ret);
}
static PyMethodDef PyUpb_ByNumberMap_Methods[] = {
{"get", (PyCFunction)&PyUpb_ByNumberMap_Get, METH_VARARGS},
{"keys", PyUpb_ByNumberMap_Keys, METH_NOARGS},
{"values", PyUpb_ByNumberMap_Values, METH_NOARGS},
{"items", PyUpb_ByNumberMap_Items, METH_NOARGS},
{NULL}};
static PyType_Slot PyUpb_ByNumberMap_Slots[] = {
{Py_mp_ass_subscript, PyUpb_ByNumberMap_AssignSubscript},
{Py_mp_length, PyUpb_ByNumberMap_Length},
{Py_mp_subscript, PyUpb_ByNumberMap_Subscript},
{Py_sq_contains, &PyUpb_ByNumberMap_Contains},
{Py_tp_dealloc, &PyUpb_ByNumberMap_Dealloc},
{Py_tp_iter, PyUpb_ByNumberMap_GetIter},
{Py_tp_methods, &PyUpb_ByNumberMap_Methods},
{Py_tp_repr, &PyUpb_DescriptorMap_Repr},
{Py_tp_richcompare, &PyUpb_ByNumberMap_RichCompare},
{0, NULL},
};
static PyType_Spec PyUpb_ByNumberMap_Spec = {
PYUPB_MODULE_NAME "._ByNumberMap", // tp_name
sizeof(PyUpb_ByNumberMap), // tp_basicsize
0, // tp_itemsize
Py_TPFLAGS_DEFAULT, // tp_flags
PyUpb_ByNumberMap_Slots,
};
// -----------------------------------------------------------------------------
// Top Level
// -----------------------------------------------------------------------------
bool PyUpb_InitDescriptorContainers(PyObject* m) {
PyUpb_ModuleState* s = PyUpb_ModuleState_GetFromModule(m);
s->by_name_map_type = PyUpb_AddClass(m, &PyUpb_ByNameMap_Spec);
s->by_number_map_type = PyUpb_AddClass(m, &PyUpb_ByNumberMap_Spec);
s->by_name_iterator_type = PyUpb_AddClass(m, &PyUpb_ByNameIterator_Spec);
s->by_number_iterator_type = PyUpb_AddClass(m, &PyUpb_ByNumberIterator_Spec);
s->generic_sequence_type = PyUpb_AddClass(m, &PyUpb_GenericSequence_Spec);
return s->by_name_map_type && s->by_number_map_type &&
s->by_name_iterator_type && s->by_number_iterator_type &&
s->generic_sequence_type;
}