• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc.  All rights reserved.
3 // https://developers.google.com/protocol-buffers/
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are
7 // met:
8 //
9 //     * Redistributions of source code must retain the above copyright
10 // notice, this list of conditions and the following disclaimer.
11 //     * Redistributions in binary form must reproduce the above
12 // copyright notice, this list of conditions and the following disclaimer
13 // in the documentation and/or other materials provided with the
14 // distribution.
15 //     * Neither the name of Google Inc. nor the names of its
16 // contributors may be used to endorse or promote products derived from
17 // this software without specific prior written permission.
18 //
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 
31 // Author: anuraag@google.com (Anuraag Agrawal)
32 // Author: tibell@google.com (Johan Tibell)
33 
34 #include <google/protobuf/pyext/repeated_composite_container.h>
35 
36 #include <memory>
37 
38 #include <google/protobuf/stubs/logging.h>
39 #include <google/protobuf/stubs/common.h>
40 #include <google/protobuf/descriptor.h>
41 #include <google/protobuf/dynamic_message.h>
42 #include <google/protobuf/message.h>
43 #include <google/protobuf/pyext/descriptor.h>
44 #include <google/protobuf/pyext/descriptor_pool.h>
45 #include <google/protobuf/pyext/message.h>
46 #include <google/protobuf/pyext/message_factory.h>
47 #include <google/protobuf/pyext/scoped_pyobject_ptr.h>
48 #include <google/protobuf/reflection.h>
49 #include <google/protobuf/stubs/map_util.h>
50 
51 #if PY_MAJOR_VERSION >= 3
52   #define PyInt_Check PyLong_Check
53   #define PyInt_AsLong PyLong_AsLong
54   #define PyInt_FromLong PyLong_FromLong
55 #endif
56 
57 namespace google {
58 namespace protobuf {
59 namespace python {
60 
61 namespace repeated_composite_container {
62 
63 // ---------------------------------------------------------------------
64 // len()
65 
Length(PyObject * pself)66 static Py_ssize_t Length(PyObject* pself) {
67   RepeatedCompositeContainer* self =
68       reinterpret_cast<RepeatedCompositeContainer*>(pself);
69 
70   Message* message = self->parent->message;
71   return message->GetReflection()->FieldSize(*message,
72                                              self->parent_field_descriptor);
73 }
74 
75 // ---------------------------------------------------------------------
76 // add()
77 
Add(RepeatedCompositeContainer * self,PyObject * args,PyObject * kwargs)78 PyObject* Add(RepeatedCompositeContainer* self, PyObject* args,
79               PyObject* kwargs) {
80   if (cmessage::AssureWritable(self->parent) == -1)
81     return NULL;
82   Message* message = self->parent->message;
83 
84   Message* sub_message =
85       message->GetReflection()->AddMessage(
86           message,
87           self->parent_field_descriptor,
88           self->child_message_class->py_message_factory->message_factory);
89   CMessage* cmsg = self->parent->BuildSubMessageFromPointer(
90       self->parent_field_descriptor, sub_message, self->child_message_class);
91 
92   if (cmessage::InitAttributes(cmsg, args, kwargs) < 0) {
93     message->GetReflection()->RemoveLast(
94         message, self->parent_field_descriptor);
95     Py_DECREF(cmsg);
96     return NULL;
97   }
98 
99   return cmsg->AsPyObject();
100 }
101 
AddMethod(PyObject * self,PyObject * args,PyObject * kwargs)102 static PyObject* AddMethod(PyObject* self, PyObject* args, PyObject* kwargs) {
103   return Add(reinterpret_cast<RepeatedCompositeContainer*>(self), args, kwargs);
104 }
105 
106 // ---------------------------------------------------------------------
107 // append()
108 
AddMessage(RepeatedCompositeContainer * self,PyObject * value)109 static PyObject* AddMessage(RepeatedCompositeContainer* self, PyObject* value) {
110   cmessage::AssureWritable(self->parent);
111   PyObject* py_cmsg;
112   Message* message = self->parent->message;
113   const Reflection* reflection = message->GetReflection();
114   py_cmsg = Add(self, nullptr, nullptr);
115   if (py_cmsg == nullptr) return nullptr;
116   CMessage* cmsg = reinterpret_cast<CMessage*>(py_cmsg);
117   if (ScopedPyObjectPtr(cmessage::MergeFrom(cmsg, value)) == nullptr) {
118     reflection->RemoveLast(
119         message, self->parent_field_descriptor);
120     Py_DECREF(cmsg);
121     return nullptr;
122   }
123   return py_cmsg;
124 }
125 
AppendMethod(PyObject * pself,PyObject * value)126 static PyObject* AppendMethod(PyObject* pself, PyObject* value) {
127   RepeatedCompositeContainer* self =
128       reinterpret_cast<RepeatedCompositeContainer*>(pself);
129   ScopedPyObjectPtr py_cmsg(AddMessage(self, value));
130   if (py_cmsg == nullptr) {
131     return nullptr;
132   }
133 
134   Py_RETURN_NONE;
135 }
136 
137 // ---------------------------------------------------------------------
138 // insert()
Insert(PyObject * pself,PyObject * args)139 static PyObject* Insert(PyObject* pself, PyObject* args) {
140   RepeatedCompositeContainer* self =
141       reinterpret_cast<RepeatedCompositeContainer*>(pself);
142 
143   Py_ssize_t index;
144   PyObject* value;
145   if (!PyArg_ParseTuple(args, "nO", &index, &value)) {
146     return nullptr;
147   }
148 
149   ScopedPyObjectPtr py_cmsg(AddMessage(self, value));
150   if (py_cmsg == nullptr) {
151     return nullptr;
152   }
153 
154   // Swap the element to right position.
155   Message* message = self->parent->message;
156   const Reflection* reflection = message->GetReflection();
157   const FieldDescriptor* field_descriptor = self->parent_field_descriptor;
158   Py_ssize_t length = reflection->FieldSize(*message, field_descriptor) - 1;
159   Py_ssize_t end_index = index;
160   if (end_index < 0) end_index += length;
161   if (end_index < 0) end_index = 0;
162   for (Py_ssize_t i = length; i > end_index; i --) {
163     reflection->SwapElements(message, field_descriptor, i, i - 1);
164   }
165 
166   Py_RETURN_NONE;
167 }
168 
169 // ---------------------------------------------------------------------
170 // extend()
171 
Extend(RepeatedCompositeContainer * self,PyObject * value)172 PyObject* Extend(RepeatedCompositeContainer* self, PyObject* value) {
173   cmessage::AssureWritable(self->parent);
174   ScopedPyObjectPtr iter(PyObject_GetIter(value));
175   if (iter == NULL) {
176     PyErr_SetString(PyExc_TypeError, "Value must be iterable");
177     return NULL;
178   }
179   ScopedPyObjectPtr next;
180   while ((next.reset(PyIter_Next(iter.get()))) != NULL) {
181     if (!PyObject_TypeCheck(next.get(), CMessage_Type)) {
182       PyErr_SetString(PyExc_TypeError, "Not a cmessage");
183       return NULL;
184     }
185     ScopedPyObjectPtr new_message(Add(self, NULL, NULL));
186     if (new_message == NULL) {
187       return NULL;
188     }
189     CMessage* new_cmessage = reinterpret_cast<CMessage*>(new_message.get());
190     if (ScopedPyObjectPtr(cmessage::MergeFrom(new_cmessage, next.get())) ==
191         NULL) {
192       return NULL;
193     }
194   }
195   if (PyErr_Occurred()) {
196     return NULL;
197   }
198   Py_RETURN_NONE;
199 }
200 
ExtendMethod(PyObject * self,PyObject * value)201 static PyObject* ExtendMethod(PyObject* self, PyObject* value) {
202   return Extend(reinterpret_cast<RepeatedCompositeContainer*>(self), value);
203 }
204 
MergeFrom(RepeatedCompositeContainer * self,PyObject * other)205 PyObject* MergeFrom(RepeatedCompositeContainer* self, PyObject* other) {
206   return Extend(self, other);
207 }
208 
MergeFromMethod(PyObject * self,PyObject * other)209 static PyObject* MergeFromMethod(PyObject* self, PyObject* other) {
210   return MergeFrom(reinterpret_cast<RepeatedCompositeContainer*>(self), other);
211 }
212 
213 // This function does not check the bounds.
GetItem(RepeatedCompositeContainer * self,Py_ssize_t index,Py_ssize_t length=-1)214 static PyObject* GetItem(RepeatedCompositeContainer* self, Py_ssize_t index,
215                          Py_ssize_t length = -1) {
216   if (length == -1) {
217     Message* message = self->parent->message;
218     const Reflection* reflection = message->GetReflection();
219     length = reflection->FieldSize(*message, self->parent_field_descriptor);
220   }
221   if (index < 0 || index >= length) {
222     PyErr_Format(PyExc_IndexError, "list index (%zd) out of range", index);
223     return NULL;
224   }
225   Message* message = self->parent->message;
226   Message* sub_message = message->GetReflection()->MutableRepeatedMessage(
227       message, self->parent_field_descriptor, index);
228   return self->parent
229       ->BuildSubMessageFromPointer(self->parent_field_descriptor, sub_message,
230                                    self->child_message_class)
231       ->AsPyObject();
232 }
233 
Subscript(RepeatedCompositeContainer * self,PyObject * item)234 PyObject* Subscript(RepeatedCompositeContainer* self, PyObject* item) {
235   Message* message = self->parent->message;
236   const Reflection* reflection = message->GetReflection();
237   Py_ssize_t length =
238       reflection->FieldSize(*message, self->parent_field_descriptor);
239 
240   if (PyIndex_Check(item)) {
241     Py_ssize_t index;
242     index = PyNumber_AsSsize_t(item, PyExc_IndexError);
243     if (index == -1 && PyErr_Occurred()) return NULL;
244     if (index < 0) index += length;
245     return GetItem(self, index, length);
246   } else if (PySlice_Check(item)) {
247     Py_ssize_t from, to, step, slicelength, cur, i;
248     PyObject* result;
249 
250 #if PY_MAJOR_VERSION >= 3
251     if (PySlice_GetIndicesEx(item,
252                              length, &from, &to, &step, &slicelength) == -1) {
253 #else
254     if (PySlice_GetIndicesEx(reinterpret_cast<PySliceObject*>(item),
255                              length, &from, &to, &step, &slicelength) == -1) {
256 #endif
257       return NULL;
258     }
259 
260     if (slicelength <= 0) {
261       return PyList_New(0);
262     } else {
263       result = PyList_New(slicelength);
264       if (!result) return NULL;
265 
266       for (cur = from, i = 0; i < slicelength; cur += step, i++) {
267         PyList_SET_ITEM(result, i, GetItem(self, cur, length));
268       }
269 
270       return result;
271     }
272   } else {
273     PyErr_Format(PyExc_TypeError, "indices must be integers, not %.200s",
274                  item->ob_type->tp_name);
275     return NULL;
276   }
277 }
278 
279 static PyObject* SubscriptMethod(PyObject* self, PyObject* slice) {
280   return Subscript(reinterpret_cast<RepeatedCompositeContainer*>(self), slice);
281 }
282 
283 int AssignSubscript(RepeatedCompositeContainer* self,
284                     PyObject* slice,
285                     PyObject* value) {
286   if (value != NULL) {
287     PyErr_SetString(PyExc_TypeError, "does not support assignment");
288     return -1;
289   }
290 
291   return cmessage::DeleteRepeatedField(self->parent,
292                                        self->parent_field_descriptor, slice);
293 }
294 
295 static int AssignSubscriptMethod(PyObject* self, PyObject* slice,
296                                  PyObject* value) {
297   return AssignSubscript(reinterpret_cast<RepeatedCompositeContainer*>(self),
298                          slice, value);
299 }
300 
301 static PyObject* Remove(PyObject* pself, PyObject* value) {
302   RepeatedCompositeContainer* self =
303       reinterpret_cast<RepeatedCompositeContainer*>(pself);
304   Py_ssize_t len = Length(reinterpret_cast<PyObject*>(self));
305 
306   for (Py_ssize_t i = 0; i < len; i++) {
307     ScopedPyObjectPtr item(GetItem(self, i, len));
308     if (item == NULL) {
309       return NULL;
310     }
311     int result = PyObject_RichCompareBool(item.get(), value, Py_EQ);
312     if (result < 0) {
313       return NULL;
314     }
315     if (result) {
316       ScopedPyObjectPtr py_index(PyLong_FromSsize_t(i));
317       if (AssignSubscript(self, py_index.get(), NULL) < 0) {
318         return NULL;
319       }
320       Py_RETURN_NONE;
321     }
322   }
323   PyErr_SetString(PyExc_ValueError, "Item to delete not in list");
324   return NULL;
325 }
326 
327 static PyObject* RichCompare(PyObject* pself, PyObject* other, int opid) {
328   RepeatedCompositeContainer* self =
329       reinterpret_cast<RepeatedCompositeContainer*>(pself);
330 
331   if (!PyObject_TypeCheck(other, &RepeatedCompositeContainer_Type)) {
332     PyErr_SetString(PyExc_TypeError,
333                     "Can only compare repeated composite fields "
334                     "against other repeated composite fields.");
335     return NULL;
336   }
337   if (opid == Py_EQ || opid == Py_NE) {
338     // TODO(anuraag): Don't make new lists just for this...
339     ScopedPyObjectPtr full_slice(PySlice_New(NULL, NULL, NULL));
340     if (full_slice == NULL) {
341       return NULL;
342     }
343     ScopedPyObjectPtr list(Subscript(self, full_slice.get()));
344     if (list == NULL) {
345       return NULL;
346     }
347     ScopedPyObjectPtr other_list(
348         Subscript(reinterpret_cast<RepeatedCompositeContainer*>(other),
349                   full_slice.get()));
350     if (other_list == NULL) {
351       return NULL;
352     }
353     return PyObject_RichCompare(list.get(), other_list.get(), opid);
354   } else {
355     Py_INCREF(Py_NotImplemented);
356     return Py_NotImplemented;
357   }
358 }
359 
360 static PyObject* ToStr(PyObject* pself) {
361   ScopedPyObjectPtr full_slice(PySlice_New(NULL, NULL, NULL));
362   if (full_slice == NULL) {
363     return NULL;
364   }
365   ScopedPyObjectPtr list(Subscript(
366       reinterpret_cast<RepeatedCompositeContainer*>(pself), full_slice.get()));
367   if (list == NULL) {
368     return NULL;
369   }
370   return PyObject_Repr(list.get());
371 }
372 
373 // ---------------------------------------------------------------------
374 // sort()
375 
376 static void ReorderAttached(RepeatedCompositeContainer* self,
377                             PyObject* child_list) {
378   Message* message = self->parent->message;
379   const Reflection* reflection = message->GetReflection();
380   const FieldDescriptor* descriptor = self->parent_field_descriptor;
381   const Py_ssize_t length = Length(reinterpret_cast<PyObject*>(self));
382 
383   // Since Python protobuf objects are never arena-allocated, adding and
384   // removing message pointers to the underlying array is just updating
385   // pointers.
386   for (Py_ssize_t i = 0; i < length; ++i)
387     reflection->ReleaseLast(message, descriptor);
388 
389   for (Py_ssize_t i = 0; i < length; ++i) {
390     CMessage* py_cmsg = reinterpret_cast<CMessage*>(
391         PyList_GET_ITEM(child_list, i));
392     reflection->AddAllocatedMessage(message, descriptor, py_cmsg->message);
393   }
394 }
395 
396 // Returns 0 if successful; returns -1 and sets an exception if
397 // unsuccessful.
398 static int SortPythonMessages(RepeatedCompositeContainer* self,
399                                PyObject* args,
400                                PyObject* kwds) {
401   ScopedPyObjectPtr child_list(
402       PySequence_List(reinterpret_cast<PyObject*>(self)));
403   if (child_list == NULL) {
404     return -1;
405   }
406   ScopedPyObjectPtr m(PyObject_GetAttrString(child_list.get(), "sort"));
407   if (m == NULL)
408     return -1;
409   if (ScopedPyObjectPtr(PyObject_Call(m.get(), args, kwds)) == NULL)
410     return -1;
411   ReorderAttached(self, child_list.get());
412   return 0;
413 }
414 
415 static PyObject* Sort(PyObject* pself, PyObject* args, PyObject* kwds) {
416   RepeatedCompositeContainer* self =
417       reinterpret_cast<RepeatedCompositeContainer*>(pself);
418 
419   // Support the old sort_function argument for backwards
420   // compatibility.
421   if (kwds != NULL) {
422     PyObject* sort_func = PyDict_GetItemString(kwds, "sort_function");
423     if (sort_func != NULL) {
424       // Must set before deleting as sort_func is a borrowed reference
425       // and kwds might be the only thing keeping it alive.
426       PyDict_SetItemString(kwds, "cmp", sort_func);
427       PyDict_DelItemString(kwds, "sort_function");
428     }
429   }
430 
431   if (SortPythonMessages(self, args, kwds) < 0) {
432     return NULL;
433   }
434   Py_RETURN_NONE;
435 }
436 
437 // ---------------------------------------------------------------------
438 
439 static PyObject* Item(PyObject* pself, Py_ssize_t index) {
440   RepeatedCompositeContainer* self =
441       reinterpret_cast<RepeatedCompositeContainer*>(pself);
442   return GetItem(self, index);
443 }
444 
445 static PyObject* Pop(PyObject* pself, PyObject* args) {
446   RepeatedCompositeContainer* self =
447       reinterpret_cast<RepeatedCompositeContainer*>(pself);
448 
449   Py_ssize_t index = -1;
450   if (!PyArg_ParseTuple(args, "|n", &index)) {
451     return NULL;
452   }
453   Py_ssize_t length = Length(pself);
454   if (index < 0) index += length;
455   PyObject* item = GetItem(self, index, length);
456   if (item == NULL) {
457     return NULL;
458   }
459   ScopedPyObjectPtr py_index(PyLong_FromSsize_t(index));
460   if (AssignSubscript(self, py_index.get(), NULL) < 0) {
461     return NULL;
462   }
463   return item;
464 }
465 
466 PyObject* DeepCopy(PyObject* pself, PyObject* arg) {
467   return reinterpret_cast<RepeatedCompositeContainer*>(pself)->DeepCopy();
468 }
469 
470 // The private constructor of RepeatedCompositeContainer objects.
471 RepeatedCompositeContainer *NewContainer(
472     CMessage* parent,
473     const FieldDescriptor* parent_field_descriptor,
474     CMessageClass* child_message_class) {
475   if (!CheckFieldBelongsToMessage(parent_field_descriptor, parent->message)) {
476     return NULL;
477   }
478 
479   RepeatedCompositeContainer* self =
480       reinterpret_cast<RepeatedCompositeContainer*>(
481           PyType_GenericAlloc(&RepeatedCompositeContainer_Type, 0));
482   if (self == NULL) {
483     return NULL;
484   }
485 
486   Py_INCREF(parent);
487   self->parent = parent;
488   self->parent_field_descriptor = parent_field_descriptor;
489   Py_INCREF(child_message_class);
490   self->child_message_class = child_message_class;
491   return self;
492 }
493 
494 static void Dealloc(PyObject* pself) {
495   RepeatedCompositeContainer* self =
496       reinterpret_cast<RepeatedCompositeContainer*>(pself);
497   self->RemoveFromParentCache();
498   Py_CLEAR(self->child_message_class);
499   Py_TYPE(self)->tp_free(pself);
500 }
501 
502 static PySequenceMethods SqMethods = {
503   Length,                 /* sq_length */
504   0,                      /* sq_concat */
505   0,                      /* sq_repeat */
506   Item                    /* sq_item */
507 };
508 
509 static PyMappingMethods MpMethods = {
510   Length,                 /* mp_length */
511   SubscriptMethod,        /* mp_subscript */
512   AssignSubscriptMethod,  /* mp_ass_subscript */
513 };
514 
515 static PyMethodDef Methods[] = {
516   { "__deepcopy__", DeepCopy, METH_VARARGS,
517     "Makes a deep copy of the class." },
518   { "add", (PyCFunction)AddMethod, METH_VARARGS | METH_KEYWORDS,
519     "Adds an object to the repeated container." },
520   { "append", AppendMethod, METH_O,
521     "Appends a message to the end of the repeated container."},
522   { "insert", Insert, METH_VARARGS,
523     "Inserts a message before the specified index." },
524   { "extend", ExtendMethod, METH_O,
525     "Adds objects to the repeated container." },
526   { "pop", Pop, METH_VARARGS,
527     "Removes an object from the repeated container and returns it." },
528   { "remove", Remove, METH_O,
529     "Removes an object from the repeated container." },
530   { "sort", (PyCFunction)Sort, METH_VARARGS | METH_KEYWORDS,
531     "Sorts the repeated container." },
532   { "MergeFrom", MergeFromMethod, METH_O,
533     "Adds objects to the repeated container." },
534   { NULL, NULL }
535 };
536 
537 }  // namespace repeated_composite_container
538 
539 PyTypeObject RepeatedCompositeContainer_Type = {
540   PyVarObject_HEAD_INIT(&PyType_Type, 0)
541   FULL_MODULE_NAME ".RepeatedCompositeContainer",  // tp_name
542   sizeof(RepeatedCompositeContainer),  // tp_basicsize
543   0,                                   //  tp_itemsize
544   repeated_composite_container::Dealloc,  //  tp_dealloc
545   0,                                   //  tp_print
546   0,                                   //  tp_getattr
547   0,                                   //  tp_setattr
548   0,                                   //  tp_compare
549   repeated_composite_container::ToStr,      //  tp_repr
550   0,                                   //  tp_as_number
551   &repeated_composite_container::SqMethods,   //  tp_as_sequence
552   &repeated_composite_container::MpMethods,   //  tp_as_mapping
553   PyObject_HashNotImplemented,         //  tp_hash
554   0,                                   //  tp_call
555   0,                                   //  tp_str
556   0,                                   //  tp_getattro
557   0,                                   //  tp_setattro
558   0,                                   //  tp_as_buffer
559   Py_TPFLAGS_DEFAULT,                  //  tp_flags
560   "A Repeated scalar container",       //  tp_doc
561   0,                                   //  tp_traverse
562   0,                                   //  tp_clear
563   repeated_composite_container::RichCompare,  //  tp_richcompare
564   0,                                   //  tp_weaklistoffset
565   0,                                   //  tp_iter
566   0,                                   //  tp_iternext
567   repeated_composite_container::Methods,   //  tp_methods
568   0,                                   //  tp_members
569   0,                                   //  tp_getset
570   0,                                   //  tp_base
571   0,                                   //  tp_dict
572   0,                                   //  tp_descr_get
573   0,                                   //  tp_descr_set
574   0,                                   //  tp_dictoffset
575   0,                                   //  tp_init
576 };
577 
578 }  // namespace python
579 }  // namespace protobuf
580 }  // namespace google
581