• 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 #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 
Length(RepeatedCompositeContainer * self)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.
UpdateChildMessages(RepeatedCompositeContainer * self)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 
AddToAttached(RepeatedCompositeContainer * self,PyObject * args,PyObject * kwargs)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, args, 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 
AddToReleased(RepeatedCompositeContainer * self,PyObject * args,PyObject * kwargs)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(), args, 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 
Add(RepeatedCompositeContainer * self,PyObject * args,PyObject * kwargs)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 
Extend(RepeatedCompositeContainer * self,PyObject * value)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 
MergeFrom(RepeatedCompositeContainer * self,PyObject * other)224 PyObject* MergeFrom(RepeatedCompositeContainer* self, PyObject* other) {
225   if (UpdateChildMessages(self) < 0) {
226     return NULL;
227   }
228   return Extend(self, other);
229 }
230 
Subscript(RepeatedCompositeContainer * self,PyObject * slice)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 
AssignSubscript(RepeatedCompositeContainer * self,PyObject * slice,PyObject * value)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 
Remove(RepeatedCompositeContainer * self,PyObject * value)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 
RichCompare(RepeatedCompositeContainer * self,PyObject * other,int opid)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 
ReorderAttached(RepeatedCompositeContainer * self)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.
SortPythonMessages(RepeatedCompositeContainer * self,PyObject * args,PyObject * kwds)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 
Sort(RepeatedCompositeContainer * self,PyObject * args,PyObject * kwds)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 
Item(RepeatedCompositeContainer * self,Py_ssize_t index)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 
Pop(RepeatedCompositeContainer * self,PyObject * args)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.
ReleaseLastTo(CMessage * parent,const FieldDescriptor * field,CMessage * 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.
Release(RepeatedCompositeContainer * self)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 
SetOwner(RepeatedCompositeContainer * self,const shared_ptr<Message> & new_owner)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.
NewContainer(CMessage * parent,const FieldDescriptor * parent_field_descriptor,CMessageClass * concrete_class)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 
Dealloc(RepeatedCompositeContainer * self)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