Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(8)

Side by Side Diff: third_party/protobuf/python/google/protobuf/pyext/repeated_composite_container.cc

Issue 1842653006: Update //third_party/protobuf to version 3. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: update README.chromium Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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 #ifndef _SHARED_PTR_H
38 #include <google/protobuf/stubs/shared_ptr.h>
39 #endif
40
41 #include <google/protobuf/stubs/logging.h>
42 #include <google/protobuf/stubs/common.h>
43 #include <google/protobuf/descriptor.h>
44 #include <google/protobuf/dynamic_message.h>
45 #include <google/protobuf/message.h>
46 #include <google/protobuf/pyext/descriptor.h>
47 #include <google/protobuf/pyext/descriptor_pool.h>
48 #include <google/protobuf/pyext/message.h>
49 #include <google/protobuf/pyext/scoped_pyobject_ptr.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 // TODO(tibell): We might also want to check:
64 // GOOGLE_CHECK_NOTNULL((self)->owner.get());
65 #define GOOGLE_CHECK_ATTACHED(self) \
66 do { \
67 GOOGLE_CHECK_NOTNULL((self)->message); \
68 GOOGLE_CHECK_NOTNULL((self)->parent_field_descriptor); \
69 } while (0);
70
71 #define GOOGLE_CHECK_RELEASED(self) \
72 do { \
73 GOOGLE_CHECK((self)->owner.get() == NULL); \
74 GOOGLE_CHECK((self)->message == NULL); \
75 GOOGLE_CHECK((self)->parent_field_descriptor == NULL); \
76 GOOGLE_CHECK((self)->parent == NULL); \
77 } while (0);
78
79 // ---------------------------------------------------------------------
80 // len()
81
82 static Py_ssize_t Length(RepeatedCompositeContainer* self) {
83 Message* message = self->message;
84 if (message != NULL) {
85 return message->GetReflection()->FieldSize(*message,
86 self->parent_field_descriptor);
87 } else {
88 // The container has been released (i.e. by a call to Clear() or
89 // ClearField() on the parent) and thus there's no message.
90 return PyList_GET_SIZE(self->child_messages);
91 }
92 }
93
94 // Returns 0 if successful; returns -1 and sets an exception if
95 // unsuccessful.
96 static int UpdateChildMessages(RepeatedCompositeContainer* self) {
97 if (self->message == NULL)
98 return 0;
99
100 // A MergeFrom on a parent message could have caused extra messages to be
101 // added in the underlying protobuf so add them to our list. They can never
102 // be removed in such a way so there's no need to worry about that.
103 Py_ssize_t message_length = Length(self);
104 Py_ssize_t child_length = PyList_GET_SIZE(self->child_messages);
105 Message* message = self->message;
106 const Reflection* reflection = message->GetReflection();
107 for (Py_ssize_t i = child_length; i < message_length; ++i) {
108 const Message& sub_message = reflection->GetRepeatedMessage(
109 *(self->message), self->parent_field_descriptor, i);
110 CMessage* cmsg = cmessage::NewEmptyMessage(self->subclass_init,
111 sub_message.GetDescriptor());
112 ScopedPyObjectPtr py_cmsg(reinterpret_cast<PyObject*>(cmsg));
113 if (cmsg == NULL) {
114 return -1;
115 }
116 cmsg->owner = self->owner;
117 cmsg->message = const_cast<Message*>(&sub_message);
118 cmsg->parent = self->parent;
119 if (PyList_Append(self->child_messages, py_cmsg.get()) < 0) {
120 return -1;
121 }
122 }
123 return 0;
124 }
125
126 // ---------------------------------------------------------------------
127 // add()
128
129 static PyObject* AddToAttached(RepeatedCompositeContainer* self,
130 PyObject* args,
131 PyObject* kwargs) {
132 GOOGLE_CHECK_ATTACHED(self);
133
134 if (UpdateChildMessages(self) < 0) {
135 return NULL;
136 }
137 if (cmessage::AssureWritable(self->parent) == -1)
138 return NULL;
139 Message* message = self->message;
140 Message* sub_message =
141 message->GetReflection()->AddMessage(message,
142 self->parent_field_descriptor);
143 CMessage* cmsg = cmessage::NewEmptyMessage(self->subclass_init,
144 sub_message->GetDescriptor());
145 if (cmsg == NULL)
146 return NULL;
147
148 cmsg->owner = self->owner;
149 cmsg->message = sub_message;
150 cmsg->parent = self->parent;
151 if (cmessage::InitAttributes(cmsg, kwargs) < 0) {
152 Py_DECREF(cmsg);
153 return NULL;
154 }
155
156 PyObject* py_cmsg = reinterpret_cast<PyObject*>(cmsg);
157 if (PyList_Append(self->child_messages, py_cmsg) < 0) {
158 Py_DECREF(py_cmsg);
159 return NULL;
160 }
161 return py_cmsg;
162 }
163
164 static PyObject* AddToReleased(RepeatedCompositeContainer* self,
165 PyObject* args,
166 PyObject* kwargs) {
167 GOOGLE_CHECK_RELEASED(self);
168
169 // Create a new Message detached from the rest.
170 PyObject* py_cmsg = PyEval_CallObjectWithKeywords(
171 self->subclass_init, NULL, kwargs);
172 if (py_cmsg == NULL)
173 return NULL;
174
175 if (PyList_Append(self->child_messages, py_cmsg) < 0) {
176 Py_DECREF(py_cmsg);
177 return NULL;
178 }
179 return py_cmsg;
180 }
181
182 PyObject* Add(RepeatedCompositeContainer* self,
183 PyObject* args,
184 PyObject* kwargs) {
185 if (self->message == NULL)
186 return AddToReleased(self, args, kwargs);
187 else
188 return AddToAttached(self, args, kwargs);
189 }
190
191 // ---------------------------------------------------------------------
192 // extend()
193
194 PyObject* Extend(RepeatedCompositeContainer* self, PyObject* value) {
195 cmessage::AssureWritable(self->parent);
196 if (UpdateChildMessages(self) < 0) {
197 return NULL;
198 }
199 ScopedPyObjectPtr iter(PyObject_GetIter(value));
200 if (iter == NULL) {
201 PyErr_SetString(PyExc_TypeError, "Value must be iterable");
202 return NULL;
203 }
204 ScopedPyObjectPtr next;
205 while ((next.reset(PyIter_Next(iter.get()))) != NULL) {
206 if (!PyObject_TypeCheck(next.get(), &CMessage_Type)) {
207 PyErr_SetString(PyExc_TypeError, "Not a cmessage");
208 return NULL;
209 }
210 ScopedPyObjectPtr new_message(Add(self, NULL, NULL));
211 if (new_message == NULL) {
212 return NULL;
213 }
214 CMessage* new_cmessage = reinterpret_cast<CMessage*>(new_message.get());
215 if (ScopedPyObjectPtr(cmessage::MergeFrom(new_cmessage, next.get())) ==
216 NULL) {
217 return NULL;
218 }
219 }
220 if (PyErr_Occurred()) {
221 return NULL;
222 }
223 Py_RETURN_NONE;
224 }
225
226 PyObject* MergeFrom(RepeatedCompositeContainer* self, PyObject* other) {
227 if (UpdateChildMessages(self) < 0) {
228 return NULL;
229 }
230 return Extend(self, other);
231 }
232
233 PyObject* Subscript(RepeatedCompositeContainer* self, PyObject* slice) {
234 if (UpdateChildMessages(self) < 0) {
235 return NULL;
236 }
237 // Just forward the call to the subscript-handling function of the
238 // list containing the child messages.
239 return PyObject_GetItem(self->child_messages, slice);
240 }
241
242 int AssignSubscript(RepeatedCompositeContainer* self,
243 PyObject* slice,
244 PyObject* value) {
245 if (UpdateChildMessages(self) < 0) {
246 return -1;
247 }
248 if (value != NULL) {
249 PyErr_SetString(PyExc_TypeError, "does not support assignment");
250 return -1;
251 }
252
253 // Delete from the underlying Message, if any.
254 if (self->parent != NULL) {
255 if (cmessage::InternalDeleteRepeatedField(self->parent,
256 self->parent_field_descriptor,
257 slice,
258 self->child_messages) < 0) {
259 return -1;
260 }
261 } else {
262 Py_ssize_t from;
263 Py_ssize_t to;
264 Py_ssize_t step;
265 Py_ssize_t length = Length(self);
266 Py_ssize_t slicelength;
267 if (PySlice_Check(slice)) {
268 #if PY_MAJOR_VERSION >= 3
269 if (PySlice_GetIndicesEx(slice,
270 #else
271 if (PySlice_GetIndicesEx(reinterpret_cast<PySliceObject*>(slice),
272 #endif
273 length, &from, &to, &step, &slicelength) == -1) {
274 return -1;
275 }
276 return PySequence_DelSlice(self->child_messages, from, to);
277 } else if (PyInt_Check(slice) || PyLong_Check(slice)) {
278 from = to = PyLong_AsLong(slice);
279 if (from < 0) {
280 from = to = length + from;
281 }
282 return PySequence_DelItem(self->child_messages, from);
283 }
284 }
285
286 return 0;
287 }
288
289 static PyObject* Remove(RepeatedCompositeContainer* self, PyObject* value) {
290 if (UpdateChildMessages(self) < 0) {
291 return NULL;
292 }
293 Py_ssize_t index = PySequence_Index(self->child_messages, value);
294 if (index == -1) {
295 return NULL;
296 }
297 ScopedPyObjectPtr py_index(PyLong_FromLong(index));
298 if (AssignSubscript(self, py_index.get(), NULL) < 0) {
299 return NULL;
300 }
301 Py_RETURN_NONE;
302 }
303
304 static PyObject* RichCompare(RepeatedCompositeContainer* self,
305 PyObject* other,
306 int opid) {
307 if (UpdateChildMessages(self) < 0) {
308 return NULL;
309 }
310 if (!PyObject_TypeCheck(other, &RepeatedCompositeContainer_Type)) {
311 PyErr_SetString(PyExc_TypeError,
312 "Can only compare repeated composite fields "
313 "against other repeated composite fields.");
314 return NULL;
315 }
316 if (opid == Py_EQ || opid == Py_NE) {
317 // TODO(anuraag): Don't make new lists just for this...
318 ScopedPyObjectPtr full_slice(PySlice_New(NULL, NULL, NULL));
319 if (full_slice == NULL) {
320 return NULL;
321 }
322 ScopedPyObjectPtr list(Subscript(self, full_slice.get()));
323 if (list == NULL) {
324 return NULL;
325 }
326 ScopedPyObjectPtr other_list(
327 Subscript(reinterpret_cast<RepeatedCompositeContainer*>(other),
328 full_slice.get()));
329 if (other_list == NULL) {
330 return NULL;
331 }
332 return PyObject_RichCompare(list.get(), other_list.get(), opid);
333 } else {
334 Py_INCREF(Py_NotImplemented);
335 return Py_NotImplemented;
336 }
337 }
338
339 // ---------------------------------------------------------------------
340 // sort()
341
342 static void ReorderAttached(RepeatedCompositeContainer* self) {
343 Message* message = self->message;
344 const Reflection* reflection = message->GetReflection();
345 const FieldDescriptor* descriptor = self->parent_field_descriptor;
346 const Py_ssize_t length = Length(self);
347
348 // Since Python protobuf objects are never arena-allocated, adding and
349 // removing message pointers to the underlying array is just updating
350 // pointers.
351 for (Py_ssize_t i = 0; i < length; ++i)
352 reflection->ReleaseLast(message, descriptor);
353
354 for (Py_ssize_t i = 0; i < length; ++i) {
355 CMessage* py_cmsg = reinterpret_cast<CMessage*>(
356 PyList_GET_ITEM(self->child_messages, i));
357 reflection->AddAllocatedMessage(message, descriptor, py_cmsg->message);
358 }
359 }
360
361 // Returns 0 if successful; returns -1 and sets an exception if
362 // unsuccessful.
363 static int SortPythonMessages(RepeatedCompositeContainer* self,
364 PyObject* args,
365 PyObject* kwds) {
366 ScopedPyObjectPtr m(PyObject_GetAttrString(self->child_messages, "sort"));
367 if (m == NULL)
368 return -1;
369 if (PyObject_Call(m.get(), args, kwds) == NULL)
370 return -1;
371 if (self->message != NULL) {
372 ReorderAttached(self);
373 }
374 return 0;
375 }
376
377 static PyObject* Sort(RepeatedCompositeContainer* self,
378 PyObject* args,
379 PyObject* kwds) {
380 // Support the old sort_function argument for backwards
381 // compatibility.
382 if (kwds != NULL) {
383 PyObject* sort_func = PyDict_GetItemString(kwds, "sort_function");
384 if (sort_func != NULL) {
385 // Must set before deleting as sort_func is a borrowed reference
386 // and kwds might be the only thing keeping it alive.
387 PyDict_SetItemString(kwds, "cmp", sort_func);
388 PyDict_DelItemString(kwds, "sort_function");
389 }
390 }
391
392 if (UpdateChildMessages(self) < 0) {
393 return NULL;
394 }
395 if (SortPythonMessages(self, args, kwds) < 0) {
396 return NULL;
397 }
398 Py_RETURN_NONE;
399 }
400
401 // ---------------------------------------------------------------------
402
403 static PyObject* Item(RepeatedCompositeContainer* self, Py_ssize_t index) {
404 if (UpdateChildMessages(self) < 0) {
405 return NULL;
406 }
407 Py_ssize_t length = Length(self);
408 if (index < 0) {
409 index = length + index;
410 }
411 PyObject* item = PyList_GetItem(self->child_messages, index);
412 if (item == NULL) {
413 return NULL;
414 }
415 Py_INCREF(item);
416 return item;
417 }
418
419 static PyObject* Pop(RepeatedCompositeContainer* self,
420 PyObject* args) {
421 Py_ssize_t index = -1;
422 if (!PyArg_ParseTuple(args, "|n", &index)) {
423 return NULL;
424 }
425 PyObject* item = Item(self, index);
426 if (item == NULL) {
427 PyErr_Format(PyExc_IndexError,
428 "list index (%zd) out of range",
429 index);
430 return NULL;
431 }
432 ScopedPyObjectPtr py_index(PyLong_FromSsize_t(index));
433 if (AssignSubscript(self, py_index.get(), NULL) < 0) {
434 return NULL;
435 }
436 return item;
437 }
438
439 // Release field of parent message and transfer the ownership to target.
440 void ReleaseLastTo(CMessage* parent,
441 const FieldDescriptor* field,
442 CMessage* target) {
443 GOOGLE_CHECK_NOTNULL(parent);
444 GOOGLE_CHECK_NOTNULL(field);
445 GOOGLE_CHECK_NOTNULL(target);
446
447 shared_ptr<Message> released_message(
448 parent->message->GetReflection()->ReleaseLast(parent->message, field));
449 // TODO(tibell): Deal with proto1.
450
451 target->parent = NULL;
452 target->parent_field_descriptor = NULL;
453 target->message = released_message.get();
454 target->read_only = false;
455 cmessage::SetOwner(target, released_message);
456 }
457
458 // Called to release a container using
459 // ClearField('container_field_name') on the parent.
460 int Release(RepeatedCompositeContainer* self) {
461 if (UpdateChildMessages(self) < 0) {
462 PyErr_WriteUnraisable(PyBytes_FromString("Failed to update released "
463 "messages"));
464 return -1;
465 }
466
467 Message* message = self->message;
468 const FieldDescriptor* field = self->parent_field_descriptor;
469
470 // The reflection API only lets us release the last message in a
471 // repeated field. Therefore we iterate through the children
472 // starting with the last one.
473 const Py_ssize_t size = PyList_GET_SIZE(self->child_messages);
474 GOOGLE_DCHECK_EQ(size, message->GetReflection()->FieldSize(*message, field));
475 for (Py_ssize_t i = size - 1; i >= 0; --i) {
476 CMessage* child_cmessage = reinterpret_cast<CMessage*>(
477 PyList_GET_ITEM(self->child_messages, i));
478 ReleaseLastTo(self->parent, field, child_cmessage);
479 }
480
481 // Detach from containing message.
482 self->parent = NULL;
483 self->parent_field_descriptor = NULL;
484 self->message = NULL;
485 self->owner.reset();
486
487 return 0;
488 }
489
490 int SetOwner(RepeatedCompositeContainer* self,
491 const shared_ptr<Message>& new_owner) {
492 GOOGLE_CHECK_ATTACHED(self);
493
494 self->owner = new_owner;
495 const Py_ssize_t n = PyList_GET_SIZE(self->child_messages);
496 for (Py_ssize_t i = 0; i < n; ++i) {
497 PyObject* msg = PyList_GET_ITEM(self->child_messages, i);
498 if (cmessage::SetOwner(reinterpret_cast<CMessage*>(msg), new_owner) == -1) {
499 return -1;
500 }
501 }
502 return 0;
503 }
504
505 // The private constructor of RepeatedCompositeContainer objects.
506 PyObject *NewContainer(
507 CMessage* parent,
508 const FieldDescriptor* parent_field_descriptor,
509 PyObject *concrete_class) {
510 if (!CheckFieldBelongsToMessage(parent_field_descriptor, parent->message)) {
511 return NULL;
512 }
513
514 RepeatedCompositeContainer* self =
515 reinterpret_cast<RepeatedCompositeContainer*>(
516 PyType_GenericAlloc(&RepeatedCompositeContainer_Type, 0));
517 if (self == NULL) {
518 return NULL;
519 }
520
521 self->message = parent->message;
522 self->parent = parent;
523 self->parent_field_descriptor = parent_field_descriptor;
524 self->owner = parent->owner;
525 Py_INCREF(concrete_class);
526 self->subclass_init = concrete_class;
527 self->child_messages = PyList_New(0);
528
529 return reinterpret_cast<PyObject*>(self);
530 }
531
532 static void Dealloc(RepeatedCompositeContainer* self) {
533 Py_CLEAR(self->child_messages);
534 Py_CLEAR(self->subclass_init);
535 // TODO(tibell): Do we need to call delete on these objects to make
536 // sure their destructors are called?
537 self->owner.reset();
538
539 Py_TYPE(self)->tp_free(reinterpret_cast<PyObject*>(self));
540 }
541
542 static PySequenceMethods SqMethods = {
543 (lenfunc)Length, /* sq_length */
544 0, /* sq_concat */
545 0, /* sq_repeat */
546 (ssizeargfunc)Item /* sq_item */
547 };
548
549 static PyMappingMethods MpMethods = {
550 (lenfunc)Length, /* mp_length */
551 (binaryfunc)Subscript, /* mp_subscript */
552 (objobjargproc)AssignSubscript,/* mp_ass_subscript */
553 };
554
555 static PyMethodDef Methods[] = {
556 { "add", (PyCFunction) Add, METH_VARARGS | METH_KEYWORDS,
557 "Adds an object to the repeated container." },
558 { "extend", (PyCFunction) Extend, METH_O,
559 "Adds objects to the repeated container." },
560 { "pop", (PyCFunction)Pop, METH_VARARGS,
561 "Removes an object from the repeated container and returns it." },
562 { "remove", (PyCFunction) Remove, METH_O,
563 "Removes an object from the repeated container." },
564 { "sort", (PyCFunction) Sort, METH_VARARGS | METH_KEYWORDS,
565 "Sorts the repeated container." },
566 { "MergeFrom", (PyCFunction) MergeFrom, METH_O,
567 "Adds objects to the repeated container." },
568 { NULL, NULL }
569 };
570
571 } // namespace repeated_composite_container
572
573 PyTypeObject RepeatedCompositeContainer_Type = {
574 PyVarObject_HEAD_INIT(&PyType_Type, 0)
575 FULL_MODULE_NAME ".RepeatedCompositeContainer", // tp_name
576 sizeof(RepeatedCompositeContainer), // tp_basicsize
577 0, // tp_itemsize
578 (destructor)repeated_composite_container::Dealloc, // tp_dealloc
579 0, // tp_print
580 0, // tp_getattr
581 0, // tp_setattr
582 0, // tp_compare
583 0, // tp_repr
584 0, // tp_as_number
585 &repeated_composite_container::SqMethods, // tp_as_sequence
586 &repeated_composite_container::MpMethods, // tp_as_mapping
587 PyObject_HashNotImplemented, // tp_hash
588 0, // tp_call
589 0, // tp_str
590 0, // tp_getattro
591 0, // tp_setattro
592 0, // tp_as_buffer
593 Py_TPFLAGS_DEFAULT, // tp_flags
594 "A Repeated scalar container", // tp_doc
595 0, // tp_traverse
596 0, // tp_clear
597 (richcmpfunc)repeated_composite_container::RichCompare, // tp_richcompare
598 0, // tp_weaklistoffset
599 0, // tp_iter
600 0, // tp_iternext
601 repeated_composite_container::Methods, // tp_methods
602 0, // tp_members
603 0, // tp_getset
604 0, // tp_base
605 0, // tp_dict
606 0, // tp_descr_get
607 0, // tp_descr_set
608 0, // tp_dictoffset
609 0, // tp_init
610 };
611
612 } // namespace python
613 } // namespace protobuf
614 } // namespace google
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698