Home | History | Annotate | Download | only in pyext
      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 (at) google.com (Anuraag Agrawal)
     32 // Author: tibell (at) 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->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 
    128 static 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 
    162 static 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 
    180 PyObject* 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 
    192 PyObject* 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 
    224 PyObject* MergeFrom(RepeatedCompositeContainer* self, PyObject* other) {
    225   if (UpdateChildMessages(self) < 0) {
    226     return NULL;
    227   }
    228   return Extend(self, other);
    229 }
    230 
    231 PyObject* 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 
    240 int 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 
    287 static 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 
    302 static 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 
    340 static 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.
    361 static 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 
    375 static 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 
    401 static 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 
    417 static 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.
    438 void 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.
    458 int 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 
    488 int 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.
    504 PyObject *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 
    530 static 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 
    540 static PySequenceMethods SqMethods = {
    541   (lenfunc)Length,        /* sq_length */
    542   0, /* sq_concat */
    543   0, /* sq_repeat */
    544   (ssizeargfunc)Item /* sq_item */
    545 };
    546 
    547 static PyMappingMethods MpMethods = {
    548   (lenfunc)Length,               /* mp_length */
    549   (binaryfunc)Subscript,      /* mp_subscript */
    550   (objobjargproc)AssignSubscript,/* mp_ass_subscript */
    551 };
    552 
    553 static 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 
    571 PyTypeObject 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