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 // This file defines a C++ DescriptorDatabase, which wraps a Python Database 32 // and delegate all its operations to Python methods. 33 34 #include <google/protobuf/pyext/descriptor_database.h> 35 36 #include <google/protobuf/stubs/logging.h> 37 #include <google/protobuf/stubs/common.h> 38 #include <google/protobuf/descriptor.pb.h> 39 #include <google/protobuf/pyext/message.h> 40 #include <google/protobuf/pyext/scoped_pyobject_ptr.h> 41 42 namespace google { 43 namespace protobuf { 44 namespace python { 45 46 PyDescriptorDatabase::PyDescriptorDatabase(PyObject* py_database) 47 : py_database_(py_database) { 48 Py_INCREF(py_database_); 49 } 50 51 PyDescriptorDatabase::~PyDescriptorDatabase() { Py_DECREF(py_database_); } 52 53 // Convert a Python object to a FileDescriptorProto pointer. 54 // Handles all kinds of Python errors, which are simply logged. 55 static bool GetFileDescriptorProto(PyObject* py_descriptor, 56 FileDescriptorProto* output) { 57 if (py_descriptor == NULL) { 58 if (PyErr_ExceptionMatches(PyExc_KeyError)) { 59 // Expected error: item was simply not found. 60 PyErr_Clear(); 61 } else { 62 GOOGLE_LOG(ERROR) << "DescriptorDatabase method raised an error"; 63 PyErr_Print(); 64 } 65 return false; 66 } 67 if (py_descriptor == Py_None) { 68 return false; 69 } 70 const Descriptor* filedescriptor_descriptor = 71 FileDescriptorProto::default_instance().GetDescriptor(); 72 CMessage* message = reinterpret_cast<CMessage*>(py_descriptor); 73 if (PyObject_TypeCheck(py_descriptor, &CMessage_Type) && 74 message->message->GetDescriptor() == filedescriptor_descriptor) { 75 // Fast path: Just use the pointer. 76 FileDescriptorProto* file_proto = 77 static_cast<FileDescriptorProto*>(message->message); 78 *output = *file_proto; 79 return true; 80 } else { 81 // Slow path: serialize the message. This allows to use databases which 82 // use a different implementation of FileDescriptorProto. 83 ScopedPyObjectPtr serialized_pb( 84 PyObject_CallMethod(py_descriptor, "SerializeToString", NULL)); 85 if (serialized_pb == NULL) { 86 GOOGLE_LOG(ERROR) 87 << "DescriptorDatabase method did not return a FileDescriptorProto"; 88 PyErr_Print(); 89 return false; 90 } 91 char* str; 92 Py_ssize_t len; 93 if (PyBytes_AsStringAndSize(serialized_pb.get(), &str, &len) < 0) { 94 GOOGLE_LOG(ERROR) 95 << "DescriptorDatabase method did not return a FileDescriptorProto"; 96 PyErr_Print(); 97 return false; 98 } 99 FileDescriptorProto file_proto; 100 if (!file_proto.ParseFromArray(str, len)) { 101 GOOGLE_LOG(ERROR) 102 << "DescriptorDatabase method did not return a FileDescriptorProto"; 103 return false; 104 } 105 *output = file_proto; 106 return true; 107 } 108 } 109 110 // Find a file by file name. 111 bool PyDescriptorDatabase::FindFileByName(const string& filename, 112 FileDescriptorProto* output) { 113 ScopedPyObjectPtr py_descriptor(PyObject_CallMethod( 114 py_database_, "FindFileByName", "s#", filename.c_str(), filename.size())); 115 return GetFileDescriptorProto(py_descriptor.get(), output); 116 } 117 118 // Find the file that declares the given fully-qualified symbol name. 119 bool PyDescriptorDatabase::FindFileContainingSymbol( 120 const string& symbol_name, FileDescriptorProto* output) { 121 ScopedPyObjectPtr py_descriptor( 122 PyObject_CallMethod(py_database_, "FindFileContainingSymbol", "s#", 123 symbol_name.c_str(), symbol_name.size())); 124 return GetFileDescriptorProto(py_descriptor.get(), output); 125 } 126 127 // Find the file which defines an extension extending the given message type 128 // with the given field number. 129 // Python DescriptorDatabases are not required to implement this method. 130 bool PyDescriptorDatabase::FindFileContainingExtension( 131 const string& containing_type, int field_number, 132 FileDescriptorProto* output) { 133 ScopedPyObjectPtr py_method( 134 PyObject_GetAttrString(py_database_, "FindFileContainingExtension")); 135 if (py_method == NULL) { 136 // This method is not implemented, returns without error. 137 PyErr_Clear(); 138 return false; 139 } 140 ScopedPyObjectPtr py_descriptor( 141 PyObject_CallFunction(py_method.get(), "s#i", containing_type.c_str(), 142 containing_type.size(), field_number)); 143 return GetFileDescriptorProto(py_descriptor.get(), output); 144 } 145 146 } // namespace python 147 } // namespace protobuf 148 } // namespace google 149