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