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
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
Dealloc(CFieldDescriptor * self)70 static void Dealloc(CFieldDescriptor* self) {
71 Py_CLEAR(self->descriptor_field);
72 Py_TYPE(self)->tp_free(reinterpret_cast<PyObject*>(self));
73 }
74
GetFullName(CFieldDescriptor * self,void * closure)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
GetName(CFieldDescriptor * self,void * closure)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
GetCppType(CFieldDescriptor * self,void * closure)87 static PyObject* GetCppType(CFieldDescriptor *self, void *closure) {
88 return PyInt_FromLong(self->descriptor->cpp_type());
89 }
90
GetLabel(CFieldDescriptor * self,void * closure)91 static PyObject* GetLabel(CFieldDescriptor *self, void *closure) {
92 return PyInt_FromLong(self->descriptor->label());
93 }
94
GetID(CFieldDescriptor * self,void * closure)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
Dealloc(CDescriptorPool * self)156 static void Dealloc(CDescriptorPool* self) {
157 Py_TYPE(self)->tp_free(reinterpret_cast<PyObject*>(self));
158 }
159
NewCDescriptor(const google::protobuf::FieldDescriptor * field_descriptor)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
FindFieldByName(CDescriptorPool * self,PyObject * name)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
FindExtensionByName(CDescriptorPool * self,PyObject * arg)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
GetDescriptorPool()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
Python_NewCDescriptorPool(PyObject * ignored,PyObject * args)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:
BuildFileErrorCollector()290 BuildFileErrorCollector() : error_message(""), had_errors(false) {}
291
AddError(const string & filename,const string & element_name,const Message * descriptor,ErrorLocation location,const string & message)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
Python_BuildFile(PyObject * ignored,PyObject * arg)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
InitDescriptor()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