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