• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2023 Google LLC.  All rights reserved.
3 //
4 // Use of this source code is governed by a BSD-style
5 // license that can be found in the LICENSE file or at
6 // https://developers.google.com/open-source/licenses/bsd
7 
8 #include "python/map.h"
9 
10 #include "python/convert.h"
11 #include "python/message.h"
12 #include "python/protobuf.h"
13 #include "upb/message/map.h"
14 #include "upb/reflection/def.h"
15 
16 // -----------------------------------------------------------------------------
17 // MapContainer
18 // -----------------------------------------------------------------------------
19 
20 typedef struct {
21   PyObject_HEAD;
22   PyObject* arena;
23   // The field descriptor (upb_FieldDef*).
24   // The low bit indicates whether the container is reified (see ptr below).
25   //   - low bit set: repeated field is a stub (empty map, no underlying data).
26   //   - low bit clear: repeated field is reified (points to upb_Array).
27   uintptr_t field;
28   union {
29     PyObject* parent;  // stub: owning pointer to parent message.
30     upb_Map* map;      // reified: the data for this array.
31   } ptr;
32   int version;
33 } PyUpb_MapContainer;
34 
35 static PyObject* PyUpb_MapIterator_New(PyUpb_MapContainer* map);
36 
PyUpb_MapContainer_IsStub(PyUpb_MapContainer * self)37 static bool PyUpb_MapContainer_IsStub(PyUpb_MapContainer* self) {
38   return self->field & 1;
39 }
40 
41 // If the map is reified, returns it.  Otherwise, returns NULL.
42 // If NULL is returned, the object is empty and has no underlying data.
PyUpb_MapContainer_GetIfReified(PyUpb_MapContainer * self)43 static upb_Map* PyUpb_MapContainer_GetIfReified(PyUpb_MapContainer* self) {
44   return PyUpb_MapContainer_IsStub(self) ? NULL : self->ptr.map;
45 }
46 
PyUpb_MapContainer_GetField(PyUpb_MapContainer * self)47 static const upb_FieldDef* PyUpb_MapContainer_GetField(
48     PyUpb_MapContainer* self) {
49   return (const upb_FieldDef*)(self->field & ~(uintptr_t)1);
50 }
51 
PyUpb_MapContainer_Dealloc(void * _self)52 static void PyUpb_MapContainer_Dealloc(void* _self) {
53   PyUpb_MapContainer* self = _self;
54   Py_DECREF(self->arena);
55   if (PyUpb_MapContainer_IsStub(self)) {
56     PyUpb_Message_CacheDelete(self->ptr.parent,
57                               PyUpb_MapContainer_GetField(self));
58     Py_DECREF(self->ptr.parent);
59   } else {
60     PyUpb_ObjCache_Delete(self->ptr.map);
61   }
62   PyUpb_Dealloc(_self);
63 }
64 
PyUpb_MapContainer_GetClass(const upb_FieldDef * f)65 static PyTypeObject* PyUpb_MapContainer_GetClass(const upb_FieldDef* f) {
66   assert(upb_FieldDef_IsMap(f));
67   PyUpb_ModuleState* state = PyUpb_ModuleState_Get();
68   const upb_FieldDef* val =
69       upb_MessageDef_Field(upb_FieldDef_MessageSubDef(f), 1);
70   assert(upb_FieldDef_Number(val) == 2);
71   return upb_FieldDef_IsSubMessage(val) ? state->message_map_container_type
72                                         : state->scalar_map_container_type;
73 }
74 
PyUpb_MapContainer_NewStub(PyObject * parent,const upb_FieldDef * f,PyObject * arena)75 PyObject* PyUpb_MapContainer_NewStub(PyObject* parent, const upb_FieldDef* f,
76                                      PyObject* arena) {
77   // We only create stubs when the parent is reified, by convention.  However
78   // this is not an invariant: the parent could become reified at any time.
79   assert(PyUpb_Message_GetIfReified(parent) == NULL);
80   PyTypeObject* cls = PyUpb_MapContainer_GetClass(f);
81   PyUpb_MapContainer* map = (void*)PyType_GenericAlloc(cls, 0);
82   map->arena = arena;
83   map->field = (uintptr_t)f | 1;
84   map->ptr.parent = parent;
85   map->version = 0;
86   Py_INCREF(arena);
87   Py_INCREF(parent);
88   return &map->ob_base;
89 }
90 
PyUpb_MapContainer_Reify(PyObject * _self,upb_Map * map)91 void PyUpb_MapContainer_Reify(PyObject* _self, upb_Map* map) {
92   PyUpb_MapContainer* self = (PyUpb_MapContainer*)_self;
93   if (!map) {
94     const upb_FieldDef* f = PyUpb_MapContainer_GetField(self);
95     upb_Arena* arena = PyUpb_Arena_Get(self->arena);
96     const upb_MessageDef* entry_m = upb_FieldDef_MessageSubDef(f);
97     const upb_FieldDef* key_f = upb_MessageDef_Field(entry_m, 0);
98     const upb_FieldDef* val_f = upb_MessageDef_Field(entry_m, 1);
99     map = upb_Map_New(arena, upb_FieldDef_CType(key_f),
100                       upb_FieldDef_CType(val_f));
101   }
102   PyUpb_ObjCache_Add(map, &self->ob_base);
103   Py_DECREF(self->ptr.parent);
104   self->ptr.map = map;  // Overwrites self->ptr.parent.
105   self->field &= ~(uintptr_t)1;
106   assert(!PyUpb_MapContainer_IsStub(self));
107 }
108 
PyUpb_MapContainer_Invalidate(PyObject * obj)109 void PyUpb_MapContainer_Invalidate(PyObject* obj) {
110   PyUpb_MapContainer* self = (PyUpb_MapContainer*)obj;
111   self->version++;
112 }
113 
PyUpb_MapContainer_EnsureReified(PyObject * _self)114 upb_Map* PyUpb_MapContainer_EnsureReified(PyObject* _self) {
115   PyUpb_MapContainer* self = (PyUpb_MapContainer*)_self;
116   self->version++;
117   upb_Map* map = PyUpb_MapContainer_GetIfReified(self);
118   if (map) return map;  // Already writable.
119 
120   const upb_FieldDef* f = PyUpb_MapContainer_GetField(self);
121   upb_Arena* arena = PyUpb_Arena_Get(self->arena);
122   const upb_MessageDef* entry_m = upb_FieldDef_MessageSubDef(f);
123   const upb_FieldDef* key_f = upb_MessageDef_Field(entry_m, 0);
124   const upb_FieldDef* val_f = upb_MessageDef_Field(entry_m, 1);
125   map =
126       upb_Map_New(arena, upb_FieldDef_CType(key_f), upb_FieldDef_CType(val_f));
127   upb_MessageValue msgval = {.map_val = map};
128   PyUpb_Message_SetConcreteSubobj(self->ptr.parent, f, msgval);
129   PyUpb_MapContainer_Reify((PyObject*)self, map);
130   return map;
131 }
132 
PyUpb_MapContainer_Set(PyUpb_MapContainer * self,upb_Map * map,upb_MessageValue key,upb_MessageValue val,upb_Arena * arena)133 static bool PyUpb_MapContainer_Set(PyUpb_MapContainer* self, upb_Map* map,
134                                    upb_MessageValue key, upb_MessageValue val,
135                                    upb_Arena* arena) {
136   switch (upb_Map_Insert(map, key, val, arena)) {
137     case kUpb_MapInsertStatus_Inserted:
138       return true;
139     case kUpb_MapInsertStatus_Replaced:
140       // We did not insert a new key, undo the previous invalidate.
141       self->version--;
142       return true;
143     case kUpb_MapInsertStatus_OutOfMemory:
144       return false;
145   }
146   return false;  // Unreachable, silence compiler warning.
147 }
148 
149 // Assigns `self[key] = val` for the map `self`.
PyUpb_MapContainer_AssignSubscript(PyObject * _self,PyObject * key,PyObject * val)150 static int PyUpb_MapContainer_AssignSubscript(PyObject* _self, PyObject* key,
151                                               PyObject* val) {
152   PyUpb_MapContainer* self = (PyUpb_MapContainer*)_self;
153   upb_Map* map = PyUpb_MapContainer_EnsureReified(_self);
154   const upb_FieldDef* f = PyUpb_MapContainer_GetField(self);
155   const upb_MessageDef* entry_m = upb_FieldDef_MessageSubDef(f);
156   const upb_FieldDef* key_f = upb_MessageDef_Field(entry_m, 0);
157   const upb_FieldDef* val_f = upb_MessageDef_Field(entry_m, 1);
158   upb_Arena* arena = PyUpb_Arena_Get(self->arena);
159   upb_MessageValue u_key, u_val;
160   if (!PyUpb_PyToUpb(key, key_f, &u_key, NULL)) return -1;
161 
162   if (val) {
163     if (!PyUpb_PyToUpb(val, val_f, &u_val, arena)) return -1;
164     if (!PyUpb_MapContainer_Set(self, map, u_key, u_val, arena)) return -1;
165   } else {
166     if (!upb_Map_Delete(map, u_key, NULL)) {
167       PyErr_Format(PyExc_KeyError, "Key not present in map");
168       return -1;
169     }
170   }
171   return 0;
172 }
173 
PyUpb_MapContainer_Subscript(PyObject * _self,PyObject * key)174 static PyObject* PyUpb_MapContainer_Subscript(PyObject* _self, PyObject* key) {
175   PyUpb_MapContainer* self = (PyUpb_MapContainer*)_self;
176   upb_Map* map = PyUpb_MapContainer_GetIfReified(self);
177   const upb_FieldDef* f = PyUpb_MapContainer_GetField(self);
178   const upb_MessageDef* entry_m = upb_FieldDef_MessageSubDef(f);
179   const upb_FieldDef* key_f = upb_MessageDef_Field(entry_m, 0);
180   const upb_FieldDef* val_f = upb_MessageDef_Field(entry_m, 1);
181   upb_MessageValue u_key, u_val;
182   if (!PyUpb_PyToUpb(key, key_f, &u_key, NULL)) return NULL;
183   if (!map || !upb_Map_Get(map, u_key, &u_val)) {
184     map = PyUpb_MapContainer_EnsureReified(_self);
185     upb_Arena* arena = PyUpb_Arena_Get(self->arena);
186     if (upb_FieldDef_IsSubMessage(val_f)) {
187       const upb_MessageDef* m = upb_FieldDef_MessageSubDef(val_f);
188       const upb_MiniTable* layout = upb_MessageDef_MiniTable(m);
189       u_val.msg_val = upb_Message_New(layout, arena);
190     } else {
191       memset(&u_val, 0, sizeof(u_val));
192     }
193     if (!PyUpb_MapContainer_Set(self, map, u_key, u_val, arena)) return false;
194   }
195   return PyUpb_UpbToPy(u_val, val_f, self->arena);
196 }
197 
PyUpb_MapContainer_Contains(PyObject * _self,PyObject * key)198 static int PyUpb_MapContainer_Contains(PyObject* _self, PyObject* key) {
199   PyUpb_MapContainer* self = (PyUpb_MapContainer*)_self;
200   upb_Map* map = PyUpb_MapContainer_GetIfReified(self);
201   if (!map) return 0;
202   const upb_FieldDef* f = PyUpb_MapContainer_GetField(self);
203   const upb_MessageDef* entry_m = upb_FieldDef_MessageSubDef(f);
204   const upb_FieldDef* key_f = upb_MessageDef_Field(entry_m, 0);
205   upb_MessageValue u_key;
206   if (!PyUpb_PyToUpb(key, key_f, &u_key, NULL)) return -1;
207   if (upb_Map_Get(map, u_key, NULL)) {
208     return 1;
209   } else {
210     return 0;
211   }
212 }
213 
PyUpb_MapContainer_Clear(PyObject * _self,PyObject * key)214 static PyObject* PyUpb_MapContainer_Clear(PyObject* _self, PyObject* key) {
215   upb_Map* map = PyUpb_MapContainer_EnsureReified(_self);
216   upb_Map_Clear(map);
217   Py_RETURN_NONE;
218 }
219 
PyUpb_MapContainer_Get(PyObject * _self,PyObject * args,PyObject * kwargs)220 static PyObject* PyUpb_MapContainer_Get(PyObject* _self, PyObject* args,
221                                         PyObject* kwargs) {
222   PyUpb_MapContainer* self = (PyUpb_MapContainer*)_self;
223   static const char* kwlist[] = {"key", "default", NULL};
224   PyObject* key;
225   PyObject* default_value = NULL;
226   upb_Map* map = PyUpb_MapContainer_GetIfReified(self);
227   if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|O", (char**)kwlist, &key,
228                                    &default_value)) {
229     return NULL;
230   }
231 
232   const upb_FieldDef* f = PyUpb_MapContainer_GetField(self);
233   const upb_MessageDef* entry_m = upb_FieldDef_MessageSubDef(f);
234   const upb_FieldDef* key_f = upb_MessageDef_Field(entry_m, 0);
235   const upb_FieldDef* val_f = upb_MessageDef_Field(entry_m, 1);
236   upb_MessageValue u_key, u_val;
237   if (!PyUpb_PyToUpb(key, key_f, &u_key, NULL)) return NULL;
238   if (map && upb_Map_Get(map, u_key, &u_val)) {
239     return PyUpb_UpbToPy(u_val, val_f, self->arena);
240   }
241   if (default_value) {
242     Py_INCREF(default_value);
243     return default_value;
244   }
245   Py_RETURN_NONE;
246 }
247 
PyUpb_MapContainer_GetEntryClass(PyObject * _self,PyObject * arg)248 static PyObject* PyUpb_MapContainer_GetEntryClass(PyObject* _self,
249                                                   PyObject* arg) {
250   PyUpb_MapContainer* self = (PyUpb_MapContainer*)_self;
251   const upb_FieldDef* f = PyUpb_MapContainer_GetField(self);
252   const upb_MessageDef* entry_m = upb_FieldDef_MessageSubDef(f);
253   return PyUpb_Descriptor_GetClass(entry_m);
254 }
255 
PyUpb_MapContainer_Length(PyObject * _self)256 static Py_ssize_t PyUpb_MapContainer_Length(PyObject* _self) {
257   PyUpb_MapContainer* self = (PyUpb_MapContainer*)_self;
258   upb_Map* map = PyUpb_MapContainer_GetIfReified(self);
259   return map ? upb_Map_Size(map) : 0;
260 }
261 
262 int PyUpb_Message_InitMapAttributes(PyObject* map, PyObject* value,
263                                     const upb_FieldDef* f);
264 
PyUpb_MapContainer_MergeFrom(PyObject * _self,PyObject * _arg)265 static PyObject* PyUpb_MapContainer_MergeFrom(PyObject* _self, PyObject* _arg) {
266   PyUpb_MapContainer* self = (PyUpb_MapContainer*)_self;
267   const upb_FieldDef* f = PyUpb_MapContainer_GetField(self);
268 
269   if (PyDict_Check(_arg)) {
270     return PyErr_Format(PyExc_AttributeError, "Merging of dict is not allowed");
271   }
272 
273   if (PyUpb_Message_InitMapAttributes(_self, _arg, f) < 0) {
274     return NULL;
275   }
276 
277   Py_RETURN_NONE;
278 }
279 
PyUpb_MapContainer_Repr(PyObject * _self)280 static PyObject* PyUpb_MapContainer_Repr(PyObject* _self) {
281   PyUpb_MapContainer* self = (PyUpb_MapContainer*)_self;
282   upb_Map* map = PyUpb_MapContainer_GetIfReified(self);
283   PyObject* dict = PyDict_New();
284   if (map) {
285     const upb_FieldDef* f = PyUpb_MapContainer_GetField(self);
286     const upb_MessageDef* entry_m = upb_FieldDef_MessageSubDef(f);
287     const upb_FieldDef* key_f = upb_MessageDef_Field(entry_m, 0);
288     const upb_FieldDef* val_f = upb_MessageDef_Field(entry_m, 1);
289     size_t iter = kUpb_Map_Begin;
290     upb_MessageValue map_key, map_val;
291     while (upb_Map_Next(map, &map_key, &map_val, &iter)) {
292       PyObject* key = PyUpb_UpbToPy(map_key, key_f, self->arena);
293       PyObject* val = PyUpb_UpbToPy(map_val, val_f, self->arena);
294       if (!key || !val) {
295         Py_XDECREF(key);
296         Py_XDECREF(val);
297         Py_DECREF(dict);
298         return NULL;
299       }
300       PyDict_SetItem(dict, key, val);
301       Py_DECREF(key);
302       Py_DECREF(val);
303     }
304   }
305   PyObject* repr = PyObject_Repr(dict);
306   Py_DECREF(dict);
307   return repr;
308 }
309 
PyUpb_MapContainer_GetOrCreateWrapper(upb_Map * map,const upb_FieldDef * f,PyObject * arena)310 PyObject* PyUpb_MapContainer_GetOrCreateWrapper(upb_Map* map,
311                                                 const upb_FieldDef* f,
312                                                 PyObject* arena) {
313   PyUpb_MapContainer* ret = (void*)PyUpb_ObjCache_Get(map);
314   if (ret) return &ret->ob_base;
315 
316   PyTypeObject* cls = PyUpb_MapContainer_GetClass(f);
317   ret = (void*)PyType_GenericAlloc(cls, 0);
318   ret->arena = arena;
319   ret->field = (uintptr_t)f;
320   ret->ptr.map = map;
321   ret->version = 0;
322   Py_INCREF(arena);
323   PyUpb_ObjCache_Add(map, &ret->ob_base);
324   return &ret->ob_base;
325 }
326 
327 // -----------------------------------------------------------------------------
328 // ScalarMapContainer
329 // -----------------------------------------------------------------------------
330 
331 static PyMethodDef PyUpb_ScalarMapContainer_Methods[] = {
332     {"clear", PyUpb_MapContainer_Clear, METH_NOARGS,
333      "Removes all elements from the map."},
334     {"get", (PyCFunction)PyUpb_MapContainer_Get, METH_VARARGS | METH_KEYWORDS,
335      "Gets the value for the given key if present, or otherwise a default"},
336     {"GetEntryClass", PyUpb_MapContainer_GetEntryClass, METH_NOARGS,
337      "Return the class used to build Entries of (key, value) pairs."},
338     {"MergeFrom", PyUpb_MapContainer_MergeFrom, METH_O,
339      "Merges a map into the current map."},
340     /*
341    { "__deepcopy__", (PyCFunction)DeepCopy, METH_VARARGS,
342      "Makes a deep copy of the class." },
343    { "__reduce__", (PyCFunction)Reduce, METH_NOARGS,
344      "Outputs picklable representation of the repeated field." },
345    */
346     {NULL, NULL},
347 };
348 
349 static PyType_Slot PyUpb_ScalarMapContainer_Slots[] = {
350     {Py_tp_dealloc, PyUpb_MapContainer_Dealloc},
351     {Py_mp_length, PyUpb_MapContainer_Length},
352     {Py_mp_subscript, PyUpb_MapContainer_Subscript},
353     {Py_mp_ass_subscript, PyUpb_MapContainer_AssignSubscript},
354     {Py_sq_contains, PyUpb_MapContainer_Contains},
355     {Py_tp_methods, PyUpb_ScalarMapContainer_Methods},
356     {Py_tp_iter, PyUpb_MapIterator_New},
357     {Py_tp_repr, PyUpb_MapContainer_Repr},
358     {0, NULL},
359 };
360 
361 static PyType_Spec PyUpb_ScalarMapContainer_Spec = {
362     PYUPB_MODULE_NAME ".ScalarMapContainer",
363     sizeof(PyUpb_MapContainer),
364     0,
365     Py_TPFLAGS_DEFAULT,
366     PyUpb_ScalarMapContainer_Slots,
367 };
368 
369 // -----------------------------------------------------------------------------
370 // MessageMapContainer
371 // -----------------------------------------------------------------------------
372 
373 static PyMethodDef PyUpb_MessageMapContainer_Methods[] = {
374     {"clear", PyUpb_MapContainer_Clear, METH_NOARGS,
375      "Removes all elements from the map."},
376     {"get", (PyCFunction)PyUpb_MapContainer_Get, METH_VARARGS | METH_KEYWORDS,
377      "Gets the value for the given key if present, or otherwise a default"},
378     {"get_or_create", PyUpb_MapContainer_Subscript, METH_O,
379      "Alias for getitem, useful to make explicit that the map is mutated."},
380     {"GetEntryClass", PyUpb_MapContainer_GetEntryClass, METH_NOARGS,
381      "Return the class used to build Entries of (key, value) pairs."},
382     {"MergeFrom", PyUpb_MapContainer_MergeFrom, METH_O,
383      "Merges a map into the current map."},
384     /*
385    { "__deepcopy__", (PyCFunction)DeepCopy, METH_VARARGS,
386      "Makes a deep copy of the class." },
387    { "__reduce__", (PyCFunction)Reduce, METH_NOARGS,
388      "Outputs picklable representation of the repeated field." },
389    */
390     {NULL, NULL},
391 };
392 
393 static PyType_Slot PyUpb_MessageMapContainer_Slots[] = {
394     {Py_tp_dealloc, PyUpb_MapContainer_Dealloc},
395     {Py_mp_length, PyUpb_MapContainer_Length},
396     {Py_mp_subscript, PyUpb_MapContainer_Subscript},
397     {Py_mp_ass_subscript, PyUpb_MapContainer_AssignSubscript},
398     {Py_sq_contains, PyUpb_MapContainer_Contains},
399     {Py_tp_methods, PyUpb_MessageMapContainer_Methods},
400     {Py_tp_iter, PyUpb_MapIterator_New},
401     {Py_tp_repr, PyUpb_MapContainer_Repr},
402     {0, NULL}};
403 
404 static PyType_Spec PyUpb_MessageMapContainer_Spec = {
405     PYUPB_MODULE_NAME ".MessageMapContainer", sizeof(PyUpb_MapContainer), 0,
406     Py_TPFLAGS_DEFAULT, PyUpb_MessageMapContainer_Slots};
407 
408 // -----------------------------------------------------------------------------
409 // MapIterator
410 // -----------------------------------------------------------------------------
411 
412 typedef struct {
413   PyObject_HEAD;
414   PyUpb_MapContainer* map;  // We own a reference.
415   size_t iter;
416   int version;
417 } PyUpb_MapIterator;
418 
PyUpb_MapIterator_New(PyUpb_MapContainer * map)419 static PyObject* PyUpb_MapIterator_New(PyUpb_MapContainer* map) {
420   PyUpb_ModuleState* state = PyUpb_ModuleState_Get();
421   PyUpb_MapIterator* iter =
422       (void*)PyType_GenericAlloc(state->map_iterator_type, 0);
423   iter->map = map;
424   iter->iter = kUpb_Map_Begin;
425   iter->version = map->version;
426   Py_INCREF(map);
427   return &iter->ob_base;
428 }
429 
PyUpb_MapIterator_Dealloc(void * _self)430 static void PyUpb_MapIterator_Dealloc(void* _self) {
431   PyUpb_MapIterator* self = (PyUpb_MapIterator*)_self;
432   Py_DECREF(&self->map->ob_base);
433   PyUpb_Dealloc(_self);
434 }
435 
PyUpb_MapIterator_IterNext(PyObject * _self)436 static PyObject* PyUpb_MapIterator_IterNext(PyObject* _self) {
437   PyUpb_MapIterator* self = (PyUpb_MapIterator*)_self;
438   if (self->version != self->map->version) {
439     return PyErr_Format(PyExc_RuntimeError, "Map modified during iteration.");
440   }
441   upb_Map* map = PyUpb_MapContainer_GetIfReified(self->map);
442   if (!map) return NULL;
443   upb_MessageValue key, val;
444   if (!upb_Map_Next(map, &key, &val, &self->iter)) return NULL;
445   const upb_FieldDef* f = PyUpb_MapContainer_GetField(self->map);
446   const upb_MessageDef* entry_m = upb_FieldDef_MessageSubDef(f);
447   const upb_FieldDef* key_f = upb_MessageDef_Field(entry_m, 0);
448   return PyUpb_UpbToPy(key, key_f, self->map->arena);
449 }
450 
451 static PyType_Slot PyUpb_MapIterator_Slots[] = {
452     {Py_tp_dealloc, PyUpb_MapIterator_Dealloc},
453     {Py_tp_iter, PyObject_SelfIter},
454     {Py_tp_iternext, PyUpb_MapIterator_IterNext},
455     {0, NULL}};
456 
457 static PyType_Spec PyUpb_MapIterator_Spec = {
458     PYUPB_MODULE_NAME ".MapIterator", sizeof(PyUpb_MapIterator), 0,
459     Py_TPFLAGS_DEFAULT, PyUpb_MapIterator_Slots};
460 
461 // -----------------------------------------------------------------------------
462 // Top Level
463 // -----------------------------------------------------------------------------
464 
GetMutableMappingBase(void)465 static PyObject* GetMutableMappingBase(void) {
466   PyObject* collections = NULL;
467   PyObject* mapping = NULL;
468   PyObject* base = NULL;
469   if ((collections = PyImport_ImportModule("collections.abc")) &&
470       (mapping = PyObject_GetAttrString(collections, "MutableMapping"))) {
471     base = Py_BuildValue("O", mapping);
472   }
473   Py_XDECREF(collections);
474   Py_XDECREF(mapping);
475   return base;
476 }
477 
PyUpb_Map_Init(PyObject * m)478 bool PyUpb_Map_Init(PyObject* m) {
479   PyUpb_ModuleState* state = PyUpb_ModuleState_GetFromModule(m);
480   PyObject* base = GetMutableMappingBase();
481   if (!base) return false;
482 
483   const char* methods[] = {"keys", "items",   "values", "__eq__",     "__ne__",
484                            "pop",  "popitem", "update", "setdefault", NULL};
485 
486   state->message_map_container_type = PyUpb_AddClassWithRegister(
487       m, &PyUpb_MessageMapContainer_Spec, base, methods);
488   if (!state->message_map_container_type) {
489     return false;
490   }
491   state->scalar_map_container_type = PyUpb_AddClassWithRegister(
492       m, &PyUpb_ScalarMapContainer_Spec, base, methods);
493   if (!state->scalar_map_container_type) {
494     return false;
495   }
496   state->map_iterator_type = PyUpb_AddClass(m, &PyUpb_MapIterator_Spec);
497 
498   Py_DECREF(base);
499   Py_DECREF(methods);
500 
501   return state->message_map_container_type &&
502          state->scalar_map_container_type && state->map_iterator_type;
503 }
504