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