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/extension_dict.h>
     35 
     36 #include <google/protobuf/stubs/common.h>
     37 #include <google/protobuf/descriptor.h>
     38 #include <google/protobuf/dynamic_message.h>
     39 #include <google/protobuf/message.h>
     40 #include <google/protobuf/pyext/descriptor.h>
     41 #include <google/protobuf/pyext/message.h>
     42 #include <google/protobuf/pyext/repeated_composite_container.h>
     43 #include <google/protobuf/pyext/repeated_scalar_container.h>
     44 #include <google/protobuf/pyext/scoped_pyobject_ptr.h>
     45 #include <google/protobuf/stubs/shared_ptr.h>
     46 
     47 namespace google {
     48 namespace protobuf {
     49 namespace python {
     50 
     51 extern google::protobuf::DynamicMessageFactory* global_message_factory;
     52 
     53 namespace extension_dict {
     54 
     55 // TODO(tibell): Always use self->message for clarity, just like in
     56 // RepeatedCompositeContainer.
     57 static google::protobuf::Message* GetMessage(ExtensionDict* self) {
     58   if (self->parent != NULL) {
     59     return self->parent->message;
     60   } else {
     61     return self->message;
     62   }
     63 }
     64 
     65 CFieldDescriptor* InternalGetCDescriptorFromExtension(PyObject* extension) {
     66   PyObject* cdescriptor = PyObject_GetAttrString(extension, "_cdescriptor");
     67   if (cdescriptor == NULL) {
     68     PyErr_SetString(PyExc_KeyError, "Unregistered extension.");
     69     return NULL;
     70   }
     71   if (!PyObject_TypeCheck(cdescriptor, &CFieldDescriptor_Type)) {
     72     PyErr_SetString(PyExc_TypeError, "Not a CFieldDescriptor");
     73     Py_DECREF(cdescriptor);
     74     return NULL;
     75   }
     76   CFieldDescriptor* descriptor =
     77       reinterpret_cast<CFieldDescriptor*>(cdescriptor);
     78   return descriptor;
     79 }
     80 
     81 PyObject* len(ExtensionDict* self) {
     82 #if PY_MAJOR_VERSION >= 3
     83   return PyLong_FromLong(PyDict_Size(self->values));
     84 #else
     85   return PyInt_FromLong(PyDict_Size(self->values));
     86 #endif
     87 }
     88 
     89 // TODO(tibell): Use VisitCompositeField.
     90 int ReleaseExtension(ExtensionDict* self,
     91                      PyObject* extension,
     92                      const google::protobuf::FieldDescriptor* descriptor) {
     93   if (descriptor->label() == google::protobuf::FieldDescriptor::LABEL_REPEATED) {
     94     if (descriptor->cpp_type() ==
     95         google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE) {
     96       if (repeated_composite_container::Release(
     97               reinterpret_cast<RepeatedCompositeContainer*>(
     98                   extension)) < 0) {
     99         return -1;
    100       }
    101     } else {
    102       if (repeated_scalar_container::Release(
    103               reinterpret_cast<RepeatedScalarContainer*>(
    104                   extension)) < 0) {
    105         return -1;
    106       }
    107     }
    108   } else if (descriptor->cpp_type() ==
    109              google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE) {
    110     if (cmessage::ReleaseSubMessage(
    111             GetMessage(self), descriptor,
    112             reinterpret_cast<CMessage*>(extension)) < 0) {
    113       return -1;
    114     }
    115   }
    116 
    117   return 0;
    118 }
    119 
    120 PyObject* subscript(ExtensionDict* self, PyObject* key) {
    121   CFieldDescriptor* cdescriptor = InternalGetCDescriptorFromExtension(
    122       key);
    123   if (cdescriptor == NULL) {
    124     return NULL;
    125   }
    126   ScopedPyObjectPtr py_cdescriptor(reinterpret_cast<PyObject*>(cdescriptor));
    127   const google::protobuf::FieldDescriptor* descriptor = cdescriptor->descriptor;
    128   if (descriptor == NULL) {
    129     return NULL;
    130   }
    131   if (descriptor->label() != FieldDescriptor::LABEL_REPEATED &&
    132       descriptor->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE) {
    133     return cmessage::InternalGetScalar(self->parent, descriptor);
    134   }
    135 
    136   PyObject* value = PyDict_GetItem(self->values, key);
    137   if (value != NULL) {
    138     Py_INCREF(value);
    139     return value;
    140   }
    141 
    142   if (descriptor->label() != FieldDescriptor::LABEL_REPEATED &&
    143       descriptor->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
    144     PyObject* sub_message = cmessage::InternalGetSubMessage(
    145         self->parent, cdescriptor);
    146     if (sub_message == NULL) {
    147       return NULL;
    148     }
    149     PyDict_SetItem(self->values, key, sub_message);
    150     return sub_message;
    151   }
    152 
    153   if (descriptor->label() == FieldDescriptor::LABEL_REPEATED) {
    154     if (descriptor->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
    155       // COPIED
    156       PyObject* py_container = PyObject_CallObject(
    157           reinterpret_cast<PyObject*>(&RepeatedCompositeContainer_Type),
    158           NULL);
    159       if (py_container == NULL) {
    160         return NULL;
    161       }
    162       RepeatedCompositeContainer* container =
    163           reinterpret_cast<RepeatedCompositeContainer*>(py_container);
    164       PyObject* field = cdescriptor->descriptor_field;
    165       PyObject* message_type = PyObject_GetAttrString(field, "message_type");
    166       PyObject* concrete_class = PyObject_GetAttrString(message_type,
    167                                                         "_concrete_class");
    168       container->owner = self->owner;
    169       container->parent = self->parent;
    170       container->message = self->parent->message;
    171       container->parent_field = cdescriptor;
    172       container->subclass_init = concrete_class;
    173       Py_DECREF(message_type);
    174       PyDict_SetItem(self->values, key, py_container);
    175       return py_container;
    176     } else {
    177       // COPIED
    178       ScopedPyObjectPtr init_args(PyTuple_Pack(2, self->parent, cdescriptor));
    179       PyObject* py_container = PyObject_CallObject(
    180           reinterpret_cast<PyObject*>(&RepeatedScalarContainer_Type),
    181           init_args);
    182       if (py_container == NULL) {
    183         return NULL;
    184       }
    185       PyDict_SetItem(self->values, key, py_container);
    186       return py_container;
    187     }
    188   }
    189   PyErr_SetString(PyExc_ValueError, "control reached unexpected line");
    190   return NULL;
    191 }
    192 
    193 int ass_subscript(ExtensionDict* self, PyObject* key, PyObject* value) {
    194   CFieldDescriptor* cdescriptor = InternalGetCDescriptorFromExtension(
    195       key);
    196   if (cdescriptor == NULL) {
    197     return -1;
    198   }
    199   ScopedPyObjectPtr py_cdescriptor(reinterpret_cast<PyObject*>(cdescriptor));
    200   const google::protobuf::FieldDescriptor* descriptor = cdescriptor->descriptor;
    201   if (descriptor->label() != FieldDescriptor::LABEL_OPTIONAL ||
    202       descriptor->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
    203     PyErr_SetString(PyExc_TypeError, "Extension is repeated and/or composite "
    204                     "type");
    205     return -1;
    206   }
    207   cmessage::AssureWritable(self->parent);
    208   if (cmessage::InternalSetScalar(self->parent, descriptor, value) < 0) {
    209     return -1;
    210   }
    211   // TODO(tibell): We shouldn't write scalars to the cache.
    212   PyDict_SetItem(self->values, key, value);
    213   return 0;
    214 }
    215 
    216 PyObject* ClearExtension(ExtensionDict* self, PyObject* extension) {
    217   CFieldDescriptor* cdescriptor = InternalGetCDescriptorFromExtension(
    218       extension);
    219   if (cdescriptor == NULL) {
    220     return NULL;
    221   }
    222   ScopedPyObjectPtr py_cdescriptor(reinterpret_cast<PyObject*>(cdescriptor));
    223   PyObject* value = PyDict_GetItem(self->values, extension);
    224   if (value != NULL) {
    225     if (ReleaseExtension(self, value, cdescriptor->descriptor) < 0) {
    226       return NULL;
    227     }
    228   }
    229   if (cmessage::ClearFieldByDescriptor(self->parent,
    230                                        cdescriptor->descriptor) == NULL) {
    231     return NULL;
    232   }
    233   if (PyDict_DelItem(self->values, extension) < 0) {
    234     PyErr_Clear();
    235   }
    236   Py_RETURN_NONE;
    237 }
    238 
    239 PyObject* HasExtension(ExtensionDict* self, PyObject* extension) {
    240   CFieldDescriptor* cdescriptor = InternalGetCDescriptorFromExtension(
    241       extension);
    242   if (cdescriptor == NULL) {
    243     return NULL;
    244   }
    245   ScopedPyObjectPtr py_cdescriptor(reinterpret_cast<PyObject*>(cdescriptor));
    246   PyObject* result = cmessage::HasFieldByDescriptor(
    247       self->parent, cdescriptor->descriptor);
    248   return result;
    249 }
    250 
    251 PyObject* _FindExtensionByName(ExtensionDict* self, PyObject* name) {
    252   ScopedPyObjectPtr extensions_by_name(PyObject_GetAttrString(
    253       reinterpret_cast<PyObject*>(self->parent), "_extensions_by_name"));
    254   if (extensions_by_name == NULL) {
    255     return NULL;
    256   }
    257   PyObject* result = PyDict_GetItem(extensions_by_name, name);
    258   if (result == NULL) {
    259     Py_RETURN_NONE;
    260   } else {
    261     Py_INCREF(result);
    262     return result;
    263   }
    264 }
    265 
    266 int init(ExtensionDict* self, PyObject* args, PyObject* kwargs) {
    267   self->parent = NULL;
    268   self->message = NULL;
    269   self->values = PyDict_New();
    270   return 0;
    271 }
    272 
    273 void dealloc(ExtensionDict* self) {
    274   Py_CLEAR(self->values);
    275   self->owner.reset();
    276   Py_TYPE(self)->tp_free(reinterpret_cast<PyObject*>(self));
    277 }
    278 
    279 static PyMappingMethods MpMethods = {
    280   (lenfunc)len,               /* mp_length */
    281   (binaryfunc)subscript,      /* mp_subscript */
    282   (objobjargproc)ass_subscript,/* mp_ass_subscript */
    283 };
    284 
    285 #define EDMETHOD(name, args, doc) { #name, (PyCFunction)name, args, doc }
    286 static PyMethodDef Methods[] = {
    287   EDMETHOD(ClearExtension, METH_O, "Clears an extension from the object."),
    288   EDMETHOD(HasExtension, METH_O, "Checks if the object has an extension."),
    289   EDMETHOD(_FindExtensionByName, METH_O,
    290            "Finds an extension by name."),
    291   { NULL, NULL }
    292 };
    293 
    294 }  // namespace extension_dict
    295 
    296 PyTypeObject ExtensionDict_Type = {
    297   PyVarObject_HEAD_INIT(&PyType_Type, 0)
    298   "google.protobuf.internal."
    299   "cpp._message.ExtensionDict",        // tp_name
    300   sizeof(ExtensionDict),               // tp_basicsize
    301   0,                                   //  tp_itemsize
    302   (destructor)extension_dict::dealloc,  //  tp_dealloc
    303   0,                                   //  tp_print
    304   0,                                   //  tp_getattr
    305   0,                                   //  tp_setattr
    306   0,                                   //  tp_compare
    307   0,                                   //  tp_repr
    308   0,                                   //  tp_as_number
    309   0,                                   //  tp_as_sequence
    310   &extension_dict::MpMethods,          //  tp_as_mapping
    311   0,                                   //  tp_hash
    312   0,                                   //  tp_call
    313   0,                                   //  tp_str
    314   0,                                   //  tp_getattro
    315   0,                                   //  tp_setattro
    316   0,                                   //  tp_as_buffer
    317   Py_TPFLAGS_DEFAULT,                  //  tp_flags
    318   "An extension dict",                 //  tp_doc
    319   0,                                   //  tp_traverse
    320   0,                                   //  tp_clear
    321   0,                                   //  tp_richcompare
    322   0,                                   //  tp_weaklistoffset
    323   0,                                   //  tp_iter
    324   0,                                   //  tp_iternext
    325   extension_dict::Methods,             //  tp_methods
    326   0,                                   //  tp_members
    327   0,                                   //  tp_getset
    328   0,                                   //  tp_base
    329   0,                                   //  tp_dict
    330   0,                                   //  tp_descr_get
    331   0,                                   //  tp_descr_set
    332   0,                                   //  tp_dictoffset
    333   (initproc)extension_dict::init,      //  tp_init
    334 };
    335 
    336 }  // namespace python
    337 }  // namespace protobuf
    338 }  // namespace google
    339