Skip to content

Commit 9a0b591

Browse files
anandoleecopybara-github
authored andcommitted
Register Scalar/MessageMapContainerTypes as virtual subclasses of
MutableMapping instead of inheriting directly in python cpp extension. This prevents these from using abc.ABCMeta metaclass to avoid deprecation warning: DeprecationWarning: Type google.protobuf.internal.cpp._message.MessageMapContainer uses PyType_Spec with a metaclass that has custom tp_new. This is deprecated and will no longer be allowed in Python 3.14. PiperOrigin-RevId: 734302096
1 parent cdbde21 commit 9a0b591

File tree

1 file changed

+51
-27
lines changed

1 file changed

+51
-27
lines changed

python/google/protobuf/pyext/map_container.cc

+51-27
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99

1010
#include "google/protobuf/pyext/map_container.h"
1111

12+
#include <Python.h>
13+
14+
#include <cstddef>
1215
#include <cstdint>
1316
#include <memory>
1417
#include <string>
@@ -30,7 +33,7 @@ namespace python {
3033
class MapReflectionFriend {
3134
public:
3235
// Methods that are in common between the map types.
33-
static PyObject* Contains(PyObject* _self, PyObject* key);
36+
static int Contains(PyObject* _self, PyObject* key);
3437
static Py_ssize_t Length(PyObject* _self);
3538
static PyObject* GetIterator(PyObject* _self);
3639
static PyObject* IterNext(PyObject* _self);
@@ -328,7 +331,7 @@ PyObject* MapReflectionFriend::MergeFrom(PyObject* _self, PyObject* arg) {
328331
Py_RETURN_NONE;
329332
}
330333

331-
PyObject* MapReflectionFriend::Contains(PyObject* _self, PyObject* key) {
334+
int MapReflectionFriend::Contains(PyObject* _self, PyObject* key) {
332335
MapContainer* self = GetMap(_self);
333336

334337
const Message* message = self->parent->message;
@@ -337,14 +340,14 @@ PyObject* MapReflectionFriend::Contains(PyObject* _self, PyObject* key) {
337340
MapKey map_key;
338341

339342
if (!PythonToMapKey(self, key, &map_key, &map_key_string)) {
340-
return nullptr;
343+
return -1;
341344
}
342345

343346
if (reflection->ContainsMapKey(*message, self->parent_field_descriptor,
344347
map_key)) {
345-
Py_RETURN_TRUE;
348+
return 1;
346349
} else {
347-
Py_RETURN_FALSE;
350+
return 0;
348351
}
349352
}
350353

@@ -450,11 +453,11 @@ static PyObject* ScalarMapSetdefault(PyObject* self, PyObject* args) {
450453
return nullptr;
451454
}
452455

453-
ScopedPyObjectPtr is_present(MapReflectionFriend::Contains(self, key));
454-
if (is_present == nullptr) {
456+
int is_present = MapReflectionFriend::Contains(self, key);
457+
if (is_present < 0) {
455458
return nullptr;
456459
}
457-
if (PyObject_IsTrue(is_present.get())) {
460+
if (is_present) {
458461
return MapReflectionFriend::ScalarMapGetItem(self, key);
459462
}
460463

@@ -476,12 +479,12 @@ static PyObject* ScalarMapGet(PyObject* self, PyObject* args,
476479
return nullptr;
477480
}
478481

479-
ScopedPyObjectPtr is_present(MapReflectionFriend::Contains(self, key));
480-
if (is_present.get() == nullptr) {
482+
int is_present = MapReflectionFriend::Contains(self, key);
483+
if (is_present < 0) {
481484
return nullptr;
482485
}
483486

484-
if (PyObject_IsTrue(is_present.get())) {
487+
if (is_present) {
485488
return MapReflectionFriend::ScalarMapGetItem(self, key);
486489
} else {
487490
if (default_value != nullptr) {
@@ -534,8 +537,6 @@ static void ScalarMapDealloc(PyObject* _self) {
534537
}
535538

536539
static PyMethodDef ScalarMapMethods[] = {
537-
{"__contains__", MapReflectionFriend::Contains, METH_O,
538-
"Tests whether a key is a member of the map."},
539540
{"clear", (PyCFunction)Clear, METH_NOARGS,
540541
"Removes all elements from the map."},
541542
{"setdefault", (PyCFunction)ScalarMapSetdefault, METH_VARARGS,
@@ -561,6 +562,7 @@ static PyType_Slot ScalarMapContainer_Type_slots[] = {
561562
{Py_mp_length, (void*)MapReflectionFriend::Length},
562563
{Py_mp_subscript, (void*)MapReflectionFriend::ScalarMapGetItem},
563564
{Py_mp_ass_subscript, (void*)MapReflectionFriend::ScalarMapSetItem},
565+
{Py_sq_contains, (void*)MapReflectionFriend::Contains},
564566
{Py_tp_methods, (void*)ScalarMapMethods},
565567
{Py_tp_iter, (void*)MapReflectionFriend::GetIterator},
566568
{Py_tp_repr, (void*)MapReflectionFriend::ScalarMapToStr},
@@ -727,12 +729,12 @@ PyObject* MessageMapGet(PyObject* self, PyObject* args, PyObject* kwargs) {
727729
return nullptr;
728730
}
729731

730-
ScopedPyObjectPtr is_present(MapReflectionFriend::Contains(self, key));
731-
if (is_present.get() == nullptr) {
732+
int is_present = MapReflectionFriend::Contains(self, key);
733+
if (is_present < 0) {
732734
return nullptr;
733735
}
734736

735-
if (PyObject_IsTrue(is_present.get())) {
737+
if (is_present) {
736738
return MapReflectionFriend::MessageMapGetItem(self, key);
737739
} else {
738740
if (default_value != nullptr) {
@@ -757,8 +759,6 @@ static void MessageMapDealloc(PyObject* _self) {
757759
}
758760

759761
static PyMethodDef MessageMapMethods[] = {
760-
{"__contains__", (PyCFunction)MapReflectionFriend::Contains, METH_O,
761-
"Tests whether the map contains this element."},
762762
{"clear", (PyCFunction)Clear, METH_NOARGS,
763763
"Removes all elements from the map."},
764764
{"setdefault", (PyCFunction)MessageMapSetdefault, METH_VARARGS,
@@ -786,6 +786,7 @@ static PyType_Slot MessageMapContainer_Type_slots[] = {
786786
{Py_mp_length, (void*)MapReflectionFriend::Length},
787787
{Py_mp_subscript, (void*)MapReflectionFriend::MessageMapGetItem},
788788
{Py_mp_ass_subscript, (void*)MapReflectionFriend::MessageMapSetItem},
789+
{Py_sq_contains, (void*)MapReflectionFriend::Contains},
789790
{Py_tp_methods, (void*)MessageMapMethods},
790791
{Py_tp_iter, (void*)MapReflectionFriend::GetIterator},
791792
{Py_tp_repr, (void*)MapReflectionFriend::MessageMapToStr},
@@ -910,6 +911,30 @@ PyTypeObject MapIterator_Type = {
910911
nullptr, // tp_init
911912
};
912913

914+
PyTypeObject* Py_AddClassWithRegister(PyType_Spec* spec, PyObject* virtual_base,
915+
const char** methods) {
916+
PyObject* type = PyType_FromSpec(spec);
917+
PyObject* ret1 = PyObject_CallMethod(virtual_base, "register", "O", type);
918+
if (!ret1) {
919+
Py_XDECREF(type);
920+
return nullptr;
921+
}
922+
for (size_t i = 0; methods[i] != nullptr; i++) {
923+
PyObject* method = PyObject_GetAttrString(virtual_base, methods[i]);
924+
if (!method) {
925+
Py_XDECREF(type);
926+
return nullptr;
927+
}
928+
int ret2 = PyObject_SetAttrString(type, methods[i], method);
929+
if (ret2 < 0) {
930+
Py_XDECREF(type);
931+
return nullptr;
932+
}
933+
}
934+
935+
return (PyTypeObject*)type;
936+
}
937+
913938
bool InitMapContainers() {
914939
// ScalarMapContainer_Type derives from our MutableMapping type.
915940
ScopedPyObjectPtr abc(PyImport_ImportModule("collections.abc"));
@@ -923,21 +948,20 @@ bool InitMapContainers() {
923948
return false;
924949
}
925950

926-
Py_INCREF(mutable_mapping.get());
927-
ScopedPyObjectPtr bases(PyTuple_Pack(1, mutable_mapping.get()));
928-
if (bases == nullptr) {
929-
return false;
930-
}
951+
const char* methods[] = {"keys", "items", "values", "__eq__", "__ne__",
952+
"pop", "popitem", "update", nullptr};
931953

932-
ScalarMapContainer_Type = reinterpret_cast<PyTypeObject*>(
933-
PyType_FromSpecWithBases(&ScalarMapContainer_Type_spec, bases.get()));
954+
ScalarMapContainer_Type =
955+
reinterpret_cast<PyTypeObject*>(Py_AddClassWithRegister(
956+
&ScalarMapContainer_Type_spec, mutable_mapping.get(), methods));
934957

935958
if (PyType_Ready(&MapIterator_Type) < 0) {
936959
return false;
937960
}
938961

939-
MessageMapContainer_Type = reinterpret_cast<PyTypeObject*>(
940-
PyType_FromSpecWithBases(&MessageMapContainer_Type_spec, bases.get()));
962+
MessageMapContainer_Type =
963+
reinterpret_cast<PyTypeObject*>(Py_AddClassWithRegister(
964+
&MessageMapContainer_Type_spec, mutable_mapping.get(), methods));
941965
return true;
942966
}
943967

0 commit comments

Comments
 (0)