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: petar (at) google.com (Petar Petrov)
     32 
     33 #include <Python.h>
     34 #include <string>
     35 
     36 #include <google/protobuf/descriptor.pb.h>
     37 #include <google/protobuf/pyext/descriptor.h>
     38 #include <google/protobuf/pyext/scoped_pyobject_ptr.h>
     39 
     40 #define C(str) const_cast<char*>(str)
     41 
     42 #if PY_MAJOR_VERSION >= 3
     43   #define PyString_FromStringAndSize PyUnicode_FromStringAndSize
     44   #define PyInt_FromLong PyLong_FromLong
     45   #if PY_VERSION_HEX < 0x03030000
     46     #error "Python 3.0 - 3.2 are not supported."
     47   #else
     48   #define PyString_AsString(ob) \
     49     (PyUnicode_Check(ob)? PyUnicode_AsUTF8(ob): PyBytes_AS_STRING(ob))
     50   #endif
     51 #endif
     52 
     53 namespace google {
     54 namespace protobuf {
     55 namespace python {
     56 
     57 
     58 #ifndef PyVarObject_HEAD_INIT
     59 #define PyVarObject_HEAD_INIT(type, size) PyObject_HEAD_INIT(type) size,
     60 #endif
     61 #ifndef Py_TYPE
     62 #define Py_TYPE(ob) (((PyObject*)(ob))->ob_type)
     63 #endif
     64 
     65 
     66 static google::protobuf::DescriptorPool* g_descriptor_pool = NULL;
     67 
     68 namespace cfield_descriptor {
     69 
     70 static void Dealloc(CFieldDescriptor* self) {
     71   Py_CLEAR(self->descriptor_field);
     72   Py_TYPE(self)->tp_free(reinterpret_cast<PyObject*>(self));
     73 }
     74 
     75 static PyObject* GetFullName(CFieldDescriptor* self, void *closure) {
     76   return PyString_FromStringAndSize(
     77       self->descriptor->full_name().c_str(),
     78       self->descriptor->full_name().size());
     79 }
     80 
     81 static PyObject* GetName(CFieldDescriptor *self, void *closure) {
     82   return PyString_FromStringAndSize(
     83       self->descriptor->name().c_str(),
     84       self->descriptor->name().size());
     85 }
     86 
     87 static PyObject* GetCppType(CFieldDescriptor *self, void *closure) {
     88   return PyInt_FromLong(self->descriptor->cpp_type());
     89 }
     90 
     91 static PyObject* GetLabel(CFieldDescriptor *self, void *closure) {
     92   return PyInt_FromLong(self->descriptor->label());
     93 }
     94 
     95 static PyObject* GetID(CFieldDescriptor *self, void *closure) {
     96   return PyLong_FromVoidPtr(self);
     97 }
     98 
     99 static PyGetSetDef Getters[] = {
    100   { C("full_name"), (getter)GetFullName, NULL, "Full name", NULL},
    101   { C("name"), (getter)GetName, NULL, "last name", NULL},
    102   { C("cpp_type"), (getter)GetCppType, NULL, "C++ Type", NULL},
    103   { C("label"), (getter)GetLabel, NULL, "Label", NULL},
    104   { C("id"), (getter)GetID, NULL, "ID", NULL},
    105   {NULL}
    106 };
    107 
    108 }  // namespace cfield_descriptor
    109 
    110 PyTypeObject CFieldDescriptor_Type = {
    111   PyVarObject_HEAD_INIT(&PyType_Type, 0)
    112   C("google.protobuf.internal."
    113     "_net_proto2___python."
    114     "CFieldDescriptor"),                // tp_name
    115   sizeof(CFieldDescriptor),             // tp_basicsize
    116   0,                                    // tp_itemsize
    117   (destructor)cfield_descriptor::Dealloc,  // tp_dealloc
    118   0,                                    // tp_print
    119   0,                                    // tp_getattr
    120   0,                                    // tp_setattr
    121   0,                                    // tp_compare
    122   0,                                    // tp_repr
    123   0,                                    // tp_as_number
    124   0,                                    // tp_as_sequence
    125   0,                                    // tp_as_mapping
    126   0,                                    // tp_hash
    127   0,                                    // tp_call
    128   0,                                    // tp_str
    129   0,                                    // tp_getattro
    130   0,                                    // tp_setattro
    131   0,                                    // tp_as_buffer
    132   Py_TPFLAGS_DEFAULT,                   // tp_flags
    133   C("A Field Descriptor"),              // tp_doc
    134   0,                                    // tp_traverse
    135   0,                                    // tp_clear
    136   0,                                    // tp_richcompare
    137   0,                                    // tp_weaklistoffset
    138   0,                                    // tp_iter
    139   0,                                    // tp_iternext
    140   0,                                    // tp_methods
    141   0,                                    // tp_members
    142   cfield_descriptor::Getters,           // tp_getset
    143   0,                                    // tp_base
    144   0,                                    // tp_dict
    145   0,                                    // tp_descr_get
    146   0,                                    // tp_descr_set
    147   0,                                    // tp_dictoffset
    148   0,                                    // tp_init
    149   PyType_GenericAlloc,                  // tp_alloc
    150   PyType_GenericNew,                    // tp_new
    151   PyObject_Del,                         // tp_free
    152 };
    153 
    154 namespace cdescriptor_pool {
    155 
    156 static void Dealloc(CDescriptorPool* self) {
    157   Py_TYPE(self)->tp_free(reinterpret_cast<PyObject*>(self));
    158 }
    159 
    160 static PyObject* NewCDescriptor(
    161     const google::protobuf::FieldDescriptor* field_descriptor) {
    162   CFieldDescriptor* cfield_descriptor = PyObject_New(
    163       CFieldDescriptor, &CFieldDescriptor_Type);
    164   if (cfield_descriptor == NULL) {
    165     return NULL;
    166   }
    167   cfield_descriptor->descriptor = field_descriptor;
    168   cfield_descriptor->descriptor_field = NULL;
    169 
    170   return reinterpret_cast<PyObject*>(cfield_descriptor);
    171 }
    172 
    173 PyObject* FindFieldByName(CDescriptorPool* self, PyObject* name) {
    174   const char* full_field_name = PyString_AsString(name);
    175   if (full_field_name == NULL) {
    176     return NULL;
    177   }
    178 
    179   const google::protobuf::FieldDescriptor* field_descriptor = NULL;
    180 
    181   field_descriptor = self->pool->FindFieldByName(full_field_name);
    182 
    183   if (field_descriptor == NULL) {
    184     PyErr_Format(PyExc_TypeError, "Couldn't find field %.200s",
    185                  full_field_name);
    186     return NULL;
    187   }
    188 
    189   return NewCDescriptor(field_descriptor);
    190 }
    191 
    192 PyObject* FindExtensionByName(CDescriptorPool* self, PyObject* arg) {
    193   const char* full_field_name = PyString_AsString(arg);
    194   if (full_field_name == NULL) {
    195     return NULL;
    196   }
    197 
    198   const google::protobuf::FieldDescriptor* field_descriptor =
    199       self->pool->FindExtensionByName(full_field_name);
    200   if (field_descriptor == NULL) {
    201     PyErr_Format(PyExc_TypeError, "Couldn't find field %.200s",
    202                  full_field_name);
    203     return NULL;
    204   }
    205 
    206   return NewCDescriptor(field_descriptor);
    207 }
    208 
    209 static PyMethodDef Methods[] = {
    210   { C("FindFieldByName"),
    211     (PyCFunction)FindFieldByName,
    212     METH_O,
    213     C("Searches for a field descriptor by full name.") },
    214   { C("FindExtensionByName"),
    215     (PyCFunction)FindExtensionByName,
    216     METH_O,
    217     C("Searches for extension descriptor by full name.") },
    218   {NULL}
    219 };
    220 
    221 }  // namespace cdescriptor_pool
    222 
    223 PyTypeObject CDescriptorPool_Type = {
    224   PyVarObject_HEAD_INIT(&PyType_Type, 0)
    225   C("google.protobuf.internal."
    226     "_net_proto2___python."
    227     "CFieldDescriptor"),               // tp_name
    228   sizeof(CDescriptorPool),             // tp_basicsize
    229   0,                                   // tp_itemsize
    230   (destructor)cdescriptor_pool::Dealloc,  // tp_dealloc
    231   0,                                   // tp_print
    232   0,                                   // tp_getattr
    233   0,                                   // tp_setattr
    234   0,                                   // tp_compare
    235   0,                                   // tp_repr
    236   0,                                   // tp_as_number
    237   0,                                   // tp_as_sequence
    238   0,                                   // tp_as_mapping
    239   0,                                   // tp_hash
    240   0,                                   // tp_call
    241   0,                                   // tp_str
    242   0,                                   // tp_getattro
    243   0,                                   // tp_setattro
    244   0,                                   // tp_as_buffer
    245   Py_TPFLAGS_DEFAULT,                  // tp_flags
    246   C("A Descriptor Pool"),              // tp_doc
    247   0,                                   // tp_traverse
    248   0,                                   // tp_clear
    249   0,                                   // tp_richcompare
    250   0,                                   // tp_weaklistoffset
    251   0,                                   // tp_iter
    252   0,                                   // tp_iternext
    253   cdescriptor_pool::Methods,           // tp_methods
    254   0,                                   // tp_members
    255   0,                                   // tp_getset
    256   0,                                   // tp_base
    257   0,                                   // tp_dict
    258   0,                                   // tp_descr_get
    259   0,                                   // tp_descr_set
    260   0,                                   // tp_dictoffset
    261   0,                                   // tp_init
    262   PyType_GenericAlloc,                 // tp_alloc
    263   PyType_GenericNew,                   // tp_new
    264   PyObject_Del,                        // tp_free
    265 };
    266 
    267 google::protobuf::DescriptorPool* GetDescriptorPool() {
    268   if (g_descriptor_pool == NULL) {
    269     g_descriptor_pool = new google::protobuf::DescriptorPool(
    270         google::protobuf::DescriptorPool::generated_pool());
    271   }
    272   return g_descriptor_pool;
    273 }
    274 
    275 PyObject* Python_NewCDescriptorPool(PyObject* ignored, PyObject* args) {
    276   CDescriptorPool* cdescriptor_pool = PyObject_New(
    277       CDescriptorPool, &CDescriptorPool_Type);
    278   if (cdescriptor_pool == NULL) {
    279     return NULL;
    280   }
    281   cdescriptor_pool->pool = GetDescriptorPool();
    282   return reinterpret_cast<PyObject*>(cdescriptor_pool);
    283 }
    284 
    285 
    286 // Collects errors that occur during proto file building to allow them to be
    287 // propagated in the python exception instead of only living in ERROR logs.
    288 class BuildFileErrorCollector : public google::protobuf::DescriptorPool::ErrorCollector {
    289  public:
    290   BuildFileErrorCollector() : error_message(""), had_errors(false) {}
    291 
    292   void AddError(const string& filename, const string& element_name,
    293                 const Message* descriptor, ErrorLocation location,
    294                 const string& message) {
    295     // Replicates the logging behavior that happens in the C++ implementation
    296     // when an error collector is not passed in.
    297     if (!had_errors) {
    298       error_message +=
    299           ("Invalid proto descriptor for file \"" + filename + "\":\n");
    300     }
    301     // As this only happens on failure and will result in the program not
    302     // running at all, no effort is made to optimize this string manipulation.
    303     error_message += ("  " + element_name + ": " + message + "\n");
    304   }
    305 
    306   string error_message;
    307   bool had_errors;
    308 };
    309 
    310 PyObject* Python_BuildFile(PyObject* ignored, PyObject* arg) {
    311   char* message_type;
    312   Py_ssize_t message_len;
    313 
    314   if (PyBytes_AsStringAndSize(arg, &message_type, &message_len) < 0) {
    315     return NULL;
    316   }
    317 
    318   google::protobuf::FileDescriptorProto file_proto;
    319   if (!file_proto.ParseFromArray(message_type, message_len)) {
    320     PyErr_SetString(PyExc_TypeError, "Couldn't parse file content!");
    321     return NULL;
    322   }
    323 
    324   if (google::protobuf::DescriptorPool::generated_pool()->FindFileByName(
    325           file_proto.name()) != NULL) {
    326     Py_RETURN_NONE;
    327   }
    328 
    329   BuildFileErrorCollector error_collector;
    330   const google::protobuf::FileDescriptor* descriptor =
    331       GetDescriptorPool()->BuildFileCollectingErrors(file_proto,
    332                                                      &error_collector);
    333   if (descriptor == NULL) {
    334     PyErr_Format(PyExc_TypeError,
    335                  "Couldn't build proto file into descriptor pool!\n%s",
    336                  error_collector.error_message.c_str());
    337     return NULL;
    338   }
    339 
    340   Py_RETURN_NONE;
    341 }
    342 
    343 bool InitDescriptor() {
    344   CFieldDescriptor_Type.tp_new = PyType_GenericNew;
    345   if (PyType_Ready(&CFieldDescriptor_Type) < 0)
    346     return false;
    347 
    348   CDescriptorPool_Type.tp_new = PyType_GenericNew;
    349   if (PyType_Ready(&CDescriptorPool_Type) < 0)
    350     return false;
    351 
    352   return true;
    353 }
    354 
    355 }  // namespace python
    356 }  // namespace protobuf
    357 }  // namespace google
    358