Nextgen Proto Pythonic API: Add 'in' operator
(Second attempt. The first attempt missed ListValue)
The “in” operator will be consistent with HasField but a little different with Proto Plus.
The detail behavior of “in” operator in Nextgen
* For WKT Struct (to be consist with old Struct behavior):
-Raise TypeError if not pass a string
-Check if the key is in the struct.fields
* For WKT ListValue (to be consist with old behavior):
-Check if the key is in the list_value.values
* For other messages:
-Raise ValueError if not pass a string
-Raise ValueError if the string is not a field
-For Oneof: Check any field under the oneof is set
-For has-presence field: check if set
-For non-has-presence field (include repeated fields): raise ValueError
PiperOrigin-RevId: 631143378
diff --git a/python/message.c b/python/message.c
index c0c0882..d5213ab 100644
--- a/python/message.c
+++ b/python/message.c
@@ -1044,6 +1044,35 @@
NULL);
}
+static PyObject* PyUpb_Message_Contains(PyObject* _self, PyObject* arg) {
+ const upb_MessageDef* msgdef = PyUpb_Message_GetMsgdef(_self);
+ switch (upb_MessageDef_WellKnownType(msgdef)) {
+ case kUpb_WellKnown_Struct: {
+ // For WKT Struct, check if the key is in the fields.
+ PyUpb_Message* self = (void*)_self;
+ if (PyUpb_Message_IsStub(self)) Py_RETURN_FALSE;
+ upb_Message* msg = PyUpb_Message_GetMsg(self);
+ const upb_FieldDef* f = upb_MessageDef_FindFieldByName(msgdef, "fields");
+ const upb_Map* map = upb_Message_GetFieldByDef(msg, f).map_val;
+ const upb_MessageDef* entry_m = upb_FieldDef_MessageSubDef(f);
+ const upb_FieldDef* key_f = upb_MessageDef_Field(entry_m, 0);
+ upb_MessageValue u_key;
+ if (!PyUpb_PyToUpb(arg, key_f, &u_key, NULL)) return NULL;
+ return PyBool_FromLong(upb_Map_Get(map, u_key, NULL));
+ }
+ case kUpb_WellKnown_ListValue: {
+ // For WKT ListValue, check if the key is in the items.
+ PyUpb_Message* self = (void*)_self;
+ if (PyUpb_Message_IsStub(self)) Py_RETURN_FALSE;
+ PyObject* items = PyObject_CallMethod(_self, "items", NULL);
+ return PyBool_FromLong(PySequence_Contains(items, arg));
+ }
+ default:
+ // For other messages, check with HasField.
+ return PyUpb_Message_HasField(_self, arg);
+ }
+}
+
static PyObject* PyUpb_Message_FindInitializationErrors(PyObject* _self,
PyObject* arg);
@@ -1642,6 +1671,8 @@
// TODO
//{ "__unicode__", (PyCFunction)ToUnicode, METH_NOARGS,
// "Outputs a unicode representation of the message." },
+ {"__contains__", PyUpb_Message_Contains, METH_O,
+ "Checks if a message field is set."},
{"ByteSize", (PyCFunction)PyUpb_Message_ByteSize, METH_NOARGS,
"Returns the size of the message in bytes."},
{"Clear", (PyCFunction)PyUpb_Message_Clear, METH_NOARGS,