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