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@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 53namespace google { 54namespace protobuf { 55namespace 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 66static google::protobuf::DescriptorPool* g_descriptor_pool = NULL; 67 68namespace cfield_descriptor { 69 70static void Dealloc(CFieldDescriptor* self) { 71 Py_CLEAR(self->descriptor_field); 72 Py_TYPE(self)->tp_free(reinterpret_cast<PyObject*>(self)); 73} 74 75static 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 81static PyObject* GetName(CFieldDescriptor *self, void *closure) { 82 return PyString_FromStringAndSize( 83 self->descriptor->name().c_str(), 84 self->descriptor->name().size()); 85} 86 87static PyObject* GetCppType(CFieldDescriptor *self, void *closure) { 88 return PyInt_FromLong(self->descriptor->cpp_type()); 89} 90 91static PyObject* GetLabel(CFieldDescriptor *self, void *closure) { 92 return PyInt_FromLong(self->descriptor->label()); 93} 94 95static PyObject* GetID(CFieldDescriptor *self, void *closure) { 96 return PyLong_FromVoidPtr(self); 97} 98 99static 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 110PyTypeObject 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 154namespace cdescriptor_pool { 155 156static void Dealloc(CDescriptorPool* self) { 157 Py_TYPE(self)->tp_free(reinterpret_cast<PyObject*>(self)); 158} 159 160static 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 173PyObject* 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 192PyObject* 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 209static 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 223PyTypeObject 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 267google::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 275PyObject* 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. 288class 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 310PyObject* 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 343bool 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