summaryrefslogtreecommitdiff
path: root/python/google/protobuf/pyext
diff options
context:
space:
mode:
Diffstat (limited to 'python/google/protobuf/pyext')
-rw-r--r--python/google/protobuf/pyext/README6
-rw-r--r--python/google/protobuf/pyext/__init__.py4
-rw-r--r--python/google/protobuf/pyext/cpp_message.py65
-rw-r--r--python/google/protobuf/pyext/descriptor.cc1845
-rw-r--r--python/google/protobuf/pyext/descriptor.h103
-rw-r--r--python/google/protobuf/pyext/descriptor_containers.cc1786
-rw-r--r--python/google/protobuf/pyext/descriptor_containers.h109
-rw-r--r--python/google/protobuf/pyext/descriptor_database.cc148
-rw-r--r--python/google/protobuf/pyext/descriptor_database.h75
-rw-r--r--python/google/protobuf/pyext/descriptor_pool.cc631
-rw-r--r--python/google/protobuf/pyext/descriptor_pool.h167
-rw-r--r--python/google/protobuf/pyext/extension_dict.cc337
-rw-r--r--python/google/protobuf/pyext/extension_dict.h137
-rw-r--r--python/google/protobuf/pyext/map_container.cc969
-rw-r--r--python/google/protobuf/pyext/map_container.h142
-rw-r--r--python/google/protobuf/pyext/message.cc3085
-rw-r--r--python/google/protobuf/pyext/message.h365
-rw-r--r--python/google/protobuf/pyext/message_module.cc88
-rw-r--r--python/google/protobuf/pyext/proto2_api_test.proto40
-rw-r--r--python/google/protobuf/pyext/python.proto68
-rw-r--r--python/google/protobuf/pyext/python_protobuf.h57
-rw-r--r--python/google/protobuf/pyext/repeated_composite_container.cc612
-rw-r--r--python/google/protobuf/pyext/repeated_composite_container.h179
-rw-r--r--python/google/protobuf/pyext/repeated_scalar_container.cc812
-rw-r--r--python/google/protobuf/pyext/repeated_scalar_container.h122
-rw-r--r--python/google/protobuf/pyext/scoped_pyobject_ptr.h96
26 files changed, 12048 insertions, 0 deletions
diff --git a/python/google/protobuf/pyext/README b/python/google/protobuf/pyext/README
new file mode 100644
index 00000000..6d61cb45
--- /dev/null
+++ b/python/google/protobuf/pyext/README
@@ -0,0 +1,6 @@
+This is the 'v2' C++ implementation for python proto2.
+
+It is active when:
+
+PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION=cpp
+PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION_VERSION=2
diff --git a/python/google/protobuf/pyext/__init__.py b/python/google/protobuf/pyext/__init__.py
new file mode 100644
index 00000000..55856141
--- /dev/null
+++ b/python/google/protobuf/pyext/__init__.py
@@ -0,0 +1,4 @@
+try:
+ __import__('pkg_resources').declare_namespace(__name__)
+except ImportError:
+ __path__ = __import__('pkgutil').extend_path(__path__, __name__)
diff --git a/python/google/protobuf/pyext/cpp_message.py b/python/google/protobuf/pyext/cpp_message.py
new file mode 100644
index 00000000..b215211e
--- /dev/null
+++ b/python/google/protobuf/pyext/cpp_message.py
@@ -0,0 +1,65 @@
+# Protocol Buffers - Google's data interchange format
+# Copyright 2008 Google Inc. All rights reserved.
+# https://developers.google.com/protocol-buffers/
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""Protocol message implementation hooks for C++ implementation.
+
+Contains helper functions used to create protocol message classes from
+Descriptor objects at runtime backed by the protocol buffer C++ API.
+"""
+
+__author__ = 'tibell@google.com (Johan Tibell)'
+
+from google.protobuf.pyext import _message
+
+
+class GeneratedProtocolMessageType(_message.MessageMeta):
+
+ """Metaclass for protocol message classes created at runtime from Descriptors.
+
+ The protocol compiler currently uses this metaclass to create protocol
+ message classes at runtime. Clients can also manually create their own
+ classes at runtime, as in this example:
+
+ mydescriptor = Descriptor(.....)
+ class MyProtoClass(Message):
+ __metaclass__ = GeneratedProtocolMessageType
+ DESCRIPTOR = mydescriptor
+ myproto_instance = MyProtoClass()
+ myproto.foo_field = 23
+ ...
+
+ The above example will not work for nested types. If you wish to include them,
+ use reflection.MakeClass() instead of manually instantiating the class in
+ order to create the appropriate class structure.
+ """
+
+ # Must be consistent with the protocol-compiler code in
+ # proto2/compiler/internal/generator.*.
+ _DESCRIPTOR_KEY = 'DESCRIPTOR'
diff --git a/python/google/protobuf/pyext/descriptor.cc b/python/google/protobuf/pyext/descriptor.cc
new file mode 100644
index 00000000..e6ef5ef5
--- /dev/null
+++ b/python/google/protobuf/pyext/descriptor.cc
@@ -0,0 +1,1845 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Author: petar@google.com (Petar Petrov)
+
+#include <Python.h>
+#include <frameobject.h>
+#include <string>
+
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/descriptor.pb.h>
+#include <google/protobuf/dynamic_message.h>
+#include <google/protobuf/pyext/descriptor.h>
+#include <google/protobuf/pyext/descriptor_containers.h>
+#include <google/protobuf/pyext/descriptor_pool.h>
+#include <google/protobuf/pyext/message.h>
+#include <google/protobuf/pyext/scoped_pyobject_ptr.h>
+
+#if PY_MAJOR_VERSION >= 3
+ #define PyString_FromStringAndSize PyUnicode_FromStringAndSize
+ #define PyString_Check PyUnicode_Check
+ #define PyString_InternFromString PyUnicode_InternFromString
+ #define PyInt_FromLong PyLong_FromLong
+ #define PyInt_FromSize_t PyLong_FromSize_t
+ #if PY_VERSION_HEX < 0x03030000
+ #error "Python 3.0 - 3.2 are not supported."
+ #endif
+ #define PyString_AsStringAndSize(ob, charpp, sizep) \
+ (PyUnicode_Check(ob)? \
+ ((*(charpp) = PyUnicode_AsUTF8AndSize(ob, (sizep))) == NULL? -1: 0): \
+ PyBytes_AsStringAndSize(ob, (charpp), (sizep)))
+#endif
+
+namespace google {
+namespace protobuf {
+namespace python {
+
+// Store interned descriptors, so that the same C++ descriptor yields the same
+// Python object. Objects are not immortal: this map does not own the
+// references, and items are deleted when the last reference to the object is
+// released.
+// This is enough to support the "is" operator on live objects.
+// All descriptors are stored here.
+hash_map<const void*, PyObject*> interned_descriptors;
+
+PyObject* PyString_FromCppString(const string& str) {
+ return PyString_FromStringAndSize(str.c_str(), str.size());
+}
+
+// Check that the calling Python code is the global scope of a _pb2.py module.
+// This function is used to support the current code generated by the proto
+// compiler, which creates descriptors, then update some properties.
+// For example:
+// message_descriptor = Descriptor(
+// name='Message',
+// fields = [FieldDescriptor(name='field')]
+// message_descriptor.fields[0].containing_type = message_descriptor
+//
+// This code is still executed, but the descriptors now have no other storage
+// than the (const) C++ pointer, and are immutable.
+// So we let this code pass, by simply ignoring the new value.
+//
+// From user code, descriptors still look immutable.
+//
+// TODO(amauryfa): Change the proto2 compiler to remove the assignments, and
+// remove this hack.
+bool _CalledFromGeneratedFile(int stacklevel) {
+#ifndef PYPY_VERSION
+ // This check is not critical and is somewhat difficult to implement correctly
+ // in PyPy.
+ PyFrameObject* frame = PyEval_GetFrame();
+ if (frame == NULL) {
+ return false;
+ }
+ while (stacklevel-- > 0) {
+ frame = frame->f_back;
+ if (frame == NULL) {
+ return false;
+ }
+ }
+ if (frame->f_globals != frame->f_locals) {
+ // Not at global module scope
+ return false;
+ }
+
+ if (frame->f_code->co_filename == NULL) {
+ return false;
+ }
+ char* filename;
+ Py_ssize_t filename_size;
+ if (PyString_AsStringAndSize(frame->f_code->co_filename,
+ &filename, &filename_size) < 0) {
+ // filename is not a string.
+ PyErr_Clear();
+ return false;
+ }
+ if (filename_size < 7) {
+ // filename is too short.
+ return false;
+ }
+ if (strcmp(&filename[filename_size - 7], "_pb2.py") != 0) {
+ // Filename is not ending with _pb2.
+ return false;
+ }
+#endif
+ return true;
+}
+
+// If the calling code is not a _pb2.py file, raise AttributeError.
+// To be used in attribute setters.
+static int CheckCalledFromGeneratedFile(const char* attr_name) {
+ if (_CalledFromGeneratedFile(0)) {
+ return 0;
+ }
+ PyErr_Format(PyExc_AttributeError,
+ "attribute is not writable: %s", attr_name);
+ return -1;
+}
+
+
+#ifndef PyVarObject_HEAD_INIT
+#define PyVarObject_HEAD_INIT(type, size) PyObject_HEAD_INIT(type) size,
+#endif
+#ifndef Py_TYPE
+#define Py_TYPE(ob) (((PyObject*)(ob))->ob_type)
+#endif
+
+
+// Helper functions for descriptor objects.
+
+// A set of templates to retrieve the C++ FileDescriptor of any descriptor.
+template<class DescriptorClass>
+const FileDescriptor* GetFileDescriptor(const DescriptorClass* descriptor) {
+ return descriptor->file();
+}
+template<>
+const FileDescriptor* GetFileDescriptor(const FileDescriptor* descriptor) {
+ return descriptor;
+}
+template<>
+const FileDescriptor* GetFileDescriptor(const EnumValueDescriptor* descriptor) {
+ return descriptor->type()->file();
+}
+template<>
+const FileDescriptor* GetFileDescriptor(const OneofDescriptor* descriptor) {
+ return descriptor->containing_type()->file();
+}
+template<>
+const FileDescriptor* GetFileDescriptor(const MethodDescriptor* descriptor) {
+ return descriptor->service()->file();
+}
+
+// Converts options into a Python protobuf, and cache the result.
+//
+// This is a bit tricky because options can contain extension fields defined in
+// the same proto file. In this case the options parsed from the serialized_pb
+// have unknown fields, and we need to parse them again.
+//
+// Always returns a new reference.
+template<class DescriptorClass>
+static PyObject* GetOrBuildOptions(const DescriptorClass *descriptor) {
+ // Options (and their extensions) are completely resolved in the proto file
+ // containing the descriptor.
+ PyDescriptorPool* pool = GetDescriptorPool_FromPool(
+ GetFileDescriptor(descriptor)->pool());
+
+ hash_map<const void*, PyObject*>* descriptor_options =
+ pool->descriptor_options;
+ // First search in the cache.
+ if (descriptor_options->find(descriptor) != descriptor_options->end()) {
+ PyObject *value = (*descriptor_options)[descriptor];
+ Py_INCREF(value);
+ return value;
+ }
+
+ // Build the Options object: get its Python class, and make a copy of the C++
+ // read-only instance.
+ const Message& options(descriptor->options());
+ const Descriptor *message_type = options.GetDescriptor();
+ CMessageClass* message_class(
+ cdescriptor_pool::GetMessageClass(pool, message_type));
+ if (message_class == NULL) {
+ // The Options message was not found in the current DescriptorPool.
+ // This means that the pool cannot contain any extensions to the Options
+ // message either, so falling back to the basic pool we can only increase
+ // the chances of successfully parsing the options.
+ PyErr_Clear();
+ pool = GetDefaultDescriptorPool();
+ message_class = cdescriptor_pool::GetMessageClass(pool, message_type);
+ }
+ if (message_class == NULL) {
+ PyErr_Format(PyExc_TypeError, "Could not retrieve class for Options: %s",
+ message_type->full_name().c_str());
+ return NULL;
+ }
+ ScopedPyObjectPtr value(
+ PyEval_CallObject(message_class->AsPyObject(), NULL));
+ if (value == NULL) {
+ return NULL;
+ }
+ if (!PyObject_TypeCheck(value.get(), &CMessage_Type)) {
+ PyErr_Format(PyExc_TypeError, "Invalid class for %s: %s",
+ message_type->full_name().c_str(),
+ Py_TYPE(value.get())->tp_name);
+ return NULL;
+ }
+ CMessage* cmsg = reinterpret_cast<CMessage*>(value.get());
+
+ const Reflection* reflection = options.GetReflection();
+ const UnknownFieldSet& unknown_fields(reflection->GetUnknownFields(options));
+ if (unknown_fields.empty()) {
+ cmsg->message->CopyFrom(options);
+ } else {
+ // Reparse options string! XXX call cmessage::MergeFromString
+ string serialized;
+ options.SerializeToString(&serialized);
+ io::CodedInputStream input(
+ reinterpret_cast<const uint8*>(serialized.c_str()), serialized.size());
+ input.SetExtensionRegistry(pool->pool, pool->message_factory);
+ bool success = cmsg->message->MergePartialFromCodedStream(&input);
+ if (!success) {
+ PyErr_Format(PyExc_ValueError, "Error parsing Options message");
+ return NULL;
+ }
+ }
+
+ // Cache the result.
+ Py_INCREF(value.get());
+ (*descriptor_options)[descriptor] = value.get();
+
+ return value.release();
+}
+
+// Copy the C++ descriptor to a Python message.
+// The Python message is an instance of descriptor_pb2.DescriptorProto
+// or similar.
+template<class DescriptorProtoClass, class DescriptorClass>
+static PyObject* CopyToPythonProto(const DescriptorClass *descriptor,
+ PyObject *target) {
+ const Descriptor* self_descriptor =
+ DescriptorProtoClass::default_instance().GetDescriptor();
+ CMessage* message = reinterpret_cast<CMessage*>(target);
+ if (!PyObject_TypeCheck(target, &CMessage_Type) ||
+ message->message->GetDescriptor() != self_descriptor) {
+ PyErr_Format(PyExc_TypeError, "Not a %s message",
+ self_descriptor->full_name().c_str());
+ return NULL;
+ }
+ cmessage::AssureWritable(message);
+ DescriptorProtoClass* descriptor_message =
+ static_cast<DescriptorProtoClass*>(message->message);
+ descriptor->CopyTo(descriptor_message);
+ Py_RETURN_NONE;
+}
+
+// All Descriptors classes share the same memory layout.
+typedef struct PyBaseDescriptor {
+ PyObject_HEAD
+
+ // Pointer to the C++ proto2 descriptor.
+ // Like all descriptors, it is owned by the global DescriptorPool.
+ const void* descriptor;
+
+ // Owned reference to the DescriptorPool, to ensure it is kept alive.
+ PyDescriptorPool* pool;
+} PyBaseDescriptor;
+
+
+// FileDescriptor structure "inherits" from the base descriptor.
+typedef struct PyFileDescriptor {
+ PyBaseDescriptor base;
+
+ // The cached version of serialized pb. Either NULL, or a Bytes string.
+ // We own the reference.
+ PyObject *serialized_pb;
+} PyFileDescriptor;
+
+
+namespace descriptor {
+
+// Creates or retrieve a Python descriptor of the specified type.
+// Objects are interned: the same descriptor will return the same object if it
+// was kept alive.
+// 'was_created' is an optional pointer to a bool, and is set to true if a new
+// object was allocated.
+// Always return a new reference.
+template<class DescriptorClass>
+PyObject* NewInternedDescriptor(PyTypeObject* type,
+ const DescriptorClass* descriptor,
+ bool* was_created) {
+ if (was_created) {
+ *was_created = false;
+ }
+ if (descriptor == NULL) {
+ PyErr_BadInternalCall();
+ return NULL;
+ }
+
+ // See if the object is in the map of interned descriptors
+ hash_map<const void*, PyObject*>::iterator it =
+ interned_descriptors.find(descriptor);
+ if (it != interned_descriptors.end()) {
+ GOOGLE_DCHECK(Py_TYPE(it->second) == type);
+ Py_INCREF(it->second);
+ return it->second;
+ }
+ // Create a new descriptor object
+ PyBaseDescriptor* py_descriptor = PyObject_New(
+ PyBaseDescriptor, type);
+ if (py_descriptor == NULL) {
+ return NULL;
+ }
+ py_descriptor->descriptor = descriptor;
+
+ // and cache it.
+ interned_descriptors.insert(
+ std::make_pair(descriptor, reinterpret_cast<PyObject*>(py_descriptor)));
+
+ // Ensures that the DescriptorPool stays alive.
+ PyDescriptorPool* pool = GetDescriptorPool_FromPool(
+ GetFileDescriptor(descriptor)->pool());
+ if (pool == NULL) {
+ // Don't DECREF, the object is not fully initialized.
+ PyObject_Del(py_descriptor);
+ return NULL;
+ }
+ Py_INCREF(pool);
+ py_descriptor->pool = pool;
+
+ if (was_created) {
+ *was_created = true;
+ }
+ return reinterpret_cast<PyObject*>(py_descriptor);
+}
+
+static void Dealloc(PyBaseDescriptor* self) {
+ // Remove from interned dictionary
+ interned_descriptors.erase(self->descriptor);
+ Py_CLEAR(self->pool);
+ Py_TYPE(self)->tp_free(reinterpret_cast<PyObject*>(self));
+}
+
+static PyGetSetDef Getters[] = {
+ {NULL}
+};
+
+PyTypeObject PyBaseDescriptor_Type = {
+ PyVarObject_HEAD_INIT(&PyType_Type, 0)
+ FULL_MODULE_NAME ".DescriptorBase", // tp_name
+ sizeof(PyBaseDescriptor), // tp_basicsize
+ 0, // tp_itemsize
+ (destructor)Dealloc, // tp_dealloc
+ 0, // tp_print
+ 0, // tp_getattr
+ 0, // tp_setattr
+ 0, // tp_compare
+ 0, // tp_repr
+ 0, // tp_as_number
+ 0, // tp_as_sequence
+ 0, // tp_as_mapping
+ 0, // tp_hash
+ 0, // tp_call
+ 0, // tp_str
+ 0, // tp_getattro
+ 0, // tp_setattro
+ 0, // tp_as_buffer
+ Py_TPFLAGS_DEFAULT, // tp_flags
+ "Descriptors base class", // tp_doc
+ 0, // tp_traverse
+ 0, // tp_clear
+ 0, // tp_richcompare
+ 0, // tp_weaklistoffset
+ 0, // tp_iter
+ 0, // tp_iternext
+ 0, // tp_methods
+ 0, // tp_members
+ Getters, // tp_getset
+};
+
+} // namespace descriptor
+
+const void* PyDescriptor_AsVoidPtr(PyObject* obj) {
+ if (!PyObject_TypeCheck(obj, &descriptor::PyBaseDescriptor_Type)) {
+ PyErr_SetString(PyExc_TypeError, "Not a BaseDescriptor");
+ return NULL;
+ }
+ return reinterpret_cast<PyBaseDescriptor*>(obj)->descriptor;
+}
+
+namespace message_descriptor {
+
+// Unchecked accessor to the C++ pointer.
+static const Descriptor* _GetDescriptor(PyBaseDescriptor* self) {
+ return reinterpret_cast<const Descriptor*>(self->descriptor);
+}
+
+static PyObject* GetName(PyBaseDescriptor* self, void *closure) {
+ return PyString_FromCppString(_GetDescriptor(self)->name());
+}
+
+static PyObject* GetFullName(PyBaseDescriptor* self, void *closure) {
+ return PyString_FromCppString(_GetDescriptor(self)->full_name());
+}
+
+static PyObject* GetFile(PyBaseDescriptor *self, void *closure) {
+ return PyFileDescriptor_FromDescriptor(_GetDescriptor(self)->file());
+}
+
+static PyObject* GetConcreteClass(PyBaseDescriptor* self, void *closure) {
+ // Retuns the canonical class for the given descriptor.
+ // This is the class that was registered with the primary descriptor pool
+ // which contains this descriptor.
+ // This might not be the one you expect! For example the returned object does
+ // not know about extensions defined in a custom pool.
+ CMessageClass* concrete_class(cdescriptor_pool::GetMessageClass(
+ GetDescriptorPool_FromPool(_GetDescriptor(self)->file()->pool()),
+ _GetDescriptor(self)));
+ Py_XINCREF(concrete_class);
+ return concrete_class->AsPyObject();
+}
+
+static PyObject* GetFieldsByName(PyBaseDescriptor* self, void *closure) {
+ return NewMessageFieldsByName(_GetDescriptor(self));
+}
+
+static PyObject* GetFieldsByCamelcaseName(PyBaseDescriptor* self,
+ void *closure) {
+ return NewMessageFieldsByCamelcaseName(_GetDescriptor(self));
+}
+
+static PyObject* GetFieldsByNumber(PyBaseDescriptor* self, void *closure) {
+ return NewMessageFieldsByNumber(_GetDescriptor(self));
+}
+
+static PyObject* GetFieldsSeq(PyBaseDescriptor* self, void *closure) {
+ return NewMessageFieldsSeq(_GetDescriptor(self));
+}
+
+static PyObject* GetNestedTypesByName(PyBaseDescriptor* self, void *closure) {
+ return NewMessageNestedTypesByName(_GetDescriptor(self));
+}
+
+static PyObject* GetNestedTypesSeq(PyBaseDescriptor* self, void *closure) {
+ return NewMessageNestedTypesSeq(_GetDescriptor(self));
+}
+
+static PyObject* GetExtensionsByName(PyBaseDescriptor* self, void *closure) {
+ return NewMessageExtensionsByName(_GetDescriptor(self));
+}
+
+static PyObject* GetExtensions(PyBaseDescriptor* self, void *closure) {
+ return NewMessageExtensionsSeq(_GetDescriptor(self));
+}
+
+static PyObject* GetEnumsSeq(PyBaseDescriptor* self, void *closure) {
+ return NewMessageEnumsSeq(_GetDescriptor(self));
+}
+
+static PyObject* GetEnumTypesByName(PyBaseDescriptor* self, void *closure) {
+ return NewMessageEnumsByName(_GetDescriptor(self));
+}
+
+static PyObject* GetEnumValuesByName(PyBaseDescriptor* self, void *closure) {
+ return NewMessageEnumValuesByName(_GetDescriptor(self));
+}
+
+static PyObject* GetOneofsByName(PyBaseDescriptor* self, void *closure) {
+ return NewMessageOneofsByName(_GetDescriptor(self));
+}
+
+static PyObject* GetOneofsSeq(PyBaseDescriptor* self, void *closure) {
+ return NewMessageOneofsSeq(_GetDescriptor(self));
+}
+
+static PyObject* IsExtendable(PyBaseDescriptor *self, void *closure) {
+ if (_GetDescriptor(self)->extension_range_count() > 0) {
+ Py_RETURN_TRUE;
+ } else {
+ Py_RETURN_FALSE;
+ }
+}
+
+static PyObject* GetExtensionRanges(PyBaseDescriptor *self, void *closure) {
+ const Descriptor* descriptor = _GetDescriptor(self);
+ PyObject* range_list = PyList_New(descriptor->extension_range_count());
+
+ for (int i = 0; i < descriptor->extension_range_count(); i++) {
+ const Descriptor::ExtensionRange* range = descriptor->extension_range(i);
+ PyObject* start = PyInt_FromLong(range->start);
+ PyObject* end = PyInt_FromLong(range->end);
+ PyList_SetItem(range_list, i, PyTuple_Pack(2, start, end));
+ }
+
+ return range_list;
+}
+
+static PyObject* GetContainingType(PyBaseDescriptor *self, void *closure) {
+ const Descriptor* containing_type =
+ _GetDescriptor(self)->containing_type();
+ if (containing_type) {
+ return PyMessageDescriptor_FromDescriptor(containing_type);
+ } else {
+ Py_RETURN_NONE;
+ }
+}
+
+static int SetContainingType(PyBaseDescriptor *self, PyObject *value,
+ void *closure) {
+ return CheckCalledFromGeneratedFile("containing_type");
+}
+
+static PyObject* GetHasOptions(PyBaseDescriptor *self, void *closure) {
+ const MessageOptions& options(_GetDescriptor(self)->options());
+ if (&options != &MessageOptions::default_instance()) {
+ Py_RETURN_TRUE;
+ } else {
+ Py_RETURN_FALSE;
+ }
+}
+static int SetHasOptions(PyBaseDescriptor *self, PyObject *value,
+ void *closure) {
+ return CheckCalledFromGeneratedFile("has_options");
+}
+
+static PyObject* GetOptions(PyBaseDescriptor *self) {
+ return GetOrBuildOptions(_GetDescriptor(self));
+}
+
+static int SetOptions(PyBaseDescriptor *self, PyObject *value,
+ void *closure) {
+ return CheckCalledFromGeneratedFile("_options");
+}
+
+static PyObject* CopyToProto(PyBaseDescriptor *self, PyObject *target) {
+ return CopyToPythonProto<DescriptorProto>(_GetDescriptor(self), target);
+}
+
+static PyObject* EnumValueName(PyBaseDescriptor *self, PyObject *args) {
+ const char *enum_name;
+ int number;
+ if (!PyArg_ParseTuple(args, "si", &enum_name, &number))
+ return NULL;
+ const EnumDescriptor *enum_type =
+ _GetDescriptor(self)->FindEnumTypeByName(enum_name);
+ if (enum_type == NULL) {
+ PyErr_SetString(PyExc_KeyError, enum_name);
+ return NULL;
+ }
+ const EnumValueDescriptor *enum_value =
+ enum_type->FindValueByNumber(number);
+ if (enum_value == NULL) {
+ PyErr_Format(PyExc_KeyError, "%d", number);
+ return NULL;
+ }
+ return PyString_FromCppString(enum_value->name());
+}
+
+static PyObject* GetSyntax(PyBaseDescriptor *self, void *closure) {
+ return PyString_InternFromString(
+ FileDescriptor::SyntaxName(_GetDescriptor(self)->file()->syntax()));
+}
+
+static PyGetSetDef Getters[] = {
+ { "name", (getter)GetName, NULL, "Last name"},
+ { "full_name", (getter)GetFullName, NULL, "Full name"},
+ { "_concrete_class", (getter)GetConcreteClass, NULL, "concrete class"},
+ { "file", (getter)GetFile, NULL, "File descriptor"},
+
+ { "fields", (getter)GetFieldsSeq, NULL, "Fields sequence"},
+ { "fields_by_name", (getter)GetFieldsByName, NULL, "Fields by name"},
+ { "fields_by_camelcase_name", (getter)GetFieldsByCamelcaseName, NULL,
+ "Fields by camelCase name"},
+ { "fields_by_number", (getter)GetFieldsByNumber, NULL, "Fields by number"},
+ { "nested_types", (getter)GetNestedTypesSeq, NULL, "Nested types sequence"},
+ { "nested_types_by_name", (getter)GetNestedTypesByName, NULL,
+ "Nested types by name"},
+ { "extensions", (getter)GetExtensions, NULL, "Extensions Sequence"},
+ { "extensions_by_name", (getter)GetExtensionsByName, NULL,
+ "Extensions by name"},
+ { "extension_ranges", (getter)GetExtensionRanges, NULL, "Extension ranges"},
+ { "enum_types", (getter)GetEnumsSeq, NULL, "Enum sequence"},
+ { "enum_types_by_name", (getter)GetEnumTypesByName, NULL,
+ "Enum types by name"},
+ { "enum_values_by_name", (getter)GetEnumValuesByName, NULL,
+ "Enum values by name"},
+ { "oneofs_by_name", (getter)GetOneofsByName, NULL, "Oneofs by name"},
+ { "oneofs", (getter)GetOneofsSeq, NULL, "Oneofs by name"},
+ { "containing_type", (getter)GetContainingType, (setter)SetContainingType,
+ "Containing type"},
+ { "is_extendable", (getter)IsExtendable, (setter)NULL},
+ { "has_options", (getter)GetHasOptions, (setter)SetHasOptions, "Has Options"},
+ { "_options", (getter)NULL, (setter)SetOptions, "Options"},
+ { "syntax", (getter)GetSyntax, (setter)NULL, "Syntax"},
+ {NULL}
+};
+
+static PyMethodDef Methods[] = {
+ { "GetOptions", (PyCFunction)GetOptions, METH_NOARGS, },
+ { "CopyToProto", (PyCFunction)CopyToProto, METH_O, },
+ { "EnumValueName", (PyCFunction)EnumValueName, METH_VARARGS, },
+ {NULL}
+};
+
+} // namespace message_descriptor
+
+PyTypeObject PyMessageDescriptor_Type = {
+ PyVarObject_HEAD_INIT(&PyType_Type, 0)
+ FULL_MODULE_NAME ".MessageDescriptor", // tp_name
+ sizeof(PyBaseDescriptor), // tp_basicsize
+ 0, // tp_itemsize
+ 0, // tp_dealloc
+ 0, // tp_print
+ 0, // tp_getattr
+ 0, // tp_setattr
+ 0, // tp_compare
+ 0, // tp_repr
+ 0, // tp_as_number
+ 0, // tp_as_sequence
+ 0, // tp_as_mapping
+ 0, // tp_hash
+ 0, // tp_call
+ 0, // tp_str
+ 0, // tp_getattro
+ 0, // tp_setattro
+ 0, // tp_as_buffer
+ Py_TPFLAGS_DEFAULT, // tp_flags
+ "A Message Descriptor", // tp_doc
+ 0, // tp_traverse
+ 0, // tp_clear
+ 0, // tp_richcompare
+ 0, // tp_weaklistoffset
+ 0, // tp_iter
+ 0, // tp_iternext
+ message_descriptor::Methods, // tp_methods
+ 0, // tp_members
+ message_descriptor::Getters, // tp_getset
+ &descriptor::PyBaseDescriptor_Type, // tp_base
+};
+
+PyObject* PyMessageDescriptor_FromDescriptor(
+ const Descriptor* message_descriptor) {
+ return descriptor::NewInternedDescriptor(
+ &PyMessageDescriptor_Type, message_descriptor, NULL);
+}
+
+const Descriptor* PyMessageDescriptor_AsDescriptor(PyObject* obj) {
+ if (!PyObject_TypeCheck(obj, &PyMessageDescriptor_Type)) {
+ PyErr_SetString(PyExc_TypeError, "Not a MessageDescriptor");
+ return NULL;
+ }
+ return reinterpret_cast<const Descriptor*>(
+ reinterpret_cast<PyBaseDescriptor*>(obj)->descriptor);
+}
+
+namespace field_descriptor {
+
+// Unchecked accessor to the C++ pointer.
+static const FieldDescriptor* _GetDescriptor(
+ PyBaseDescriptor *self) {
+ return reinterpret_cast<const FieldDescriptor*>(self->descriptor);
+}
+
+static PyObject* GetFullName(PyBaseDescriptor* self, void *closure) {
+ return PyString_FromCppString(_GetDescriptor(self)->full_name());
+}
+
+static PyObject* GetName(PyBaseDescriptor *self, void *closure) {
+ return PyString_FromCppString(_GetDescriptor(self)->name());
+}
+
+static PyObject* GetCamelcaseName(PyBaseDescriptor* self, void *closure) {
+ return PyString_FromCppString(_GetDescriptor(self)->camelcase_name());
+}
+
+static PyObject* GetType(PyBaseDescriptor *self, void *closure) {
+ return PyInt_FromLong(_GetDescriptor(self)->type());
+}
+
+static PyObject* GetCppType(PyBaseDescriptor *self, void *closure) {
+ return PyInt_FromLong(_GetDescriptor(self)->cpp_type());
+}
+
+static PyObject* GetLabel(PyBaseDescriptor *self, void *closure) {
+ return PyInt_FromLong(_GetDescriptor(self)->label());
+}
+
+static PyObject* GetNumber(PyBaseDescriptor *self, void *closure) {
+ return PyInt_FromLong(_GetDescriptor(self)->number());
+}
+
+static PyObject* GetIndex(PyBaseDescriptor *self, void *closure) {
+ return PyInt_FromLong(_GetDescriptor(self)->index());
+}
+
+static PyObject* GetID(PyBaseDescriptor *self, void *closure) {
+ return PyLong_FromVoidPtr(self);
+}
+
+static PyObject* IsExtension(PyBaseDescriptor *self, void *closure) {
+ return PyBool_FromLong(_GetDescriptor(self)->is_extension());
+}
+
+static PyObject* HasDefaultValue(PyBaseDescriptor *self, void *closure) {
+ return PyBool_FromLong(_GetDescriptor(self)->has_default_value());
+}
+
+static PyObject* GetDefaultValue(PyBaseDescriptor *self, void *closure) {
+ PyObject *result;
+
+ switch (_GetDescriptor(self)->cpp_type()) {
+ case FieldDescriptor::CPPTYPE_INT32: {
+ int32 value = _GetDescriptor(self)->default_value_int32();
+ result = PyInt_FromLong(value);
+ break;
+ }
+ case FieldDescriptor::CPPTYPE_INT64: {
+ int64 value = _GetDescriptor(self)->default_value_int64();
+ result = PyLong_FromLongLong(value);
+ break;
+ }
+ case FieldDescriptor::CPPTYPE_UINT32: {
+ uint32 value = _GetDescriptor(self)->default_value_uint32();
+ result = PyInt_FromSize_t(value);
+ break;
+ }
+ case FieldDescriptor::CPPTYPE_UINT64: {
+ uint64 value = _GetDescriptor(self)->default_value_uint64();
+ result = PyLong_FromUnsignedLongLong(value);
+ break;
+ }
+ case FieldDescriptor::CPPTYPE_FLOAT: {
+ float value = _GetDescriptor(self)->default_value_float();
+ result = PyFloat_FromDouble(value);
+ break;
+ }
+ case FieldDescriptor::CPPTYPE_DOUBLE: {
+ double value = _GetDescriptor(self)->default_value_double();
+ result = PyFloat_FromDouble(value);
+ break;
+ }
+ case FieldDescriptor::CPPTYPE_BOOL: {
+ bool value = _GetDescriptor(self)->default_value_bool();
+ result = PyBool_FromLong(value);
+ break;
+ }
+ case FieldDescriptor::CPPTYPE_STRING: {
+ string value = _GetDescriptor(self)->default_value_string();
+ result = ToStringObject(_GetDescriptor(self), value);
+ break;
+ }
+ case FieldDescriptor::CPPTYPE_ENUM: {
+ const EnumValueDescriptor* value =
+ _GetDescriptor(self)->default_value_enum();
+ result = PyInt_FromLong(value->number());
+ break;
+ }
+ default:
+ PyErr_Format(PyExc_NotImplementedError, "default value for %s",
+ _GetDescriptor(self)->full_name().c_str());
+ return NULL;
+ }
+ return result;
+}
+
+static PyObject* GetCDescriptor(PyObject *self, void *closure) {
+ Py_INCREF(self);
+ return self;
+}
+
+static PyObject *GetEnumType(PyBaseDescriptor *self, void *closure) {
+ const EnumDescriptor* enum_type = _GetDescriptor(self)->enum_type();
+ if (enum_type) {
+ return PyEnumDescriptor_FromDescriptor(enum_type);
+ } else {
+ Py_RETURN_NONE;
+ }
+}
+
+static int SetEnumType(PyBaseDescriptor *self, PyObject *value, void *closure) {
+ return CheckCalledFromGeneratedFile("enum_type");
+}
+
+static PyObject *GetMessageType(PyBaseDescriptor *self, void *closure) {
+ const Descriptor* message_type = _GetDescriptor(self)->message_type();
+ if (message_type) {
+ return PyMessageDescriptor_FromDescriptor(message_type);
+ } else {
+ Py_RETURN_NONE;
+ }
+}
+
+static int SetMessageType(PyBaseDescriptor *self, PyObject *value,
+ void *closure) {
+ return CheckCalledFromGeneratedFile("message_type");
+}
+
+static PyObject* GetContainingType(PyBaseDescriptor *self, void *closure) {
+ const Descriptor* containing_type =
+ _GetDescriptor(self)->containing_type();
+ if (containing_type) {
+ return PyMessageDescriptor_FromDescriptor(containing_type);
+ } else {
+ Py_RETURN_NONE;
+ }
+}
+
+static int SetContainingType(PyBaseDescriptor *self, PyObject *value,
+ void *closure) {
+ return CheckCalledFromGeneratedFile("containing_type");
+}
+
+static PyObject* GetExtensionScope(PyBaseDescriptor *self, void *closure) {
+ const Descriptor* extension_scope =
+ _GetDescriptor(self)->extension_scope();
+ if (extension_scope) {
+ return PyMessageDescriptor_FromDescriptor(extension_scope);
+ } else {
+ Py_RETURN_NONE;
+ }
+}
+
+static PyObject* GetContainingOneof(PyBaseDescriptor *self, void *closure) {
+ const OneofDescriptor* containing_oneof =
+ _GetDescriptor(self)->containing_oneof();
+ if (containing_oneof) {
+ return PyOneofDescriptor_FromDescriptor(containing_oneof);
+ } else {
+ Py_RETURN_NONE;
+ }
+}
+
+static int SetContainingOneof(PyBaseDescriptor *self, PyObject *value,
+ void *closure) {
+ return CheckCalledFromGeneratedFile("containing_oneof");
+}
+
+static PyObject* GetHasOptions(PyBaseDescriptor *self, void *closure) {
+ const FieldOptions& options(_GetDescriptor(self)->options());
+ if (&options != &FieldOptions::default_instance()) {
+ Py_RETURN_TRUE;
+ } else {
+ Py_RETURN_FALSE;
+ }
+}
+static int SetHasOptions(PyBaseDescriptor *self, PyObject *value,
+ void *closure) {
+ return CheckCalledFromGeneratedFile("has_options");
+}
+
+static PyObject* GetOptions(PyBaseDescriptor *self) {
+ return GetOrBuildOptions(_GetDescriptor(self));
+}
+
+static int SetOptions(PyBaseDescriptor *self, PyObject *value,
+ void *closure) {
+ return CheckCalledFromGeneratedFile("_options");
+}
+
+
+static PyGetSetDef Getters[] = {
+ { "full_name", (getter)GetFullName, NULL, "Full name"},
+ { "name", (getter)GetName, NULL, "Unqualified name"},
+ { "camelcase_name", (getter)GetCamelcaseName, NULL, "Camelcase name"},
+ { "type", (getter)GetType, NULL, "C++ Type"},
+ { "cpp_type", (getter)GetCppType, NULL, "C++ Type"},
+ { "label", (getter)GetLabel, NULL, "Label"},
+ { "number", (getter)GetNumber, NULL, "Number"},
+ { "index", (getter)GetIndex, NULL, "Index"},
+ { "default_value", (getter)GetDefaultValue, NULL, "Default Value"},
+ { "has_default_value", (getter)HasDefaultValue},
+ { "is_extension", (getter)IsExtension, NULL, "ID"},
+ { "id", (getter)GetID, NULL, "ID"},
+ { "_cdescriptor", (getter)GetCDescriptor, NULL, "HAACK REMOVE ME"},
+
+ { "message_type", (getter)GetMessageType, (setter)SetMessageType,
+ "Message type"},
+ { "enum_type", (getter)GetEnumType, (setter)SetEnumType, "Enum type"},
+ { "containing_type", (getter)GetContainingType, (setter)SetContainingType,
+ "Containing type"},
+ { "extension_scope", (getter)GetExtensionScope, (setter)NULL,
+ "Extension scope"},
+ { "containing_oneof", (getter)GetContainingOneof, (setter)SetContainingOneof,
+ "Containing oneof"},
+ { "has_options", (getter)GetHasOptions, (setter)SetHasOptions, "Has Options"},
+ { "_options", (getter)NULL, (setter)SetOptions, "Options"},
+ {NULL}
+};
+
+static PyMethodDef Methods[] = {
+ { "GetOptions", (PyCFunction)GetOptions, METH_NOARGS, },
+ {NULL}
+};
+
+} // namespace field_descriptor
+
+PyTypeObject PyFieldDescriptor_Type = {
+ PyVarObject_HEAD_INIT(&PyType_Type, 0)
+ FULL_MODULE_NAME ".FieldDescriptor", // tp_name
+ sizeof(PyBaseDescriptor), // tp_basicsize
+ 0, // tp_itemsize
+ 0, // tp_dealloc
+ 0, // tp_print
+ 0, // tp_getattr
+ 0, // tp_setattr
+ 0, // tp_compare
+ 0, // tp_repr
+ 0, // tp_as_number
+ 0, // tp_as_sequence
+ 0, // tp_as_mapping
+ 0, // tp_hash
+ 0, // tp_call
+ 0, // tp_str
+ 0, // tp_getattro
+ 0, // tp_setattro
+ 0, // tp_as_buffer
+ Py_TPFLAGS_DEFAULT, // tp_flags
+ "A Field Descriptor", // tp_doc
+ 0, // tp_traverse
+ 0, // tp_clear
+ 0, // tp_richcompare
+ 0, // tp_weaklistoffset
+ 0, // tp_iter
+ 0, // tp_iternext
+ field_descriptor::Methods, // tp_methods
+ 0, // tp_members
+ field_descriptor::Getters, // tp_getset
+ &descriptor::PyBaseDescriptor_Type, // tp_base
+};
+
+PyObject* PyFieldDescriptor_FromDescriptor(
+ const FieldDescriptor* field_descriptor) {
+ return descriptor::NewInternedDescriptor(
+ &PyFieldDescriptor_Type, field_descriptor, NULL);
+}
+
+const FieldDescriptor* PyFieldDescriptor_AsDescriptor(PyObject* obj) {
+ if (!PyObject_TypeCheck(obj, &PyFieldDescriptor_Type)) {
+ PyErr_SetString(PyExc_TypeError, "Not a FieldDescriptor");
+ return NULL;
+ }
+ return reinterpret_cast<const FieldDescriptor*>(
+ reinterpret_cast<PyBaseDescriptor*>(obj)->descriptor);
+}
+
+namespace enum_descriptor {
+
+// Unchecked accessor to the C++ pointer.
+static const EnumDescriptor* _GetDescriptor(
+ PyBaseDescriptor *self) {
+ return reinterpret_cast<const EnumDescriptor*>(self->descriptor);
+}
+
+static PyObject* GetFullName(PyBaseDescriptor* self, void *closure) {
+ return PyString_FromCppString(_GetDescriptor(self)->full_name());
+}
+
+static PyObject* GetName(PyBaseDescriptor *self, void *closure) {
+ return PyString_FromCppString(_GetDescriptor(self)->name());
+}
+
+static PyObject* GetFile(PyBaseDescriptor *self, void *closure) {
+ return PyFileDescriptor_FromDescriptor(_GetDescriptor(self)->file());
+}
+
+static PyObject* GetEnumvaluesByName(PyBaseDescriptor* self, void *closure) {
+ return NewEnumValuesByName(_GetDescriptor(self));
+}
+
+static PyObject* GetEnumvaluesByNumber(PyBaseDescriptor* self, void *closure) {
+ return NewEnumValuesByNumber(_GetDescriptor(self));
+}
+
+static PyObject* GetEnumvaluesSeq(PyBaseDescriptor* self, void *closure) {
+ return NewEnumValuesSeq(_GetDescriptor(self));
+}
+
+static PyObject* GetContainingType(PyBaseDescriptor *self, void *closure) {
+ const Descriptor* containing_type =
+ _GetDescriptor(self)->containing_type();
+ if (containing_type) {
+ return PyMessageDescriptor_FromDescriptor(containing_type);
+ } else {
+ Py_RETURN_NONE;
+ }
+}
+
+static int SetContainingType(PyBaseDescriptor *self, PyObject *value,
+ void *closure) {
+ return CheckCalledFromGeneratedFile("containing_type");
+}
+
+
+static PyObject* GetHasOptions(PyBaseDescriptor *self, void *closure) {
+ const EnumOptions& options(_GetDescriptor(self)->options());
+ if (&options != &EnumOptions::default_instance()) {
+ Py_RETURN_TRUE;
+ } else {
+ Py_RETURN_FALSE;
+ }
+}
+static int SetHasOptions(PyBaseDescriptor *self, PyObject *value,
+ void *closure) {
+ return CheckCalledFromGeneratedFile("has_options");
+}
+
+static PyObject* GetOptions(PyBaseDescriptor *self) {
+ return GetOrBuildOptions(_GetDescriptor(self));
+}
+
+static int SetOptions(PyBaseDescriptor *self, PyObject *value,
+ void *closure) {
+ return CheckCalledFromGeneratedFile("_options");
+}
+
+static PyObject* CopyToProto(PyBaseDescriptor *self, PyObject *target) {
+ return CopyToPythonProto<EnumDescriptorProto>(_GetDescriptor(self), target);
+}
+
+static PyMethodDef Methods[] = {
+ { "GetOptions", (PyCFunction)GetOptions, METH_NOARGS, },
+ { "CopyToProto", (PyCFunction)CopyToProto, METH_O, },
+ {NULL}
+};
+
+static PyGetSetDef Getters[] = {
+ { "full_name", (getter)GetFullName, NULL, "Full name"},
+ { "name", (getter)GetName, NULL, "last name"},
+ { "file", (getter)GetFile, NULL, "File descriptor"},
+ { "values", (getter)GetEnumvaluesSeq, NULL, "values"},
+ { "values_by_name", (getter)GetEnumvaluesByName, NULL,
+ "Enum values by name"},
+ { "values_by_number", (getter)GetEnumvaluesByNumber, NULL,
+ "Enum values by number"},
+
+ { "containing_type", (getter)GetContainingType, (setter)SetContainingType,
+ "Containing type"},
+ { "has_options", (getter)GetHasOptions, (setter)SetHasOptions, "Has Options"},
+ { "_options", (getter)NULL, (setter)SetOptions, "Options"},
+ {NULL}
+};
+
+} // namespace enum_descriptor
+
+PyTypeObject PyEnumDescriptor_Type = {
+ PyVarObject_HEAD_INIT(&PyType_Type, 0)
+ FULL_MODULE_NAME ".EnumDescriptor", // tp_name
+ sizeof(PyBaseDescriptor), // tp_basicsize
+ 0, // tp_itemsize
+ 0, // tp_dealloc
+ 0, // tp_print
+ 0, // tp_getattr
+ 0, // tp_setattr
+ 0, // tp_compare
+ 0, // tp_repr
+ 0, // tp_as_number
+ 0, // tp_as_sequence
+ 0, // tp_as_mapping
+ 0, // tp_hash
+ 0, // tp_call
+ 0, // tp_str
+ 0, // tp_getattro
+ 0, // tp_setattro
+ 0, // tp_as_buffer
+ Py_TPFLAGS_DEFAULT, // tp_flags
+ "A Enum Descriptor", // tp_doc
+ 0, // tp_traverse
+ 0, // tp_clear
+ 0, // tp_richcompare
+ 0, // tp_weaklistoffset
+ 0, // tp_iter
+ 0, // tp_iternext
+ enum_descriptor::Methods, // tp_methods
+ 0, // tp_members
+ enum_descriptor::Getters, // tp_getset
+ &descriptor::PyBaseDescriptor_Type, // tp_base
+};
+
+PyObject* PyEnumDescriptor_FromDescriptor(
+ const EnumDescriptor* enum_descriptor) {
+ return descriptor::NewInternedDescriptor(
+ &PyEnumDescriptor_Type, enum_descriptor, NULL);
+}
+
+const EnumDescriptor* PyEnumDescriptor_AsDescriptor(PyObject* obj) {
+ if (!PyObject_TypeCheck(obj, &PyEnumDescriptor_Type)) {
+ PyErr_SetString(PyExc_TypeError, "Not an EnumDescriptor");
+ return NULL;
+ }
+ return reinterpret_cast<const EnumDescriptor*>(
+ reinterpret_cast<PyBaseDescriptor*>(obj)->descriptor);
+}
+
+namespace enumvalue_descriptor {
+
+// Unchecked accessor to the C++ pointer.
+static const EnumValueDescriptor* _GetDescriptor(
+ PyBaseDescriptor *self) {
+ return reinterpret_cast<const EnumValueDescriptor*>(self->descriptor);
+}
+
+static PyObject* GetName(PyBaseDescriptor *self, void *closure) {
+ return PyString_FromCppString(_GetDescriptor(self)->name());
+}
+
+static PyObject* GetNumber(PyBaseDescriptor *self, void *closure) {
+ return PyInt_FromLong(_GetDescriptor(self)->number());
+}
+
+static PyObject* GetIndex(PyBaseDescriptor *self, void *closure) {
+ return PyInt_FromLong(_GetDescriptor(self)->index());
+}
+
+static PyObject* GetType(PyBaseDescriptor *self, void *closure) {
+ return PyEnumDescriptor_FromDescriptor(_GetDescriptor(self)->type());
+}
+
+static PyObject* GetHasOptions(PyBaseDescriptor *self, void *closure) {
+ const EnumValueOptions& options(_GetDescriptor(self)->options());
+ if (&options != &EnumValueOptions::default_instance()) {
+ Py_RETURN_TRUE;
+ } else {
+ Py_RETURN_FALSE;
+ }
+}
+static int SetHasOptions(PyBaseDescriptor *self, PyObject *value,
+ void *closure) {
+ return CheckCalledFromGeneratedFile("has_options");
+}
+
+static PyObject* GetOptions(PyBaseDescriptor *self) {
+ return GetOrBuildOptions(_GetDescriptor(self));
+}
+
+static int SetOptions(PyBaseDescriptor *self, PyObject *value,
+ void *closure) {
+ return CheckCalledFromGeneratedFile("_options");
+}
+
+
+static PyGetSetDef Getters[] = {
+ { "name", (getter)GetName, NULL, "name"},
+ { "number", (getter)GetNumber, NULL, "number"},
+ { "index", (getter)GetIndex, NULL, "index"},
+ { "type", (getter)GetType, NULL, "index"},
+
+ { "has_options", (getter)GetHasOptions, (setter)SetHasOptions, "Has Options"},
+ { "_options", (getter)NULL, (setter)SetOptions, "Options"},
+ {NULL}
+};
+
+static PyMethodDef Methods[] = {
+ { "GetOptions", (PyCFunction)GetOptions, METH_NOARGS, },
+ {NULL}
+};
+
+} // namespace enumvalue_descriptor
+
+PyTypeObject PyEnumValueDescriptor_Type = {
+ PyVarObject_HEAD_INIT(&PyType_Type, 0)
+ FULL_MODULE_NAME ".EnumValueDescriptor", // tp_name
+ sizeof(PyBaseDescriptor), // tp_basicsize
+ 0, // tp_itemsize
+ 0, // tp_dealloc
+ 0, // tp_print
+ 0, // tp_getattr
+ 0, // tp_setattr
+ 0, // tp_compare
+ 0, // tp_repr
+ 0, // tp_as_number
+ 0, // tp_as_sequence
+ 0, // tp_as_mapping
+ 0, // tp_hash
+ 0, // tp_call
+ 0, // tp_str
+ 0, // tp_getattro
+ 0, // tp_setattro
+ 0, // tp_as_buffer
+ Py_TPFLAGS_DEFAULT, // tp_flags
+ "A EnumValue Descriptor", // tp_doc
+ 0, // tp_traverse
+ 0, // tp_clear
+ 0, // tp_richcompare
+ 0, // tp_weaklistoffset
+ 0, // tp_iter
+ 0, // tp_iternext
+ enumvalue_descriptor::Methods, // tp_methods
+ 0, // tp_members
+ enumvalue_descriptor::Getters, // tp_getset
+ &descriptor::PyBaseDescriptor_Type, // tp_base
+};
+
+PyObject* PyEnumValueDescriptor_FromDescriptor(
+ const EnumValueDescriptor* enumvalue_descriptor) {
+ return descriptor::NewInternedDescriptor(
+ &PyEnumValueDescriptor_Type, enumvalue_descriptor, NULL);
+}
+
+namespace file_descriptor {
+
+// Unchecked accessor to the C++ pointer.
+static const FileDescriptor* _GetDescriptor(PyFileDescriptor *self) {
+ return reinterpret_cast<const FileDescriptor*>(self->base.descriptor);
+}
+
+static void Dealloc(PyFileDescriptor* self) {
+ Py_XDECREF(self->serialized_pb);
+ descriptor::Dealloc(&self->base);
+}
+
+static PyObject* GetPool(PyFileDescriptor *self, void *closure) {
+ PyObject* pool = reinterpret_cast<PyObject*>(
+ GetDescriptorPool_FromPool(_GetDescriptor(self)->pool()));
+ Py_XINCREF(pool);
+ return pool;
+}
+
+static PyObject* GetName(PyFileDescriptor *self, void *closure) {
+ return PyString_FromCppString(_GetDescriptor(self)->name());
+}
+
+static PyObject* GetPackage(PyFileDescriptor *self, void *closure) {
+ return PyString_FromCppString(_GetDescriptor(self)->package());
+}
+
+static PyObject* GetSerializedPb(PyFileDescriptor *self, void *closure) {
+ PyObject *serialized_pb = self->serialized_pb;
+ if (serialized_pb != NULL) {
+ Py_INCREF(serialized_pb);
+ return serialized_pb;
+ }
+ FileDescriptorProto file_proto;
+ _GetDescriptor(self)->CopyTo(&file_proto);
+ string contents;
+ file_proto.SerializePartialToString(&contents);
+ self->serialized_pb = PyBytes_FromStringAndSize(
+ contents.c_str(), contents.size());
+ if (self->serialized_pb == NULL) {
+ return NULL;
+ }
+ Py_INCREF(self->serialized_pb);
+ return self->serialized_pb;
+}
+
+static PyObject* GetMessageTypesByName(PyFileDescriptor* self, void *closure) {
+ return NewFileMessageTypesByName(_GetDescriptor(self));
+}
+
+static PyObject* GetEnumTypesByName(PyFileDescriptor* self, void *closure) {
+ return NewFileEnumTypesByName(_GetDescriptor(self));
+}
+
+static PyObject* GetExtensionsByName(PyFileDescriptor* self, void *closure) {
+ return NewFileExtensionsByName(_GetDescriptor(self));
+}
+
+static PyObject* GetServicesByName(PyFileDescriptor* self, void *closure) {
+ return NewFileServicesByName(_GetDescriptor(self));
+}
+
+static PyObject* GetDependencies(PyFileDescriptor* self, void *closure) {
+ return NewFileDependencies(_GetDescriptor(self));
+}
+
+static PyObject* GetPublicDependencies(PyFileDescriptor* self, void *closure) {
+ return NewFilePublicDependencies(_GetDescriptor(self));
+}
+
+static PyObject* GetHasOptions(PyFileDescriptor *self, void *closure) {
+ const FileOptions& options(_GetDescriptor(self)->options());
+ if (&options != &FileOptions::default_instance()) {
+ Py_RETURN_TRUE;
+ } else {
+ Py_RETURN_FALSE;
+ }
+}
+static int SetHasOptions(PyFileDescriptor *self, PyObject *value,
+ void *closure) {
+ return CheckCalledFromGeneratedFile("has_options");
+}
+
+static PyObject* GetOptions(PyFileDescriptor *self) {
+ return GetOrBuildOptions(_GetDescriptor(self));
+}
+
+static int SetOptions(PyFileDescriptor *self, PyObject *value,
+ void *closure) {
+ return CheckCalledFromGeneratedFile("_options");
+}
+
+static PyObject* GetSyntax(PyFileDescriptor *self, void *closure) {
+ return PyString_InternFromString(
+ FileDescriptor::SyntaxName(_GetDescriptor(self)->syntax()));
+}
+
+static PyObject* CopyToProto(PyFileDescriptor *self, PyObject *target) {
+ return CopyToPythonProto<FileDescriptorProto>(_GetDescriptor(self), target);
+}
+
+static PyGetSetDef Getters[] = {
+ { "pool", (getter)GetPool, NULL, "pool"},
+ { "name", (getter)GetName, NULL, "name"},
+ { "package", (getter)GetPackage, NULL, "package"},
+ { "serialized_pb", (getter)GetSerializedPb},
+ { "message_types_by_name", (getter)GetMessageTypesByName, NULL,
+ "Messages by name"},
+ { "enum_types_by_name", (getter)GetEnumTypesByName, NULL, "Enums by name"},
+ { "extensions_by_name", (getter)GetExtensionsByName, NULL,
+ "Extensions by name"},
+ { "services_by_name", (getter)GetServicesByName, NULL, "Services by name"},
+ { "dependencies", (getter)GetDependencies, NULL, "Dependencies"},
+ { "public_dependencies", (getter)GetPublicDependencies, NULL, "Dependencies"},
+
+ { "has_options", (getter)GetHasOptions, (setter)SetHasOptions, "Has Options"},
+ { "_options", (getter)NULL, (setter)SetOptions, "Options"},
+ { "syntax", (getter)GetSyntax, (setter)NULL, "Syntax"},
+ {NULL}
+};
+
+static PyMethodDef Methods[] = {
+ { "GetOptions", (PyCFunction)GetOptions, METH_NOARGS, },
+ { "CopyToProto", (PyCFunction)CopyToProto, METH_O, },
+ {NULL}
+};
+
+} // namespace file_descriptor
+
+PyTypeObject PyFileDescriptor_Type = {
+ PyVarObject_HEAD_INIT(&PyType_Type, 0)
+ FULL_MODULE_NAME ".FileDescriptor", // tp_name
+ sizeof(PyFileDescriptor), // tp_basicsize
+ 0, // tp_itemsize
+ (destructor)file_descriptor::Dealloc, // tp_dealloc
+ 0, // tp_print
+ 0, // tp_getattr
+ 0, // tp_setattr
+ 0, // tp_compare
+ 0, // tp_repr
+ 0, // tp_as_number
+ 0, // tp_as_sequence
+ 0, // tp_as_mapping
+ 0, // tp_hash
+ 0, // tp_call
+ 0, // tp_str
+ 0, // tp_getattro
+ 0, // tp_setattro
+ 0, // tp_as_buffer
+ Py_TPFLAGS_DEFAULT, // tp_flags
+ "A File Descriptor", // tp_doc
+ 0, // tp_traverse
+ 0, // tp_clear
+ 0, // tp_richcompare
+ 0, // tp_weaklistoffset
+ 0, // tp_iter
+ 0, // tp_iternext
+ file_descriptor::Methods, // tp_methods
+ 0, // tp_members
+ file_descriptor::Getters, // tp_getset
+ &descriptor::PyBaseDescriptor_Type, // tp_base
+ 0, // tp_dict
+ 0, // tp_descr_get
+ 0, // tp_descr_set
+ 0, // tp_dictoffset
+ 0, // tp_init
+ 0, // tp_alloc
+ 0, // tp_new
+ PyObject_Del, // tp_free
+};
+
+PyObject* PyFileDescriptor_FromDescriptor(
+ const FileDescriptor* file_descriptor) {
+ return PyFileDescriptor_FromDescriptorWithSerializedPb(file_descriptor,
+ NULL);
+}
+
+PyObject* PyFileDescriptor_FromDescriptorWithSerializedPb(
+ const FileDescriptor* file_descriptor, PyObject *serialized_pb) {
+ bool was_created;
+ PyObject* py_descriptor = descriptor::NewInternedDescriptor(
+ &PyFileDescriptor_Type, file_descriptor, &was_created);
+ if (py_descriptor == NULL) {
+ return NULL;
+ }
+ if (was_created) {
+ PyFileDescriptor* cfile_descriptor =
+ reinterpret_cast<PyFileDescriptor*>(py_descriptor);
+ Py_XINCREF(serialized_pb);
+ cfile_descriptor->serialized_pb = serialized_pb;
+ }
+ // TODO(amauryfa): In the case of a cached object, check that serialized_pb
+ // is the same as before.
+
+ return py_descriptor;
+}
+
+const FileDescriptor* PyFileDescriptor_AsDescriptor(PyObject* obj) {
+ if (!PyObject_TypeCheck(obj, &PyFileDescriptor_Type)) {
+ PyErr_SetString(PyExc_TypeError, "Not a FileDescriptor");
+ return NULL;
+ }
+ return reinterpret_cast<const FileDescriptor*>(
+ reinterpret_cast<PyBaseDescriptor*>(obj)->descriptor);
+}
+
+namespace oneof_descriptor {
+
+// Unchecked accessor to the C++ pointer.
+static const OneofDescriptor* _GetDescriptor(
+ PyBaseDescriptor *self) {
+ return reinterpret_cast<const OneofDescriptor*>(self->descriptor);
+}
+
+static PyObject* GetName(PyBaseDescriptor* self, void *closure) {
+ return PyString_FromCppString(_GetDescriptor(self)->name());
+}
+
+static PyObject* GetFullName(PyBaseDescriptor* self, void *closure) {
+ return PyString_FromCppString(_GetDescriptor(self)->full_name());
+}
+
+static PyObject* GetIndex(PyBaseDescriptor *self, void *closure) {
+ return PyInt_FromLong(_GetDescriptor(self)->index());
+}
+
+static PyObject* GetFields(PyBaseDescriptor* self, void *closure) {
+ return NewOneofFieldsSeq(_GetDescriptor(self));
+}
+
+static PyObject* GetContainingType(PyBaseDescriptor *self, void *closure) {
+ const Descriptor* containing_type =
+ _GetDescriptor(self)->containing_type();
+ if (containing_type) {
+ return PyMessageDescriptor_FromDescriptor(containing_type);
+ } else {
+ Py_RETURN_NONE;
+ }
+}
+
+static PyObject* GetHasOptions(PyBaseDescriptor *self, void *closure) {
+ const OneofOptions& options(_GetDescriptor(self)->options());
+ if (&options != &OneofOptions::default_instance()) {
+ Py_RETURN_TRUE;
+ } else {
+ Py_RETURN_FALSE;
+ }
+}
+static int SetHasOptions(PyBaseDescriptor *self, PyObject *value,
+ void *closure) {
+ return CheckCalledFromGeneratedFile("has_options");
+}
+
+static PyObject* GetOptions(PyBaseDescriptor *self) {
+ return GetOrBuildOptions(_GetDescriptor(self));
+}
+
+static int SetOptions(PyBaseDescriptor *self, PyObject *value,
+ void *closure) {
+ return CheckCalledFromGeneratedFile("_options");
+}
+
+static PyGetSetDef Getters[] = {
+ { "name", (getter)GetName, NULL, "Name"},
+ { "full_name", (getter)GetFullName, NULL, "Full name"},
+ { "index", (getter)GetIndex, NULL, "Index"},
+
+ { "containing_type", (getter)GetContainingType, NULL, "Containing type"},
+ { "has_options", (getter)GetHasOptions, (setter)SetHasOptions, "Has Options"},
+ { "_options", (getter)NULL, (setter)SetOptions, "Options"},
+ { "fields", (getter)GetFields, NULL, "Fields"},
+ {NULL}
+};
+
+static PyMethodDef Methods[] = {
+ { "GetOptions", (PyCFunction)GetOptions, METH_NOARGS },
+ {NULL}
+};
+
+} // namespace oneof_descriptor
+
+PyTypeObject PyOneofDescriptor_Type = {
+ PyVarObject_HEAD_INIT(&PyType_Type, 0)
+ FULL_MODULE_NAME ".OneofDescriptor", // tp_name
+ sizeof(PyBaseDescriptor), // tp_basicsize
+ 0, // tp_itemsize
+ 0, // tp_dealloc
+ 0, // tp_print
+ 0, // tp_getattr
+ 0, // tp_setattr
+ 0, // tp_compare
+ 0, // tp_repr
+ 0, // tp_as_number
+ 0, // tp_as_sequence
+ 0, // tp_as_mapping
+ 0, // tp_hash
+ 0, // tp_call
+ 0, // tp_str
+ 0, // tp_getattro
+ 0, // tp_setattro
+ 0, // tp_as_buffer
+ Py_TPFLAGS_DEFAULT, // tp_flags
+ "A Oneof Descriptor", // tp_doc
+ 0, // tp_traverse
+ 0, // tp_clear
+ 0, // tp_richcompare
+ 0, // tp_weaklistoffset
+ 0, // tp_iter
+ 0, // tp_iternext
+ oneof_descriptor::Methods, // tp_methods
+ 0, // tp_members
+ oneof_descriptor::Getters, // tp_getset
+ &descriptor::PyBaseDescriptor_Type, // tp_base
+};
+
+PyObject* PyOneofDescriptor_FromDescriptor(
+ const OneofDescriptor* oneof_descriptor) {
+ return descriptor::NewInternedDescriptor(
+ &PyOneofDescriptor_Type, oneof_descriptor, NULL);
+}
+
+namespace service_descriptor {
+
+// Unchecked accessor to the C++ pointer.
+static const ServiceDescriptor* _GetDescriptor(
+ PyBaseDescriptor *self) {
+ return reinterpret_cast<const ServiceDescriptor*>(self->descriptor);
+}
+
+static PyObject* GetName(PyBaseDescriptor* self, void *closure) {
+ return PyString_FromCppString(_GetDescriptor(self)->name());
+}
+
+static PyObject* GetFullName(PyBaseDescriptor* self, void *closure) {
+ return PyString_FromCppString(_GetDescriptor(self)->full_name());
+}
+
+static PyObject* GetIndex(PyBaseDescriptor *self, void *closure) {
+ return PyInt_FromLong(_GetDescriptor(self)->index());
+}
+
+static PyObject* GetMethods(PyBaseDescriptor* self, void *closure) {
+ return NewServiceMethodsSeq(_GetDescriptor(self));
+}
+
+static PyObject* GetMethodsByName(PyBaseDescriptor* self, void *closure) {
+ return NewServiceMethodsByName(_GetDescriptor(self));
+}
+
+static PyObject* FindMethodByName(PyBaseDescriptor *self, PyObject* arg) {
+ Py_ssize_t name_size;
+ char* name;
+ if (PyString_AsStringAndSize(arg, &name, &name_size) < 0) {
+ return NULL;
+ }
+
+ const MethodDescriptor* method_descriptor =
+ _GetDescriptor(self)->FindMethodByName(string(name, name_size));
+ if (method_descriptor == NULL) {
+ PyErr_Format(PyExc_KeyError, "Couldn't find method %.200s", name);
+ return NULL;
+ }
+
+ return PyMethodDescriptor_FromDescriptor(method_descriptor);
+}
+
+static PyObject* GetOptions(PyBaseDescriptor *self) {
+ return GetOrBuildOptions(_GetDescriptor(self));
+}
+
+static PyObject* CopyToProto(PyBaseDescriptor *self, PyObject *target) {
+ return CopyToPythonProto<ServiceDescriptorProto>(_GetDescriptor(self),
+ target);
+}
+
+static PyGetSetDef Getters[] = {
+ { "name", (getter)GetName, NULL, "Name", NULL},
+ { "full_name", (getter)GetFullName, NULL, "Full name", NULL},
+ { "index", (getter)GetIndex, NULL, "Index", NULL},
+
+ { "methods", (getter)GetMethods, NULL, "Methods", NULL},
+ { "methods_by_name", (getter)GetMethodsByName, NULL, "Methods by name", NULL},
+ {NULL}
+};
+
+static PyMethodDef Methods[] = {
+ { "GetOptions", (PyCFunction)GetOptions, METH_NOARGS },
+ { "CopyToProto", (PyCFunction)CopyToProto, METH_O, },
+ { "FindMethodByName", (PyCFunction)FindMethodByName, METH_O },
+ {NULL}
+};
+
+} // namespace service_descriptor
+
+PyTypeObject PyServiceDescriptor_Type = {
+ PyVarObject_HEAD_INIT(&PyType_Type, 0)
+ FULL_MODULE_NAME ".ServiceDescriptor", // tp_name
+ sizeof(PyBaseDescriptor), // tp_basicsize
+ 0, // tp_itemsize
+ 0, // tp_dealloc
+ 0, // tp_print
+ 0, // tp_getattr
+ 0, // tp_setattr
+ 0, // tp_compare
+ 0, // tp_repr
+ 0, // tp_as_number
+ 0, // tp_as_sequence
+ 0, // tp_as_mapping
+ 0, // tp_hash
+ 0, // tp_call
+ 0, // tp_str
+ 0, // tp_getattro
+ 0, // tp_setattro
+ 0, // tp_as_buffer
+ Py_TPFLAGS_DEFAULT, // tp_flags
+ "A Service Descriptor", // tp_doc
+ 0, // tp_traverse
+ 0, // tp_clear
+ 0, // tp_richcompare
+ 0, // tp_weaklistoffset
+ 0, // tp_iter
+ 0, // tp_iternext
+ service_descriptor::Methods, // tp_methods
+ 0, // tp_members
+ service_descriptor::Getters, // tp_getset
+ &descriptor::PyBaseDescriptor_Type, // tp_base
+};
+
+PyObject* PyServiceDescriptor_FromDescriptor(
+ const ServiceDescriptor* service_descriptor) {
+ return descriptor::NewInternedDescriptor(
+ &PyServiceDescriptor_Type, service_descriptor, NULL);
+}
+
+namespace method_descriptor {
+
+// Unchecked accessor to the C++ pointer.
+static const MethodDescriptor* _GetDescriptor(
+ PyBaseDescriptor *self) {
+ return reinterpret_cast<const MethodDescriptor*>(self->descriptor);
+}
+
+static PyObject* GetName(PyBaseDescriptor* self, void *closure) {
+ return PyString_FromCppString(_GetDescriptor(self)->name());
+}
+
+static PyObject* GetFullName(PyBaseDescriptor* self, void *closure) {
+ return PyString_FromCppString(_GetDescriptor(self)->full_name());
+}
+
+static PyObject* GetIndex(PyBaseDescriptor *self, void *closure) {
+ return PyInt_FromLong(_GetDescriptor(self)->index());
+}
+
+static PyObject* GetContainingService(PyBaseDescriptor *self, void *closure) {
+ const ServiceDescriptor* containing_service =
+ _GetDescriptor(self)->service();
+ return PyServiceDescriptor_FromDescriptor(containing_service);
+}
+
+static PyObject* GetInputType(PyBaseDescriptor *self, void *closure) {
+ const Descriptor* input_type = _GetDescriptor(self)->input_type();
+ return PyMessageDescriptor_FromDescriptor(input_type);
+}
+
+static PyObject* GetOutputType(PyBaseDescriptor *self, void *closure) {
+ const Descriptor* output_type = _GetDescriptor(self)->output_type();
+ return PyMessageDescriptor_FromDescriptor(output_type);
+}
+
+static PyObject* GetOptions(PyBaseDescriptor *self) {
+ return GetOrBuildOptions(_GetDescriptor(self));
+}
+
+static PyObject* CopyToProto(PyBaseDescriptor *self, PyObject *target) {
+ return CopyToPythonProto<MethodDescriptorProto>(_GetDescriptor(self), target);
+}
+
+static PyGetSetDef Getters[] = {
+ { "name", (getter)GetName, NULL, "Name", NULL},
+ { "full_name", (getter)GetFullName, NULL, "Full name", NULL},
+ { "index", (getter)GetIndex, NULL, "Index", NULL},
+ { "containing_service", (getter)GetContainingService, NULL,
+ "Containing service", NULL},
+ { "input_type", (getter)GetInputType, NULL, "Input type", NULL},
+ { "output_type", (getter)GetOutputType, NULL, "Output type", NULL},
+ {NULL}
+};
+
+static PyMethodDef Methods[] = {
+ { "GetOptions", (PyCFunction)GetOptions, METH_NOARGS, },
+ { "CopyToProto", (PyCFunction)CopyToProto, METH_O, },
+ {NULL}
+};
+
+} // namespace method_descriptor
+
+PyTypeObject PyMethodDescriptor_Type = {
+ PyVarObject_HEAD_INIT(&PyType_Type, 0)
+ FULL_MODULE_NAME ".MethodDescriptor", // tp_name
+ sizeof(PyBaseDescriptor), // tp_basicsize
+ 0, // tp_itemsize
+ 0, // tp_dealloc
+ 0, // tp_print
+ 0, // tp_getattr
+ 0, // tp_setattr
+ 0, // tp_compare
+ 0, // tp_repr
+ 0, // tp_as_number
+ 0, // tp_as_sequence
+ 0, // tp_as_mapping
+ 0, // tp_hash
+ 0, // tp_call
+ 0, // tp_str
+ 0, // tp_getattro
+ 0, // tp_setattro
+ 0, // tp_as_buffer
+ Py_TPFLAGS_DEFAULT, // tp_flags
+ "A Method Descriptor", // tp_doc
+ 0, // tp_traverse
+ 0, // tp_clear
+ 0, // tp_richcompare
+ 0, // tp_weaklistoffset
+ 0, // tp_iter
+ 0, // tp_iternext
+ method_descriptor::Methods, // tp_methods
+ 0, // tp_members
+ method_descriptor::Getters, // tp_getset
+ &descriptor::PyBaseDescriptor_Type, // tp_base
+};
+
+PyObject* PyMethodDescriptor_FromDescriptor(
+ const MethodDescriptor* method_descriptor) {
+ return descriptor::NewInternedDescriptor(
+ &PyMethodDescriptor_Type, method_descriptor, NULL);
+}
+
+// Add a enum values to a type dictionary.
+static bool AddEnumValues(PyTypeObject *type,
+ const EnumDescriptor* enum_descriptor) {
+ for (int i = 0; i < enum_descriptor->value_count(); ++i) {
+ const EnumValueDescriptor* value = enum_descriptor->value(i);
+ ScopedPyObjectPtr obj(PyInt_FromLong(value->number()));
+ if (obj == NULL) {
+ return false;
+ }
+ if (PyDict_SetItemString(type->tp_dict, value->name().c_str(), obj.get()) <
+ 0) {
+ return false;
+ }
+ }
+ return true;
+}
+
+static bool AddIntConstant(PyTypeObject *type, const char* name, int value) {
+ ScopedPyObjectPtr obj(PyInt_FromLong(value));
+ if (PyDict_SetItemString(type->tp_dict, name, obj.get()) < 0) {
+ return false;
+ }
+ return true;
+}
+
+
+bool InitDescriptor() {
+ if (PyType_Ready(&PyMessageDescriptor_Type) < 0)
+ return false;
+
+ if (PyType_Ready(&PyFieldDescriptor_Type) < 0)
+ return false;
+
+ if (!AddEnumValues(&PyFieldDescriptor_Type,
+ FieldDescriptorProto::Label_descriptor())) {
+ return false;
+ }
+ if (!AddEnumValues(&PyFieldDescriptor_Type,
+ FieldDescriptorProto::Type_descriptor())) {
+ return false;
+ }
+#define ADD_FIELDDESC_CONSTANT(NAME) AddIntConstant( \
+ &PyFieldDescriptor_Type, #NAME, FieldDescriptor::NAME)
+ if (!ADD_FIELDDESC_CONSTANT(CPPTYPE_INT32) ||
+ !ADD_FIELDDESC_CONSTANT(CPPTYPE_INT64) ||
+ !ADD_FIELDDESC_CONSTANT(CPPTYPE_UINT32) ||
+ !ADD_FIELDDESC_CONSTANT(CPPTYPE_UINT64) ||
+ !ADD_FIELDDESC_CONSTANT(CPPTYPE_DOUBLE) ||
+ !ADD_FIELDDESC_CONSTANT(CPPTYPE_FLOAT) ||
+ !ADD_FIELDDESC_CONSTANT(CPPTYPE_BOOL) ||
+ !ADD_FIELDDESC_CONSTANT(CPPTYPE_ENUM) ||
+ !ADD_FIELDDESC_CONSTANT(CPPTYPE_STRING) ||
+ !ADD_FIELDDESC_CONSTANT(CPPTYPE_MESSAGE)) {
+ return false;
+ }
+#undef ADD_FIELDDESC_CONSTANT
+
+ if (PyType_Ready(&PyEnumDescriptor_Type) < 0)
+ return false;
+
+ if (PyType_Ready(&PyEnumValueDescriptor_Type) < 0)
+ return false;
+
+ if (PyType_Ready(&PyFileDescriptor_Type) < 0)
+ return false;
+
+ if (PyType_Ready(&PyOneofDescriptor_Type) < 0)
+ return false;
+
+ if (PyType_Ready(&PyServiceDescriptor_Type) < 0)
+ return false;
+
+ if (PyType_Ready(&PyMethodDescriptor_Type) < 0)
+ return false;
+
+ if (!InitDescriptorMappingTypes())
+ return false;
+
+ return true;
+}
+
+} // namespace python
+} // namespace protobuf
+} // namespace google
diff --git a/python/google/protobuf/pyext/descriptor.h b/python/google/protobuf/pyext/descriptor.h
new file mode 100644
index 00000000..1ae0e672
--- /dev/null
+++ b/python/google/protobuf/pyext/descriptor.h
@@ -0,0 +1,103 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Author: petar@google.com (Petar Petrov)
+
+#ifndef GOOGLE_PROTOBUF_PYTHON_CPP_DESCRIPTOR_H__
+#define GOOGLE_PROTOBUF_PYTHON_CPP_DESCRIPTOR_H__
+
+#include <Python.h>
+
+#include <google/protobuf/descriptor.h>
+
+namespace google {
+namespace protobuf {
+namespace python {
+
+extern PyTypeObject PyMessageDescriptor_Type;
+extern PyTypeObject PyFieldDescriptor_Type;
+extern PyTypeObject PyEnumDescriptor_Type;
+extern PyTypeObject PyEnumValueDescriptor_Type;
+extern PyTypeObject PyFileDescriptor_Type;
+extern PyTypeObject PyOneofDescriptor_Type;
+extern PyTypeObject PyServiceDescriptor_Type;
+extern PyTypeObject PyMethodDescriptor_Type;
+
+// Wraps a Descriptor in a Python object.
+// The C++ pointer is usually borrowed from the global DescriptorPool.
+// In any case, it must stay alive as long as the Python object.
+// Returns a new reference.
+PyObject* PyMessageDescriptor_FromDescriptor(const Descriptor* descriptor);
+PyObject* PyFieldDescriptor_FromDescriptor(const FieldDescriptor* descriptor);
+PyObject* PyEnumDescriptor_FromDescriptor(const EnumDescriptor* descriptor);
+PyObject* PyEnumValueDescriptor_FromDescriptor(
+ const EnumValueDescriptor* descriptor);
+PyObject* PyOneofDescriptor_FromDescriptor(const OneofDescriptor* descriptor);
+PyObject* PyFileDescriptor_FromDescriptor(
+ const FileDescriptor* file_descriptor);
+PyObject* PyServiceDescriptor_FromDescriptor(
+ const ServiceDescriptor* descriptor);
+PyObject* PyMethodDescriptor_FromDescriptor(
+ const MethodDescriptor* descriptor);
+
+// Alternate constructor of PyFileDescriptor, used when we already have a
+// serialized FileDescriptorProto that can be cached.
+// Returns a new reference.
+PyObject* PyFileDescriptor_FromDescriptorWithSerializedPb(
+ const FileDescriptor* file_descriptor, PyObject* serialized_pb);
+
+// Return the C++ descriptor pointer.
+// This function checks the parameter type; on error, return NULL with a Python
+// exception set.
+const Descriptor* PyMessageDescriptor_AsDescriptor(PyObject* obj);
+const FieldDescriptor* PyFieldDescriptor_AsDescriptor(PyObject* obj);
+const EnumDescriptor* PyEnumDescriptor_AsDescriptor(PyObject* obj);
+const FileDescriptor* PyFileDescriptor_AsDescriptor(PyObject* obj);
+
+// Returns the raw C++ pointer.
+const void* PyDescriptor_AsVoidPtr(PyObject* obj);
+
+// Check that the calling Python code is the global scope of a _pb2.py module.
+// This function is used to support the current code generated by the proto
+// compiler, which insists on modifying descriptors after they have been
+// created.
+//
+// stacklevel indicates which Python frame should be the _pb2.py module.
+//
+// Don't use this function outside descriptor classes.
+bool _CalledFromGeneratedFile(int stacklevel);
+
+bool InitDescriptor();
+
+} // namespace python
+} // namespace protobuf
+
+} // namespace google
+#endif // GOOGLE_PROTOBUF_PYTHON_CPP_DESCRIPTOR_H__
diff --git a/python/google/protobuf/pyext/descriptor_containers.cc b/python/google/protobuf/pyext/descriptor_containers.cc
new file mode 100644
index 00000000..d0aae9c9
--- /dev/null
+++ b/python/google/protobuf/pyext/descriptor_containers.cc
@@ -0,0 +1,1786 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Mappings and Sequences of descriptors.
+// Used by Descriptor.fields_by_name, EnumDescriptor.values...
+//
+// They avoid the allocation of a full dictionary or a full list: they simply
+// store a pointer to the parent descriptor, use the C++ Descriptor methods (see
+// google/protobuf/descriptor.h) to retrieve other descriptors, and create
+// Python objects on the fly.
+//
+// The containers fully conform to abc.Mapping and abc.Sequence, and behave just
+// like read-only dictionaries and lists.
+//
+// Because the interface of C++ Descriptors is quite regular, this file actually
+// defines only three types, the exact behavior of a container is controlled by
+// a DescriptorContainerDef structure, which contains functions that uses the
+// public Descriptor API.
+//
+// Note: This DescriptorContainerDef is similar to the "virtual methods table"
+// that a C++ compiler generates for a class. We have to make it explicit
+// because the Python API is based on C, and does not play well with C++
+// inheritance.
+
+#include <Python.h>
+
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/pyext/descriptor_containers.h>
+#include <google/protobuf/pyext/descriptor_pool.h>
+#include <google/protobuf/pyext/descriptor.h>
+#include <google/protobuf/pyext/scoped_pyobject_ptr.h>
+
+#if PY_MAJOR_VERSION >= 3
+ #define PyString_FromStringAndSize PyUnicode_FromStringAndSize
+ #define PyString_FromFormat PyUnicode_FromFormat
+ #define PyInt_FromLong PyLong_FromLong
+ #if PY_VERSION_HEX < 0x03030000
+ #error "Python 3.0 - 3.2 are not supported."
+ #endif
+ #define PyString_AsStringAndSize(ob, charpp, sizep) \
+ (PyUnicode_Check(ob)? \
+ ((*(charpp) = PyUnicode_AsUTF8AndSize(ob, (sizep))) == NULL? -1: 0): \
+ PyBytes_AsStringAndSize(ob, (charpp), (sizep)))
+#endif
+
+namespace google {
+namespace protobuf {
+namespace python {
+
+struct PyContainer;
+
+typedef int (*CountMethod)(PyContainer* self);
+typedef const void* (*GetByIndexMethod)(PyContainer* self, int index);
+typedef const void* (*GetByNameMethod)(PyContainer* self, const string& name);
+typedef const void* (*GetByCamelcaseNameMethod)(PyContainer* self,
+ const string& name);
+typedef const void* (*GetByNumberMethod)(PyContainer* self, int index);
+typedef PyObject* (*NewObjectFromItemMethod)(const void* descriptor);
+typedef const string& (*GetItemNameMethod)(const void* descriptor);
+typedef const string& (*GetItemCamelcaseNameMethod)(const void* descriptor);
+typedef int (*GetItemNumberMethod)(const void* descriptor);
+typedef int (*GetItemIndexMethod)(const void* descriptor);
+
+struct DescriptorContainerDef {
+ const char* mapping_name;
+ // Returns the number of items in the container.
+ CountMethod count_fn;
+ // Retrieve item by index (usually the order of declaration in the proto file)
+ // Used by sequences, but also iterators. 0 <= index < Count().
+ GetByIndexMethod get_by_index_fn;
+ // Retrieve item by name (usually a call to some 'FindByName' method).
+ // Used by "by_name" mappings.
+ GetByNameMethod get_by_name_fn;
+ // Retrieve item by camelcase name (usually a call to some
+ // 'FindByCamelcaseName' method). Used by "by_camelcase_name" mappings.
+ GetByCamelcaseNameMethod get_by_camelcase_name_fn;
+ // Retrieve item by declared number (field tag, or enum value).
+ // Used by "by_number" mappings.
+ GetByNumberMethod get_by_number_fn;
+ // Converts a item C++ descriptor to a Python object. Returns a new reference.
+ NewObjectFromItemMethod new_object_from_item_fn;
+ // Retrieve the name of an item. Used by iterators on "by_name" mappings.
+ GetItemNameMethod get_item_name_fn;
+ // Retrieve the camelcase name of an item. Used by iterators on
+ // "by_camelcase_name" mappings.
+ GetItemCamelcaseNameMethod get_item_camelcase_name_fn;
+ // Retrieve the number of an item. Used by iterators on "by_number" mappings.
+ GetItemNumberMethod get_item_number_fn;
+ // Retrieve the index of an item for the container type.
+ // Used by "__contains__".
+ // If not set, "x in sequence" will do a linear search.
+ GetItemIndexMethod get_item_index_fn;
+};
+
+struct PyContainer {
+ PyObject_HEAD
+
+ // The proto2 descriptor this container belongs to the global DescriptorPool.
+ const void* descriptor;
+
+ // A pointer to a static structure with function pointers that control the
+ // behavior of the container. Very similar to the table of virtual functions
+ // of a C++ class.
+ const DescriptorContainerDef* container_def;
+
+ // The kind of container: list, or dict by name or value.
+ enum ContainerKind {
+ KIND_SEQUENCE,
+ KIND_BYNAME,
+ KIND_BYCAMELCASENAME,
+ KIND_BYNUMBER,
+ } kind;
+};
+
+struct PyContainerIterator {
+ PyObject_HEAD
+
+ // The container we are iterating over. Own a reference.
+ PyContainer* container;
+
+ // The current index in the iterator.
+ int index;
+
+ // The kind of container: list, or dict by name or value.
+ enum IterKind {
+ KIND_ITERKEY,
+ KIND_ITERVALUE,
+ KIND_ITERITEM,
+ KIND_ITERVALUE_REVERSED, // For sequences
+ } kind;
+};
+
+namespace descriptor {
+
+// Returns the C++ item descriptor for a given Python key.
+// When the descriptor is found, return true and set *item.
+// When the descriptor is not found, return true, but set *item to NULL.
+// On error, returns false with an exception set.
+static bool _GetItemByKey(PyContainer* self, PyObject* key, const void** item) {
+ switch (self->kind) {
+ case PyContainer::KIND_BYNAME:
+ {
+ char* name;
+ Py_ssize_t name_size;
+ if (PyString_AsStringAndSize(key, &name, &name_size) < 0) {
+ if (PyErr_ExceptionMatches(PyExc_TypeError)) {
+ // Not a string, cannot be in the container.
+ PyErr_Clear();
+ *item = NULL;
+ return true;
+ }
+ return false;
+ }
+ *item = self->container_def->get_by_name_fn(
+ self, string(name, name_size));
+ return true;
+ }
+ case PyContainer::KIND_BYCAMELCASENAME:
+ {
+ char* camelcase_name;
+ Py_ssize_t name_size;
+ if (PyString_AsStringAndSize(key, &camelcase_name, &name_size) < 0) {
+ if (PyErr_ExceptionMatches(PyExc_TypeError)) {
+ // Not a string, cannot be in the container.
+ PyErr_Clear();
+ *item = NULL;
+ return true;
+ }
+ return false;
+ }
+ *item = self->container_def->get_by_camelcase_name_fn(
+ self, string(camelcase_name, name_size));
+ return true;
+ }
+ case PyContainer::KIND_BYNUMBER:
+ {
+ Py_ssize_t number = PyNumber_AsSsize_t(key, NULL);
+ if (number == -1 && PyErr_Occurred()) {
+ if (PyErr_ExceptionMatches(PyExc_TypeError)) {
+ // Not a number, cannot be in the container.
+ PyErr_Clear();
+ *item = NULL;
+ return true;
+ }
+ return false;
+ }
+ *item = self->container_def->get_by_number_fn(self, number);
+ return true;
+ }
+ default:
+ PyErr_SetNone(PyExc_NotImplementedError);
+ return false;
+ }
+}
+
+// Returns the key of the object at the given index.
+// Used when iterating over mappings.
+static PyObject* _NewKey_ByIndex(PyContainer* self, Py_ssize_t index) {
+ const void* item = self->container_def->get_by_index_fn(self, index);
+ switch (self->kind) {
+ case PyContainer::KIND_BYNAME:
+ {
+ const string& name(self->container_def->get_item_name_fn(item));
+ return PyString_FromStringAndSize(name.c_str(), name.size());
+ }
+ case PyContainer::KIND_BYCAMELCASENAME:
+ {
+ const string& name(
+ self->container_def->get_item_camelcase_name_fn(item));
+ return PyString_FromStringAndSize(name.c_str(), name.size());
+ }
+ case PyContainer::KIND_BYNUMBER:
+ {
+ int value = self->container_def->get_item_number_fn(item);
+ return PyInt_FromLong(value);
+ }
+ default:
+ PyErr_SetNone(PyExc_NotImplementedError);
+ return NULL;
+ }
+}
+
+// Returns the object at the given index.
+// Also used when iterating over mappings.
+static PyObject* _NewObj_ByIndex(PyContainer* self, Py_ssize_t index) {
+ return self->container_def->new_object_from_item_fn(
+ self->container_def->get_by_index_fn(self, index));
+}
+
+static Py_ssize_t Length(PyContainer* self) {
+ return self->container_def->count_fn(self);
+}
+
+// The DescriptorMapping type.
+
+static PyObject* Subscript(PyContainer* self, PyObject* key) {
+ const void* item = NULL;
+ if (!_GetItemByKey(self, key, &item)) {
+ return NULL;
+ }
+ if (!item) {
+ PyErr_SetObject(PyExc_KeyError, key);
+ return NULL;
+ }
+ return self->container_def->new_object_from_item_fn(item);
+}
+
+static int AssSubscript(PyContainer* self, PyObject* key, PyObject* value) {
+ if (_CalledFromGeneratedFile(0)) {
+ return 0;
+ }
+ PyErr_Format(PyExc_TypeError,
+ "'%.200s' object does not support item assignment",
+ Py_TYPE(self)->tp_name);
+ return -1;
+}
+
+static PyMappingMethods MappingMappingMethods = {
+ (lenfunc)Length, // mp_length
+ (binaryfunc)Subscript, // mp_subscript
+ (objobjargproc)AssSubscript, // mp_ass_subscript
+};
+
+static int Contains(PyContainer* self, PyObject* key) {
+ const void* item = NULL;
+ if (!_GetItemByKey(self, key, &item)) {
+ return -1;
+ }
+ if (item) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+static PyObject* ContainerRepr(PyContainer* self) {
+ const char* kind = "";
+ switch (self->kind) {
+ case PyContainer::KIND_SEQUENCE:
+ kind = "sequence";
+ break;
+ case PyContainer::KIND_BYNAME:
+ kind = "mapping by name";
+ break;
+ case PyContainer::KIND_BYCAMELCASENAME:
+ kind = "mapping by camelCase name";
+ break;
+ case PyContainer::KIND_BYNUMBER:
+ kind = "mapping by number";
+ break;
+ }
+ return PyString_FromFormat(
+ "<%s %s>", self->container_def->mapping_name, kind);
+}
+
+extern PyTypeObject DescriptorMapping_Type;
+extern PyTypeObject DescriptorSequence_Type;
+
+// A sequence container can only be equal to another sequence container, or (for
+// backward compatibility) to a list containing the same items.
+// Returns 1 if equal, 0 if unequal, -1 on error.
+static int DescriptorSequence_Equal(PyContainer* self, PyObject* other) {
+ // Check the identity of C++ pointers.
+ if (PyObject_TypeCheck(other, &DescriptorSequence_Type)) {
+ PyContainer* other_container = reinterpret_cast<PyContainer*>(other);
+ if (self->descriptor == other_container->descriptor &&
+ self->container_def == other_container->container_def &&
+ self->kind == other_container->kind) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+
+ // If other is a list
+ if (PyList_Check(other)) {
+ // return list(self) == other
+ int size = Length(self);
+ if (size != PyList_Size(other)) {
+ return false;
+ }
+ for (int index = 0; index < size; index++) {
+ ScopedPyObjectPtr value1(_NewObj_ByIndex(self, index));
+ if (value1 == NULL) {
+ return -1;
+ }
+ PyObject* value2 = PyList_GetItem(other, index);
+ if (value2 == NULL) {
+ return -1;
+ }
+ int cmp = PyObject_RichCompareBool(value1.get(), value2, Py_EQ);
+ if (cmp != 1) // error or not equal
+ return cmp;
+ }
+ // All items were found and equal
+ return 1;
+ }
+
+ // Any other object is different.
+ return 0;
+}
+
+// A mapping container can only be equal to another mapping container, or (for
+// backward compatibility) to a dict containing the same items.
+// Returns 1 if equal, 0 if unequal, -1 on error.
+static int DescriptorMapping_Equal(PyContainer* self, PyObject* other) {
+ // Check the identity of C++ pointers.
+ if (PyObject_TypeCheck(other, &DescriptorMapping_Type)) {
+ PyContainer* other_container = reinterpret_cast<PyContainer*>(other);
+ if (self->descriptor == other_container->descriptor &&
+ self->container_def == other_container->container_def &&
+ self->kind == other_container->kind) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+
+ // If other is a dict
+ if (PyDict_Check(other)) {
+ // equivalent to dict(self.items()) == other
+ int size = Length(self);
+ if (size != PyDict_Size(other)) {
+ return false;
+ }
+ for (int index = 0; index < size; index++) {
+ ScopedPyObjectPtr key(_NewKey_ByIndex(self, index));
+ if (key == NULL) {
+ return -1;
+ }
+ ScopedPyObjectPtr value1(_NewObj_ByIndex(self, index));
+ if (value1 == NULL) {
+ return -1;
+ }
+ PyObject* value2 = PyDict_GetItem(other, key.get());
+ if (value2 == NULL) {
+ // Not found in the other dictionary
+ return 0;
+ }
+ int cmp = PyObject_RichCompareBool(value1.get(), value2, Py_EQ);
+ if (cmp != 1) // error or not equal
+ return cmp;
+ }
+ // All items were found and equal
+ return 1;
+ }
+
+ // Any other object is different.
+ return 0;
+}
+
+static PyObject* RichCompare(PyContainer* self, PyObject* other, int opid) {
+ if (opid != Py_EQ && opid != Py_NE) {
+ Py_INCREF(Py_NotImplemented);
+ return Py_NotImplemented;
+ }
+
+ int result;
+
+ if (self->kind == PyContainer::KIND_SEQUENCE) {
+ result = DescriptorSequence_Equal(self, other);
+ } else {
+ result = DescriptorMapping_Equal(self, other);
+ }
+ if (result < 0) {
+ return NULL;
+ }
+ if (result ^ (opid == Py_NE)) {
+ Py_RETURN_TRUE;
+ } else {
+ Py_RETURN_FALSE;
+ }
+}
+
+static PySequenceMethods MappingSequenceMethods = {
+ 0, // sq_length
+ 0, // sq_concat
+ 0, // sq_repeat
+ 0, // sq_item
+ 0, // sq_slice
+ 0, // sq_ass_item
+ 0, // sq_ass_slice
+ (objobjproc)Contains, // sq_contains
+};
+
+static PyObject* Get(PyContainer* self, PyObject* args) {
+ PyObject* key;
+ PyObject* default_value = Py_None;
+ if (!PyArg_UnpackTuple(args, "get", 1, 2, &key, &default_value)) {
+ return NULL;
+ }
+
+ const void* item;
+ if (!_GetItemByKey(self, key, &item)) {
+ return NULL;
+ }
+ if (item == NULL) {
+ Py_INCREF(default_value);
+ return default_value;
+ }
+ return self->container_def->new_object_from_item_fn(item);
+}
+
+static PyObject* Keys(PyContainer* self, PyObject* args) {
+ Py_ssize_t count = Length(self);
+ ScopedPyObjectPtr list(PyList_New(count));
+ if (list == NULL) {
+ return NULL;
+ }
+ for (Py_ssize_t index = 0; index < count; ++index) {
+ PyObject* key = _NewKey_ByIndex(self, index);
+ if (key == NULL) {
+ return NULL;
+ }
+ PyList_SET_ITEM(list.get(), index, key);
+ }
+ return list.release();
+}
+
+static PyObject* Values(PyContainer* self, PyObject* args) {
+ Py_ssize_t count = Length(self);
+ ScopedPyObjectPtr list(PyList_New(count));
+ if (list == NULL) {
+ return NULL;
+ }
+ for (Py_ssize_t index = 0; index < count; ++index) {
+ PyObject* value = _NewObj_ByIndex(self, index);
+ if (value == NULL) {
+ return NULL;
+ }
+ PyList_SET_ITEM(list.get(), index, value);
+ }
+ return list.release();
+}
+
+static PyObject* Items(PyContainer* self, PyObject* args) {
+ Py_ssize_t count = Length(self);
+ ScopedPyObjectPtr list(PyList_New(count));
+ if (list == NULL) {
+ return NULL;
+ }
+ for (Py_ssize_t index = 0; index < count; ++index) {
+ ScopedPyObjectPtr obj(PyTuple_New(2));
+ if (obj == NULL) {
+ return NULL;
+ }
+ PyObject* key = _NewKey_ByIndex(self, index);
+ if (key == NULL) {
+ return NULL;
+ }
+ PyTuple_SET_ITEM(obj.get(), 0, key);
+ PyObject* value = _NewObj_ByIndex(self, index);
+ if (value == NULL) {
+ return NULL;
+ }
+ PyTuple_SET_ITEM(obj.get(), 1, value);
+ PyList_SET_ITEM(list.get(), index, obj.release());
+ }
+ return list.release();
+}
+
+static PyObject* NewContainerIterator(PyContainer* mapping,
+ PyContainerIterator::IterKind kind);
+
+static PyObject* Iter(PyContainer* self) {
+ return NewContainerIterator(self, PyContainerIterator::KIND_ITERKEY);
+}
+static PyObject* IterKeys(PyContainer* self, PyObject* args) {
+ return NewContainerIterator(self, PyContainerIterator::KIND_ITERKEY);
+}
+static PyObject* IterValues(PyContainer* self, PyObject* args) {
+ return NewContainerIterator(self, PyContainerIterator::KIND_ITERVALUE);
+}
+static PyObject* IterItems(PyContainer* self, PyObject* args) {
+ return NewContainerIterator(self, PyContainerIterator::KIND_ITERITEM);
+}
+
+static PyMethodDef MappingMethods[] = {
+ { "get", (PyCFunction)Get, METH_VARARGS, },
+ { "keys", (PyCFunction)Keys, METH_NOARGS, },
+ { "values", (PyCFunction)Values, METH_NOARGS, },
+ { "items", (PyCFunction)Items, METH_NOARGS, },
+ { "iterkeys", (PyCFunction)IterKeys, METH_NOARGS, },
+ { "itervalues", (PyCFunction)IterValues, METH_NOARGS, },
+ { "iteritems", (PyCFunction)IterItems, METH_NOARGS, },
+ {NULL}
+};
+
+PyTypeObject DescriptorMapping_Type = {
+ PyVarObject_HEAD_INIT(&PyType_Type, 0)
+ "DescriptorMapping", // tp_name
+ sizeof(PyContainer), // tp_basicsize
+ 0, // tp_itemsize
+ 0, // tp_dealloc
+ 0, // tp_print
+ 0, // tp_getattr
+ 0, // tp_setattr
+ 0, // tp_compare
+ (reprfunc)ContainerRepr, // tp_repr
+ 0, // tp_as_number
+ &MappingSequenceMethods, // tp_as_sequence
+ &MappingMappingMethods, // tp_as_mapping
+ 0, // tp_hash
+ 0, // tp_call
+ 0, // tp_str
+ 0, // tp_getattro
+ 0, // tp_setattro
+ 0, // tp_as_buffer
+ Py_TPFLAGS_DEFAULT, // tp_flags
+ 0, // tp_doc
+ 0, // tp_traverse
+ 0, // tp_clear
+ (richcmpfunc)RichCompare, // tp_richcompare
+ 0, // tp_weaklistoffset
+ (getiterfunc)Iter, // tp_iter
+ 0, // tp_iternext
+ MappingMethods, // tp_methods
+ 0, // tp_members
+ 0, // tp_getset
+ 0, // tp_base
+ 0, // tp_dict
+ 0, // tp_descr_get
+ 0, // tp_descr_set
+ 0, // tp_dictoffset
+ 0, // tp_init
+ 0, // tp_alloc
+ 0, // tp_new
+ 0, // tp_free
+};
+
+// The DescriptorSequence type.
+
+static PyObject* GetItem(PyContainer* self, Py_ssize_t index) {
+ if (index < 0) {
+ index += Length(self);
+ }
+ if (index < 0 || index >= Length(self)) {
+ PyErr_SetString(PyExc_IndexError, "index out of range");
+ return NULL;
+ }
+ return _NewObj_ByIndex(self, index);
+}
+
+static PyObject *
+SeqSubscript(PyContainer* self, PyObject* item) {
+ if (PyIndex_Check(item)) {
+ Py_ssize_t index;
+ index = PyNumber_AsSsize_t(item, PyExc_IndexError);
+ if (index == -1 && PyErr_Occurred())
+ return NULL;
+ return GetItem(self, index);
+ }
+ // Materialize the list and delegate the operation to it.
+ ScopedPyObjectPtr list(PyObject_CallFunctionObjArgs(
+ reinterpret_cast<PyObject*>(&PyList_Type), self, NULL));
+ if (list == NULL) {
+ return NULL;
+ }
+ return Py_TYPE(list.get())->tp_as_mapping->mp_subscript(list.get(), item);
+}
+
+// Returns the position of the item in the sequence, of -1 if not found.
+// This function never fails.
+int Find(PyContainer* self, PyObject* item) {
+ // The item can only be in one position: item.index.
+ // Check that self[item.index] == item, it's faster than a linear search.
+ //
+ // This assumes that sequences are only defined by syntax of the .proto file:
+ // a specific item belongs to only one sequence, depending on its position in
+ // the .proto file definition.
+ const void* descriptor_ptr = PyDescriptor_AsVoidPtr(item);
+ if (descriptor_ptr == NULL) {
+ // Not a descriptor, it cannot be in the list.
+ return -1;
+ }
+ if (self->container_def->get_item_index_fn) {
+ int index = self->container_def->get_item_index_fn(descriptor_ptr);
+ if (index < 0 || index >= Length(self)) {
+ // This index is not from this collection.
+ return -1;
+ }
+ if (self->container_def->get_by_index_fn(self, index) != descriptor_ptr) {
+ // The descriptor at this index is not the same.
+ return -1;
+ }
+ // self[item.index] == item, so return the index.
+ return index;
+ } else {
+ // Fall back to linear search.
+ int length = Length(self);
+ for (int index=0; index < length; index++) {
+ if (self->container_def->get_by_index_fn(self, index) == descriptor_ptr) {
+ return index;
+ }
+ }
+ // Not found
+ return -1;
+ }
+}
+
+// Implements list.index(): the position of the item is in the sequence.
+static PyObject* Index(PyContainer* self, PyObject* item) {
+ int position = Find(self, item);
+ if (position < 0) {
+ // Not found
+ PyErr_SetNone(PyExc_ValueError);
+ return NULL;
+ } else {
+ return PyInt_FromLong(position);
+ }
+}
+// Implements "list.__contains__()": is the object in the sequence.
+static int SeqContains(PyContainer* self, PyObject* item) {
+ int position = Find(self, item);
+ if (position < 0) {
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+// Implements list.count(): number of occurrences of the item in the sequence.
+// An item can only appear once in a sequence. If it exists, return 1.
+static PyObject* Count(PyContainer* self, PyObject* item) {
+ int position = Find(self, item);
+ if (position < 0) {
+ return PyInt_FromLong(0);
+ } else {
+ return PyInt_FromLong(1);
+ }
+}
+
+static PyObject* Append(PyContainer* self, PyObject* args) {
+ if (_CalledFromGeneratedFile(0)) {
+ Py_RETURN_NONE;
+ }
+ PyErr_Format(PyExc_TypeError,
+ "'%.200s' object is not a mutable sequence",
+ Py_TYPE(self)->tp_name);
+ return NULL;
+}
+
+static PyObject* Reversed(PyContainer* self, PyObject* args) {
+ return NewContainerIterator(self,
+ PyContainerIterator::KIND_ITERVALUE_REVERSED);
+}
+
+static PyMethodDef SeqMethods[] = {
+ { "index", (PyCFunction)Index, METH_O, },
+ { "count", (PyCFunction)Count, METH_O, },
+ { "append", (PyCFunction)Append, METH_O, },
+ { "__reversed__", (PyCFunction)Reversed, METH_NOARGS, },
+ {NULL}
+};
+
+static PySequenceMethods SeqSequenceMethods = {
+ (lenfunc)Length, // sq_length
+ 0, // sq_concat
+ 0, // sq_repeat
+ (ssizeargfunc)GetItem, // sq_item
+ 0, // sq_slice
+ 0, // sq_ass_item
+ 0, // sq_ass_slice
+ (objobjproc)SeqContains, // sq_contains
+};
+
+static PyMappingMethods SeqMappingMethods = {
+ (lenfunc)Length, // mp_length
+ (binaryfunc)SeqSubscript, // mp_subscript
+ 0, // mp_ass_subscript
+};
+
+PyTypeObject DescriptorSequence_Type = {
+ PyVarObject_HEAD_INIT(&PyType_Type, 0)
+ "DescriptorSequence", // tp_name
+ sizeof(PyContainer), // tp_basicsize
+ 0, // tp_itemsize
+ 0, // tp_dealloc
+ 0, // tp_print
+ 0, // tp_getattr
+ 0, // tp_setattr
+ 0, // tp_compare
+ (reprfunc)ContainerRepr, // tp_repr
+ 0, // tp_as_number
+ &SeqSequenceMethods, // tp_as_sequence
+ &SeqMappingMethods, // tp_as_mapping
+ 0, // tp_hash
+ 0, // tp_call
+ 0, // tp_str
+ 0, // tp_getattro
+ 0, // tp_setattro
+ 0, // tp_as_buffer
+ Py_TPFLAGS_DEFAULT, // tp_flags
+ 0, // tp_doc
+ 0, // tp_traverse
+ 0, // tp_clear
+ (richcmpfunc)RichCompare, // tp_richcompare
+ 0, // tp_weaklistoffset
+ 0, // tp_iter
+ 0, // tp_iternext
+ SeqMethods, // tp_methods
+ 0, // tp_members
+ 0, // tp_getset
+ 0, // tp_base
+ 0, // tp_dict
+ 0, // tp_descr_get
+ 0, // tp_descr_set
+ 0, // tp_dictoffset
+ 0, // tp_init
+ 0, // tp_alloc
+ 0, // tp_new
+ 0, // tp_free
+};
+
+static PyObject* NewMappingByName(
+ DescriptorContainerDef* container_def, const void* descriptor) {
+ PyContainer* self = PyObject_New(PyContainer, &DescriptorMapping_Type);
+ if (self == NULL) {
+ return NULL;
+ }
+ self->descriptor = descriptor;
+ self->container_def = container_def;
+ self->kind = PyContainer::KIND_BYNAME;
+ return reinterpret_cast<PyObject*>(self);
+}
+
+static PyObject* NewMappingByCamelcaseName(
+ DescriptorContainerDef* container_def, const void* descriptor) {
+ PyContainer* self = PyObject_New(PyContainer, &DescriptorMapping_Type);
+ if (self == NULL) {
+ return NULL;
+ }
+ self->descriptor = descriptor;
+ self->container_def = container_def;
+ self->kind = PyContainer::KIND_BYCAMELCASENAME;
+ return reinterpret_cast<PyObject*>(self);
+}
+
+static PyObject* NewMappingByNumber(
+ DescriptorContainerDef* container_def, const void* descriptor) {
+ if (container_def->get_by_number_fn == NULL ||
+ container_def->get_item_number_fn == NULL) {
+ PyErr_SetNone(PyExc_NotImplementedError);
+ return NULL;
+ }
+ PyContainer* self = PyObject_New(PyContainer, &DescriptorMapping_Type);
+ if (self == NULL) {
+ return NULL;
+ }
+ self->descriptor = descriptor;
+ self->container_def = container_def;
+ self->kind = PyContainer::KIND_BYNUMBER;
+ return reinterpret_cast<PyObject*>(self);
+}
+
+static PyObject* NewSequence(
+ DescriptorContainerDef* container_def, const void* descriptor) {
+ PyContainer* self = PyObject_New(PyContainer, &DescriptorSequence_Type);
+ if (self == NULL) {
+ return NULL;
+ }
+ self->descriptor = descriptor;
+ self->container_def = container_def;
+ self->kind = PyContainer::KIND_SEQUENCE;
+ return reinterpret_cast<PyObject*>(self);
+}
+
+// Implement iterators over PyContainers.
+
+static void Iterator_Dealloc(PyContainerIterator* self) {
+ Py_CLEAR(self->container);
+ Py_TYPE(self)->tp_free(reinterpret_cast<PyObject*>(self));
+}
+
+static PyObject* Iterator_Next(PyContainerIterator* self) {
+ int count = self->container->container_def->count_fn(self->container);
+ if (self->index >= count) {
+ // Return NULL with no exception to indicate the end.
+ return NULL;
+ }
+ int index = self->index;
+ self->index += 1;
+ switch (self->kind) {
+ case PyContainerIterator::KIND_ITERKEY:
+ return _NewKey_ByIndex(self->container, index);
+ case PyContainerIterator::KIND_ITERVALUE:
+ return _NewObj_ByIndex(self->container, index);
+ case PyContainerIterator::KIND_ITERVALUE_REVERSED:
+ return _NewObj_ByIndex(self->container, count - index - 1);
+ case PyContainerIterator::KIND_ITERITEM:
+ {
+ PyObject* obj = PyTuple_New(2);
+ if (obj == NULL) {
+ return NULL;
+ }
+ PyObject* key = _NewKey_ByIndex(self->container, index);
+ if (key == NULL) {
+ Py_DECREF(obj);
+ return NULL;
+ }
+ PyTuple_SET_ITEM(obj, 0, key);
+ PyObject* value = _NewObj_ByIndex(self->container, index);
+ if (value == NULL) {
+ Py_DECREF(obj);
+ return NULL;
+ }
+ PyTuple_SET_ITEM(obj, 1, value);
+ return obj;
+ }
+ default:
+ PyErr_SetNone(PyExc_NotImplementedError);
+ return NULL;
+ }
+}
+
+static PyTypeObject ContainerIterator_Type = {
+ PyVarObject_HEAD_INIT(&PyType_Type, 0)
+ "DescriptorContainerIterator", // tp_name
+ sizeof(PyContainerIterator), // tp_basicsize
+ 0, // tp_itemsize
+ (destructor)Iterator_Dealloc, // tp_dealloc
+ 0, // tp_print
+ 0, // tp_getattr
+ 0, // tp_setattr
+ 0, // tp_compare
+ 0, // tp_repr
+ 0, // tp_as_number
+ 0, // tp_as_sequence
+ 0, // tp_as_mapping
+ 0, // tp_hash
+ 0, // tp_call
+ 0, // tp_str
+ 0, // tp_getattro
+ 0, // tp_setattro
+ 0, // tp_as_buffer
+ Py_TPFLAGS_DEFAULT, // tp_flags
+ 0, // tp_doc
+ 0, // tp_traverse
+ 0, // tp_clear
+ 0, // tp_richcompare
+ 0, // tp_weaklistoffset
+ PyObject_SelfIter, // tp_iter
+ (iternextfunc)Iterator_Next, // tp_iternext
+ 0, // tp_methods
+ 0, // tp_members
+ 0, // tp_getset
+ 0, // tp_base
+ 0, // tp_dict
+ 0, // tp_descr_get
+ 0, // tp_descr_set
+ 0, // tp_dictoffset
+ 0, // tp_init
+ 0, // tp_alloc
+ 0, // tp_new
+ 0, // tp_free
+};
+
+static PyObject* NewContainerIterator(PyContainer* container,
+ PyContainerIterator::IterKind kind) {
+ PyContainerIterator* self = PyObject_New(PyContainerIterator,
+ &ContainerIterator_Type);
+ if (self == NULL) {
+ return NULL;
+ }
+ Py_INCREF(container);
+ self->container = container;
+ self->kind = kind;
+ self->index = 0;
+
+ return reinterpret_cast<PyObject*>(self);
+}
+
+} // namespace descriptor
+
+// Now define the real collections!
+
+namespace message_descriptor {
+
+typedef const Descriptor* ParentDescriptor;
+
+static ParentDescriptor GetDescriptor(PyContainer* self) {
+ return reinterpret_cast<ParentDescriptor>(self->descriptor);
+}
+
+namespace fields {
+
+typedef const FieldDescriptor* ItemDescriptor;
+
+static int Count(PyContainer* self) {
+ return GetDescriptor(self)->field_count();
+}
+
+static ItemDescriptor GetByName(PyContainer* self, const string& name) {
+ return GetDescriptor(self)->FindFieldByName(name);
+}
+
+static ItemDescriptor GetByCamelcaseName(PyContainer* self,
+ const string& name) {
+ return GetDescriptor(self)->FindFieldByCamelcaseName(name);
+}
+
+static ItemDescriptor GetByNumber(PyContainer* self, int number) {
+ return GetDescriptor(self)->FindFieldByNumber(number);
+}
+
+static ItemDescriptor GetByIndex(PyContainer* self, int index) {
+ return GetDescriptor(self)->field(index);
+}
+
+static PyObject* NewObjectFromItem(ItemDescriptor item) {
+ return PyFieldDescriptor_FromDescriptor(item);
+}
+
+static const string& GetItemName(ItemDescriptor item) {
+ return item->name();
+}
+
+static const string& GetItemCamelcaseName(ItemDescriptor item) {
+ return item->camelcase_name();
+}
+
+static int GetItemNumber(ItemDescriptor item) {
+ return item->number();
+}
+
+static int GetItemIndex(ItemDescriptor item) {
+ return item->index();
+}
+
+static DescriptorContainerDef ContainerDef = {
+ "MessageFields",
+ (CountMethod)Count,
+ (GetByIndexMethod)GetByIndex,
+ (GetByNameMethod)GetByName,
+ (GetByCamelcaseNameMethod)GetByCamelcaseName,
+ (GetByNumberMethod)GetByNumber,
+ (NewObjectFromItemMethod)NewObjectFromItem,
+ (GetItemNameMethod)GetItemName,
+ (GetItemCamelcaseNameMethod)GetItemCamelcaseName,
+ (GetItemNumberMethod)GetItemNumber,
+ (GetItemIndexMethod)GetItemIndex,
+};
+
+} // namespace fields
+
+PyObject* NewMessageFieldsByName(ParentDescriptor descriptor) {
+ return descriptor::NewMappingByName(&fields::ContainerDef, descriptor);
+}
+
+PyObject* NewMessageFieldsByCamelcaseName(ParentDescriptor descriptor) {
+ return descriptor::NewMappingByCamelcaseName(&fields::ContainerDef,
+ descriptor);
+}
+
+PyObject* NewMessageFieldsByNumber(ParentDescriptor descriptor) {
+ return descriptor::NewMappingByNumber(&fields::ContainerDef, descriptor);
+}
+
+PyObject* NewMessageFieldsSeq(ParentDescriptor descriptor) {
+ return descriptor::NewSequence(&fields::ContainerDef, descriptor);
+}
+
+namespace nested_types {
+
+typedef const Descriptor* ItemDescriptor;
+
+static int Count(PyContainer* self) {
+ return GetDescriptor(self)->nested_type_count();
+}
+
+static ItemDescriptor GetByName(PyContainer* self, const string& name) {
+ return GetDescriptor(self)->FindNestedTypeByName(name);
+}
+
+static ItemDescriptor GetByIndex(PyContainer* self, int index) {
+ return GetDescriptor(self)->nested_type(index);
+}
+
+static PyObject* NewObjectFromItem(ItemDescriptor item) {
+ return PyMessageDescriptor_FromDescriptor(item);
+}
+
+static const string& GetItemName(ItemDescriptor item) {
+ return item->name();
+}
+
+static int GetItemIndex(ItemDescriptor item) {
+ return item->index();
+}
+
+static DescriptorContainerDef ContainerDef = {
+ "MessageNestedTypes",
+ (CountMethod)Count,
+ (GetByIndexMethod)GetByIndex,
+ (GetByNameMethod)GetByName,
+ (GetByCamelcaseNameMethod)NULL,
+ (GetByNumberMethod)NULL,
+ (NewObjectFromItemMethod)NewObjectFromItem,
+ (GetItemNameMethod)GetItemName,
+ (GetItemCamelcaseNameMethod)NULL,
+ (GetItemNumberMethod)NULL,
+ (GetItemIndexMethod)GetItemIndex,
+};
+
+} // namespace nested_types
+
+PyObject* NewMessageNestedTypesSeq(ParentDescriptor descriptor) {
+ return descriptor::NewSequence(&nested_types::ContainerDef, descriptor);
+}
+
+PyObject* NewMessageNestedTypesByName(ParentDescriptor descriptor) {
+ return descriptor::NewMappingByName(&nested_types::ContainerDef, descriptor);
+}
+
+namespace enums {
+
+typedef const EnumDescriptor* ItemDescriptor;
+
+static int Count(PyContainer* self) {
+ return GetDescriptor(self)->enum_type_count();
+}
+
+static ItemDescriptor GetByName(PyContainer* self, const string& name) {
+ return GetDescriptor(self)->FindEnumTypeByName(name);
+}
+
+static ItemDescriptor GetByIndex(PyContainer* self, int index) {
+ return GetDescriptor(self)->enum_type(index);
+}
+
+static PyObject* NewObjectFromItem(ItemDescriptor item) {
+ return PyEnumDescriptor_FromDescriptor(item);
+}
+
+static const string& GetItemName(ItemDescriptor item) {
+ return item->name();
+}
+
+static int GetItemIndex(ItemDescriptor item) {
+ return item->index();
+}
+
+static DescriptorContainerDef ContainerDef = {
+ "MessageNestedEnums",
+ (CountMethod)Count,
+ (GetByIndexMethod)GetByIndex,
+ (GetByNameMethod)GetByName,
+ (GetByCamelcaseNameMethod)NULL,
+ (GetByNumberMethod)NULL,
+ (NewObjectFromItemMethod)NewObjectFromItem,
+ (GetItemNameMethod)GetItemName,
+ (GetItemCamelcaseNameMethod)NULL,
+ (GetItemNumberMethod)NULL,
+ (GetItemIndexMethod)GetItemIndex,
+};
+
+} // namespace enums
+
+PyObject* NewMessageEnumsByName(ParentDescriptor descriptor) {
+ return descriptor::NewMappingByName(&enums::ContainerDef, descriptor);
+}
+
+PyObject* NewMessageEnumsSeq(ParentDescriptor descriptor) {
+ return descriptor::NewSequence(&enums::ContainerDef, descriptor);
+}
+
+namespace enumvalues {
+
+// This is the "enum_values_by_name" mapping, which collects values from all
+// enum types in a message.
+//
+// Note that the behavior of the C++ descriptor is different: it will search and
+// return the first value that matches the name, whereas the Python
+// implementation retrieves the last one.
+
+typedef const EnumValueDescriptor* ItemDescriptor;
+
+static int Count(PyContainer* self) {
+ int count = 0;
+ for (int i = 0; i < GetDescriptor(self)->enum_type_count(); ++i) {
+ count += GetDescriptor(self)->enum_type(i)->value_count();
+ }
+ return count;
+}
+
+static ItemDescriptor GetByName(PyContainer* self, const string& name) {
+ return GetDescriptor(self)->FindEnumValueByName(name);
+}
+
+static ItemDescriptor GetByIndex(PyContainer* self, int index) {
+ // This is not optimal, but the number of enums *types* in a given message
+ // is small. This function is only used when iterating over the mapping.
+ const EnumDescriptor* enum_type = NULL;
+ int enum_type_count = GetDescriptor(self)->enum_type_count();
+ for (int i = 0; i < enum_type_count; ++i) {
+ enum_type = GetDescriptor(self)->enum_type(i);
+ int enum_value_count = enum_type->value_count();
+ if (index < enum_value_count) {
+ // Found it!
+ break;
+ }
+ index -= enum_value_count;
+ }
+ // The next statement cannot overflow, because this function is only called by
+ // internal iterators which ensure that 0 <= index < Count().
+ return enum_type->value(index);
+}
+
+static PyObject* NewObjectFromItem(ItemDescriptor item) {
+ return PyEnumValueDescriptor_FromDescriptor(item);
+}
+
+static const string& GetItemName(ItemDescriptor item) {
+ return item->name();
+}
+
+static DescriptorContainerDef ContainerDef = {
+ "MessageEnumValues",
+ (CountMethod)Count,
+ (GetByIndexMethod)GetByIndex,
+ (GetByNameMethod)GetByName,
+ (GetByCamelcaseNameMethod)NULL,
+ (GetByNumberMethod)NULL,
+ (NewObjectFromItemMethod)NewObjectFromItem,
+ (GetItemNameMethod)GetItemName,
+ (GetItemCamelcaseNameMethod)NULL,
+ (GetItemNumberMethod)NULL,
+ (GetItemIndexMethod)NULL,
+};
+
+} // namespace enumvalues
+
+PyObject* NewMessageEnumValuesByName(ParentDescriptor descriptor) {
+ return descriptor::NewMappingByName(&enumvalues::ContainerDef, descriptor);
+}
+
+namespace extensions {
+
+typedef const FieldDescriptor* ItemDescriptor;
+
+static int Count(PyContainer* self) {
+ return GetDescriptor(self)->extension_count();
+}
+
+static ItemDescriptor GetByName(PyContainer* self, const string& name) {
+ return GetDescriptor(self)->FindExtensionByName(name);
+}
+
+static ItemDescriptor GetByIndex(PyContainer* self, int index) {
+ return GetDescriptor(self)->extension(index);
+}
+
+static PyObject* NewObjectFromItem(ItemDescriptor item) {
+ return PyFieldDescriptor_FromDescriptor(item);
+}
+
+static const string& GetItemName(ItemDescriptor item) {
+ return item->name();
+}
+
+static int GetItemIndex(ItemDescriptor item) {
+ return item->index();
+}
+
+static DescriptorContainerDef ContainerDef = {
+ "MessageExtensions",
+ (CountMethod)Count,
+ (GetByIndexMethod)GetByIndex,
+ (GetByNameMethod)GetByName,
+ (GetByCamelcaseNameMethod)NULL,
+ (GetByNumberMethod)NULL,
+ (NewObjectFromItemMethod)NewObjectFromItem,
+ (GetItemNameMethod)GetItemName,
+ (GetItemCamelcaseNameMethod)NULL,
+ (GetItemNumberMethod)NULL,
+ (GetItemIndexMethod)GetItemIndex,
+};
+
+} // namespace extensions
+
+PyObject* NewMessageExtensionsByName(ParentDescriptor descriptor) {
+ return descriptor::NewMappingByName(&extensions::ContainerDef, descriptor);
+}
+
+PyObject* NewMessageExtensionsSeq(ParentDescriptor descriptor) {
+ return descriptor::NewSequence(&extensions::ContainerDef, descriptor);
+}
+
+namespace oneofs {
+
+typedef const OneofDescriptor* ItemDescriptor;
+
+static int Count(PyContainer* self) {
+ return GetDescriptor(self)->oneof_decl_count();
+}
+
+static ItemDescriptor GetByName(PyContainer* self, const string& name) {
+ return GetDescriptor(self)->FindOneofByName(name);
+}
+
+static ItemDescriptor GetByIndex(PyContainer* self, int index) {
+ return GetDescriptor(self)->oneof_decl(index);
+}
+
+static PyObject* NewObjectFromItem(ItemDescriptor item) {
+ return PyOneofDescriptor_FromDescriptor(item);
+}
+
+static const string& GetItemName(ItemDescriptor item) {
+ return item->name();
+}
+
+static int GetItemIndex(ItemDescriptor item) {
+ return item->index();
+}
+
+static DescriptorContainerDef ContainerDef = {
+ "MessageOneofs",
+ (CountMethod)Count,
+ (GetByIndexMethod)GetByIndex,
+ (GetByNameMethod)GetByName,
+ (GetByCamelcaseNameMethod)NULL,
+ (GetByNumberMethod)NULL,
+ (NewObjectFromItemMethod)NewObjectFromItem,
+ (GetItemNameMethod)GetItemName,
+ (GetItemCamelcaseNameMethod)NULL,
+ (GetItemNumberMethod)NULL,
+ (GetItemIndexMethod)GetItemIndex,
+};
+
+} // namespace oneofs
+
+PyObject* NewMessageOneofsByName(ParentDescriptor descriptor) {
+ return descriptor::NewMappingByName(&oneofs::ContainerDef, descriptor);
+}
+
+PyObject* NewMessageOneofsSeq(ParentDescriptor descriptor) {
+ return descriptor::NewSequence(&oneofs::ContainerDef, descriptor);
+}
+
+} // namespace message_descriptor
+
+namespace enum_descriptor {
+
+typedef const EnumDescriptor* ParentDescriptor;
+
+static ParentDescriptor GetDescriptor(PyContainer* self) {
+ return reinterpret_cast<ParentDescriptor>(self->descriptor);
+}
+
+namespace enumvalues {
+
+typedef const EnumValueDescriptor* ItemDescriptor;
+
+static int Count(PyContainer* self) {
+ return GetDescriptor(self)->value_count();
+}
+
+static ItemDescriptor GetByIndex(PyContainer* self, int index) {
+ return GetDescriptor(self)->value(index);
+}
+
+static ItemDescriptor GetByName(PyContainer* self, const string& name) {
+ return GetDescriptor(self)->FindValueByName(name);
+}
+
+static ItemDescriptor GetByNumber(PyContainer* self, int number) {
+ return GetDescriptor(self)->FindValueByNumber(number);
+}
+
+static PyObject* NewObjectFromItem(ItemDescriptor item) {
+ return PyEnumValueDescriptor_FromDescriptor(item);
+}
+
+static const string& GetItemName(ItemDescriptor item) {
+ return item->name();
+}
+
+static int GetItemNumber(ItemDescriptor item) {
+ return item->number();
+}
+
+static int GetItemIndex(ItemDescriptor item) {
+ return item->index();
+}
+
+static DescriptorContainerDef ContainerDef = {
+ "EnumValues",
+ (CountMethod)Count,
+ (GetByIndexMethod)GetByIndex,
+ (GetByNameMethod)GetByName,
+ (GetByCamelcaseNameMethod)NULL,
+ (GetByNumberMethod)GetByNumber,
+ (NewObjectFromItemMethod)NewObjectFromItem,
+ (GetItemNameMethod)GetItemName,
+ (GetItemCamelcaseNameMethod)NULL,
+ (GetItemNumberMethod)GetItemNumber,
+ (GetItemIndexMethod)GetItemIndex,
+};
+
+} // namespace enumvalues
+
+PyObject* NewEnumValuesByName(ParentDescriptor descriptor) {
+ return descriptor::NewMappingByName(&enumvalues::ContainerDef, descriptor);
+}
+
+PyObject* NewEnumValuesByNumber(ParentDescriptor descriptor) {
+ return descriptor::NewMappingByNumber(&enumvalues::ContainerDef, descriptor);
+}
+
+PyObject* NewEnumValuesSeq(ParentDescriptor descriptor) {
+ return descriptor::NewSequence(&enumvalues::ContainerDef, descriptor);
+}
+
+} // namespace enum_descriptor
+
+namespace oneof_descriptor {
+
+typedef const OneofDescriptor* ParentDescriptor;
+
+static ParentDescriptor GetDescriptor(PyContainer* self) {
+ return reinterpret_cast<ParentDescriptor>(self->descriptor);
+}
+
+namespace fields {
+
+typedef const FieldDescriptor* ItemDescriptor;
+
+static int Count(PyContainer* self) {
+ return GetDescriptor(self)->field_count();
+}
+
+static ItemDescriptor GetByIndex(PyContainer* self, int index) {
+ return GetDescriptor(self)->field(index);
+}
+
+static PyObject* NewObjectFromItem(ItemDescriptor item) {
+ return PyFieldDescriptor_FromDescriptor(item);
+}
+
+static int GetItemIndex(ItemDescriptor item) {
+ return item->index_in_oneof();
+}
+
+static DescriptorContainerDef ContainerDef = {
+ "OneofFields",
+ (CountMethod)Count,
+ (GetByIndexMethod)GetByIndex,
+ (GetByNameMethod)NULL,
+ (GetByCamelcaseNameMethod)NULL,
+ (GetByNumberMethod)NULL,
+ (NewObjectFromItemMethod)NewObjectFromItem,
+ (GetItemNameMethod)NULL,
+ (GetItemCamelcaseNameMethod)NULL,
+ (GetItemNumberMethod)NULL,
+ (GetItemIndexMethod)GetItemIndex,
+};
+
+} // namespace fields
+
+PyObject* NewOneofFieldsSeq(ParentDescriptor descriptor) {
+ return descriptor::NewSequence(&fields::ContainerDef, descriptor);
+}
+
+} // namespace oneof_descriptor
+
+namespace service_descriptor {
+
+typedef const ServiceDescriptor* ParentDescriptor;
+
+static ParentDescriptor GetDescriptor(PyContainer* self) {
+ return reinterpret_cast<ParentDescriptor>(self->descriptor);
+}
+
+namespace methods {
+
+typedef const MethodDescriptor* ItemDescriptor;
+
+static int Count(PyContainer* self) {
+ return GetDescriptor(self)->method_count();
+}
+
+static ItemDescriptor GetByName(PyContainer* self, const string& name) {
+ return GetDescriptor(self)->FindMethodByName(name);
+}
+
+static ItemDescriptor GetByIndex(PyContainer* self, int index) {
+ return GetDescriptor(self)->method(index);
+}
+
+static PyObject* NewObjectFromItem(ItemDescriptor item) {
+ return PyMethodDescriptor_FromDescriptor(item);
+}
+
+static const string& GetItemName(ItemDescriptor item) {
+ return item->name();
+}
+
+static int GetItemIndex(ItemDescriptor item) {
+ return item->index();
+}
+
+static DescriptorContainerDef ContainerDef = {
+ "ServiceMethods",
+ (CountMethod)Count,
+ (GetByIndexMethod)GetByIndex,
+ (GetByNameMethod)GetByName,
+ (GetByCamelcaseNameMethod)NULL,
+ (GetByNumberMethod)NULL,
+ (NewObjectFromItemMethod)NewObjectFromItem,
+ (GetItemNameMethod)GetItemName,
+ (GetItemCamelcaseNameMethod)NULL,
+ (GetItemNumberMethod)NULL,
+ (GetItemIndexMethod)GetItemIndex,
+};
+
+} // namespace methods
+
+PyObject* NewServiceMethodsSeq(ParentDescriptor descriptor) {
+ return descriptor::NewSequence(&methods::ContainerDef, descriptor);
+}
+
+PyObject* NewServiceMethodsByName(ParentDescriptor descriptor) {
+ return descriptor::NewMappingByName(&methods::ContainerDef, descriptor);
+}
+
+} // namespace service_descriptor
+
+namespace file_descriptor {
+
+typedef const FileDescriptor* ParentDescriptor;
+
+static ParentDescriptor GetDescriptor(PyContainer* self) {
+ return reinterpret_cast<ParentDescriptor>(self->descriptor);
+}
+
+namespace messages {
+
+typedef const Descriptor* ItemDescriptor;
+
+static int Count(PyContainer* self) {
+ return GetDescriptor(self)->message_type_count();
+}
+
+static ItemDescriptor GetByName(PyContainer* self, const string& name) {
+ return GetDescriptor(self)->FindMessageTypeByName(name);
+}
+
+static ItemDescriptor GetByIndex(PyContainer* self, int index) {
+ return GetDescriptor(self)->message_type(index);
+}
+
+static PyObject* NewObjectFromItem(ItemDescriptor item) {
+ return PyMessageDescriptor_FromDescriptor(item);
+}
+
+static const string& GetItemName(ItemDescriptor item) {
+ return item->name();
+}
+
+static int GetItemIndex(ItemDescriptor item) {
+ return item->index();
+}
+
+static DescriptorContainerDef ContainerDef = {
+ "FileMessages",
+ (CountMethod)Count,
+ (GetByIndexMethod)GetByIndex,
+ (GetByNameMethod)GetByName,
+ (GetByCamelcaseNameMethod)NULL,
+ (GetByNumberMethod)NULL,
+ (NewObjectFromItemMethod)NewObjectFromItem,
+ (GetItemNameMethod)GetItemName,
+ (GetItemCamelcaseNameMethod)NULL,
+ (GetItemNumberMethod)NULL,
+ (GetItemIndexMethod)GetItemIndex,
+};
+
+} // namespace messages
+
+PyObject* NewFileMessageTypesByName(ParentDescriptor descriptor) {
+ return descriptor::NewMappingByName(&messages::ContainerDef, descriptor);
+}
+
+namespace enums {
+
+typedef const EnumDescriptor* ItemDescriptor;
+
+static int Count(PyContainer* self) {
+ return GetDescriptor(self)->enum_type_count();
+}
+
+static ItemDescriptor GetByName(PyContainer* self, const string& name) {
+ return GetDescriptor(self)->FindEnumTypeByName(name);
+}
+
+static ItemDescriptor GetByIndex(PyContainer* self, int index) {
+ return GetDescriptor(self)->enum_type(index);
+}
+
+static PyObject* NewObjectFromItem(ItemDescriptor item) {
+ return PyEnumDescriptor_FromDescriptor(item);
+}
+
+static const string& GetItemName(ItemDescriptor item) {
+ return item->name();
+}
+
+static int GetItemIndex(ItemDescriptor item) {
+ return item->index();
+}
+
+static DescriptorContainerDef ContainerDef = {
+ "FileEnums",
+ (CountMethod)Count,
+ (GetByIndexMethod)GetByIndex,
+ (GetByNameMethod)GetByName,
+ (GetByCamelcaseNameMethod)NULL,
+ (GetByNumberMethod)NULL,
+ (NewObjectFromItemMethod)NewObjectFromItem,
+ (GetItemNameMethod)GetItemName,
+ (GetItemCamelcaseNameMethod)NULL,
+ (GetItemNumberMethod)NULL,
+ (GetItemIndexMethod)GetItemIndex,
+};
+
+} // namespace enums
+
+PyObject* NewFileEnumTypesByName(ParentDescriptor descriptor) {
+ return descriptor::NewMappingByName(&enums::ContainerDef, descriptor);
+}
+
+namespace extensions {
+
+typedef const FieldDescriptor* ItemDescriptor;
+
+static int Count(PyContainer* self) {
+ return GetDescriptor(self)->extension_count();
+}
+
+static ItemDescriptor GetByName(PyContainer* self, const string& name) {
+ return GetDescriptor(self)->FindExtensionByName(name);
+}
+
+static ItemDescriptor GetByIndex(PyContainer* self, int index) {
+ return GetDescriptor(self)->extension(index);
+}
+
+static PyObject* NewObjectFromItem(ItemDescriptor item) {
+ return PyFieldDescriptor_FromDescriptor(item);
+}
+
+static const string& GetItemName(ItemDescriptor item) {
+ return item->name();
+}
+
+static int GetItemIndex(ItemDescriptor item) {
+ return item->index();
+}
+
+static DescriptorContainerDef ContainerDef = {
+ "FileExtensions",
+ (CountMethod)Count,
+ (GetByIndexMethod)GetByIndex,
+ (GetByNameMethod)GetByName,
+ (GetByCamelcaseNameMethod)NULL,
+ (GetByNumberMethod)NULL,
+ (NewObjectFromItemMethod)NewObjectFromItem,
+ (GetItemNameMethod)GetItemName,
+ (GetItemCamelcaseNameMethod)NULL,
+ (GetItemNumberMethod)NULL,
+ (GetItemIndexMethod)GetItemIndex,
+};
+
+} // namespace extensions
+
+PyObject* NewFileExtensionsByName(ParentDescriptor descriptor) {
+ return descriptor::NewMappingByName(&extensions::ContainerDef, descriptor);
+}
+
+namespace services {
+
+typedef const ServiceDescriptor* ItemDescriptor;
+
+static int Count(PyContainer* self) {
+ return GetDescriptor(self)->service_count();
+}
+
+static ItemDescriptor GetByName(PyContainer* self, const string& name) {
+ return GetDescriptor(self)->FindServiceByName(name);
+}
+
+static ItemDescriptor GetByIndex(PyContainer* self, int index) {
+ return GetDescriptor(self)->service(index);
+}
+
+static PyObject* NewObjectFromItem(ItemDescriptor item) {
+ return PyServiceDescriptor_FromDescriptor(item);
+}
+
+static const string& GetItemName(ItemDescriptor item) {
+ return item->name();
+}
+
+static int GetItemIndex(ItemDescriptor item) {
+ return item->index();
+}
+
+static DescriptorContainerDef ContainerDef = {
+ "FileServices",
+ (CountMethod)Count,
+ (GetByIndexMethod)GetByIndex,
+ (GetByNameMethod)GetByName,
+ (GetByCamelcaseNameMethod)NULL,
+ (GetByNumberMethod)NULL,
+ (NewObjectFromItemMethod)NewObjectFromItem,
+ (GetItemNameMethod)GetItemName,
+ (GetItemCamelcaseNameMethod)NULL,
+ (GetItemNumberMethod)NULL,
+ (GetItemIndexMethod)GetItemIndex,
+};
+
+} // namespace services
+
+PyObject* NewFileServicesByName(const FileDescriptor* descriptor) {
+ return descriptor::NewMappingByName(&services::ContainerDef, descriptor);
+}
+
+namespace dependencies {
+
+typedef const FileDescriptor* ItemDescriptor;
+
+static int Count(PyContainer* self) {
+ return GetDescriptor(self)->dependency_count();
+}
+
+static ItemDescriptor GetByIndex(PyContainer* self, int index) {
+ return GetDescriptor(self)->dependency(index);
+}
+
+static PyObject* NewObjectFromItem(ItemDescriptor item) {
+ return PyFileDescriptor_FromDescriptor(item);
+}
+
+static DescriptorContainerDef ContainerDef = {
+ "FileDependencies",
+ (CountMethod)Count,
+ (GetByIndexMethod)GetByIndex,
+ (GetByNameMethod)NULL,
+ (GetByCamelcaseNameMethod)NULL,
+ (GetByNumberMethod)NULL,
+ (NewObjectFromItemMethod)NewObjectFromItem,
+ (GetItemNameMethod)NULL,
+ (GetItemCamelcaseNameMethod)NULL,
+ (GetItemNumberMethod)NULL,
+ (GetItemIndexMethod)NULL,
+};
+
+} // namespace dependencies
+
+PyObject* NewFileDependencies(const FileDescriptor* descriptor) {
+ return descriptor::NewSequence(&dependencies::ContainerDef, descriptor);
+}
+
+namespace public_dependencies {
+
+typedef const FileDescriptor* ItemDescriptor;
+
+static int Count(PyContainer* self) {
+ return GetDescriptor(self)->public_dependency_count();
+}
+
+static ItemDescriptor GetByIndex(PyContainer* self, int index) {
+ return GetDescriptor(self)->public_dependency(index);
+}
+
+static PyObject* NewObjectFromItem(ItemDescriptor item) {
+ return PyFileDescriptor_FromDescriptor(item);
+}
+
+static DescriptorContainerDef ContainerDef = {
+ "FilePublicDependencies",
+ (CountMethod)Count,
+ (GetByIndexMethod)GetByIndex,
+ (GetByNameMethod)NULL,
+ (GetByCamelcaseNameMethod)NULL,
+ (GetByNumberMethod)NULL,
+ (NewObjectFromItemMethod)NewObjectFromItem,
+ (GetItemNameMethod)NULL,
+ (GetItemCamelcaseNameMethod)NULL,
+ (GetItemNumberMethod)NULL,
+ (GetItemIndexMethod)NULL,
+};
+
+} // namespace public_dependencies
+
+PyObject* NewFilePublicDependencies(const FileDescriptor* descriptor) {
+ return descriptor::NewSequence(&public_dependencies::ContainerDef,
+ descriptor);
+}
+
+} // namespace file_descriptor
+
+
+// Register all implementations
+
+bool InitDescriptorMappingTypes() {
+ if (PyType_Ready(&descriptor::DescriptorMapping_Type) < 0)
+ return false;
+ if (PyType_Ready(&descriptor::DescriptorSequence_Type) < 0)
+ return false;
+ if (PyType_Ready(&descriptor::ContainerIterator_Type) < 0)
+ return false;
+ return true;
+}
+
+} // namespace python
+} // namespace protobuf
+} // namespace google
diff --git a/python/google/protobuf/pyext/descriptor_containers.h b/python/google/protobuf/pyext/descriptor_containers.h
new file mode 100644
index 00000000..83de07b6
--- /dev/null
+++ b/python/google/protobuf/pyext/descriptor_containers.h
@@ -0,0 +1,109 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef GOOGLE_PROTOBUF_PYTHON_CPP_DESCRIPTOR_CONTAINERS_H__
+#define GOOGLE_PROTOBUF_PYTHON_CPP_DESCRIPTOR_CONTAINERS_H__
+
+// Mappings and Sequences of descriptors.
+// They implement containers like fields_by_name, EnumDescriptor.values...
+// See descriptor_containers.cc for more description.
+#include <Python.h>
+
+namespace google {
+namespace protobuf {
+
+class Descriptor;
+class FileDescriptor;
+class EnumDescriptor;
+class OneofDescriptor;
+class ServiceDescriptor;
+
+namespace python {
+
+// Initialize the various types and objects.
+bool InitDescriptorMappingTypes();
+
+// Each function below returns a Mapping, or a Sequence of descriptors.
+// They all return a new reference.
+
+namespace message_descriptor {
+PyObject* NewMessageFieldsByName(const Descriptor* descriptor);
+PyObject* NewMessageFieldsByCamelcaseName(const Descriptor* descriptor);
+PyObject* NewMessageFieldsByNumber(const Descriptor* descriptor);
+PyObject* NewMessageFieldsSeq(const Descriptor* descriptor);
+
+PyObject* NewMessageNestedTypesSeq(const Descriptor* descriptor);
+PyObject* NewMessageNestedTypesByName(const Descriptor* descriptor);
+
+PyObject* NewMessageEnumsByName(const Descriptor* descriptor);
+PyObject* NewMessageEnumsSeq(const Descriptor* descriptor);
+PyObject* NewMessageEnumValuesByName(const Descriptor* descriptor);
+
+PyObject* NewMessageExtensionsByName(const Descriptor* descriptor);
+PyObject* NewMessageExtensionsSeq(const Descriptor* descriptor);
+
+PyObject* NewMessageOneofsByName(const Descriptor* descriptor);
+PyObject* NewMessageOneofsSeq(const Descriptor* descriptor);
+} // namespace message_descriptor
+
+namespace enum_descriptor {
+PyObject* NewEnumValuesByName(const EnumDescriptor* descriptor);
+PyObject* NewEnumValuesByNumber(const EnumDescriptor* descriptor);
+PyObject* NewEnumValuesSeq(const EnumDescriptor* descriptor);
+} // namespace enum_descriptor
+
+namespace oneof_descriptor {
+PyObject* NewOneofFieldsSeq(const OneofDescriptor* descriptor);
+} // namespace oneof_descriptor
+
+namespace file_descriptor {
+PyObject* NewFileMessageTypesByName(const FileDescriptor* descriptor);
+
+PyObject* NewFileEnumTypesByName(const FileDescriptor* descriptor);
+
+PyObject* NewFileExtensionsByName(const FileDescriptor* descriptor);
+
+PyObject* NewFileServicesByName(const FileDescriptor* descriptor);
+
+PyObject* NewFileDependencies(const FileDescriptor* descriptor);
+PyObject* NewFilePublicDependencies(const FileDescriptor* descriptor);
+} // namespace file_descriptor
+
+namespace service_descriptor {
+PyObject* NewServiceMethodsSeq(const ServiceDescriptor* descriptor);
+PyObject* NewServiceMethodsByName(const ServiceDescriptor* descriptor);
+} // namespace service_descriptor
+
+
+} // namespace python
+} // namespace protobuf
+
+} // namespace google
+#endif // GOOGLE_PROTOBUF_PYTHON_CPP_DESCRIPTOR_CONTAINERS_H__
diff --git a/python/google/protobuf/pyext/descriptor_database.cc b/python/google/protobuf/pyext/descriptor_database.cc
new file mode 100644
index 00000000..daa40cc7
--- /dev/null
+++ b/python/google/protobuf/pyext/descriptor_database.cc
@@ -0,0 +1,148 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// This file defines a C++ DescriptorDatabase, which wraps a Python Database
+// and delegate all its operations to Python methods.
+
+#include <google/protobuf/pyext/descriptor_database.h>
+
+#include <google/protobuf/stubs/logging.h>
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/descriptor.pb.h>
+#include <google/protobuf/pyext/message.h>
+#include <google/protobuf/pyext/scoped_pyobject_ptr.h>
+
+namespace google {
+namespace protobuf {
+namespace python {
+
+PyDescriptorDatabase::PyDescriptorDatabase(PyObject* py_database)
+ : py_database_(py_database) {
+ Py_INCREF(py_database_);
+}
+
+PyDescriptorDatabase::~PyDescriptorDatabase() { Py_DECREF(py_database_); }
+
+// Convert a Python object to a FileDescriptorProto pointer.
+// Handles all kinds of Python errors, which are simply logged.
+static bool GetFileDescriptorProto(PyObject* py_descriptor,
+ FileDescriptorProto* output) {
+ if (py_descriptor == NULL) {
+ if (PyErr_ExceptionMatches(PyExc_KeyError)) {
+ // Expected error: item was simply not found.
+ PyErr_Clear();
+ } else {
+ GOOGLE_LOG(ERROR) << "DescriptorDatabase method raised an error";
+ PyErr_Print();
+ }
+ return false;
+ }
+ if (py_descriptor == Py_None) {
+ return false;
+ }
+ const Descriptor* filedescriptor_descriptor =
+ FileDescriptorProto::default_instance().GetDescriptor();
+ CMessage* message = reinterpret_cast<CMessage*>(py_descriptor);
+ if (PyObject_TypeCheck(py_descriptor, &CMessage_Type) &&
+ message->message->GetDescriptor() == filedescriptor_descriptor) {
+ // Fast path: Just use the pointer.
+ FileDescriptorProto* file_proto =
+ static_cast<FileDescriptorProto*>(message->message);
+ *output = *file_proto;
+ return true;
+ } else {
+ // Slow path: serialize the message. This allows to use databases which
+ // use a different implementation of FileDescriptorProto.
+ ScopedPyObjectPtr serialized_pb(
+ PyObject_CallMethod(py_descriptor, "SerializeToString", NULL));
+ if (serialized_pb == NULL) {
+ GOOGLE_LOG(ERROR)
+ << "DescriptorDatabase method did not return a FileDescriptorProto";
+ PyErr_Print();
+ return false;
+ }
+ char* str;
+ Py_ssize_t len;
+ if (PyBytes_AsStringAndSize(serialized_pb.get(), &str, &len) < 0) {
+ GOOGLE_LOG(ERROR)
+ << "DescriptorDatabase method did not return a FileDescriptorProto";
+ PyErr_Print();
+ return false;
+ }
+ FileDescriptorProto file_proto;
+ if (!file_proto.ParseFromArray(str, len)) {
+ GOOGLE_LOG(ERROR)
+ << "DescriptorDatabase method did not return a FileDescriptorProto";
+ return false;
+ }
+ *output = file_proto;
+ return true;
+ }
+}
+
+// Find a file by file name.
+bool PyDescriptorDatabase::FindFileByName(const string& filename,
+ FileDescriptorProto* output) {
+ ScopedPyObjectPtr py_descriptor(PyObject_CallMethod(
+ py_database_, "FindFileByName", "s#", filename.c_str(), filename.size()));
+ return GetFileDescriptorProto(py_descriptor.get(), output);
+}
+
+// Find the file that declares the given fully-qualified symbol name.
+bool PyDescriptorDatabase::FindFileContainingSymbol(
+ const string& symbol_name, FileDescriptorProto* output) {
+ ScopedPyObjectPtr py_descriptor(
+ PyObject_CallMethod(py_database_, "FindFileContainingSymbol", "s#",
+ symbol_name.c_str(), symbol_name.size()));
+ return GetFileDescriptorProto(py_descriptor.get(), output);
+}
+
+// Find the file which defines an extension extending the given message type
+// with the given field number.
+// Python DescriptorDatabases are not required to implement this method.
+bool PyDescriptorDatabase::FindFileContainingExtension(
+ const string& containing_type, int field_number,
+ FileDescriptorProto* output) {
+ ScopedPyObjectPtr py_method(
+ PyObject_GetAttrString(py_database_, "FindFileContainingExtension"));
+ if (py_method == NULL) {
+ // This method is not implemented, returns without error.
+ PyErr_Clear();
+ return false;
+ }
+ ScopedPyObjectPtr py_descriptor(
+ PyObject_CallFunction(py_method.get(), "s#i", containing_type.c_str(),
+ containing_type.size(), field_number));
+ return GetFileDescriptorProto(py_descriptor.get(), output);
+}
+
+} // namespace python
+} // namespace protobuf
+} // namespace google
diff --git a/python/google/protobuf/pyext/descriptor_database.h b/python/google/protobuf/pyext/descriptor_database.h
new file mode 100644
index 00000000..fc71c4bc
--- /dev/null
+++ b/python/google/protobuf/pyext/descriptor_database.h
@@ -0,0 +1,75 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef GOOGLE_PROTOBUF_PYTHON_CPP_DESCRIPTOR_DATABASE_H__
+#define GOOGLE_PROTOBUF_PYTHON_CPP_DESCRIPTOR_DATABASE_H__
+
+#include <Python.h>
+
+#include <google/protobuf/descriptor_database.h>
+
+namespace google {
+namespace protobuf {
+namespace python {
+
+class PyDescriptorDatabase : public DescriptorDatabase {
+ public:
+ explicit PyDescriptorDatabase(PyObject* py_database);
+ ~PyDescriptorDatabase();
+
+ // Implement the abstract interface. All these functions fill the output
+ // with a copy of FileDescriptorProto.
+
+ // Find a file by file name.
+ bool FindFileByName(const string& filename,
+ FileDescriptorProto* output);
+
+ // Find the file that declares the given fully-qualified symbol name.
+ bool FindFileContainingSymbol(const string& symbol_name,
+ FileDescriptorProto* output);
+
+ // Find the file which defines an extension extending the given message type
+ // with the given field number.
+ // Containing_type must be a fully-qualified type name.
+ // Python objects are not required to implement this method.
+ bool FindFileContainingExtension(const string& containing_type,
+ int field_number,
+ FileDescriptorProto* output);
+
+ private:
+ // The python object that implements the database. The reference is owned.
+ PyObject* py_database_;
+};
+
+} // namespace python
+} // namespace protobuf
+
+} // namespace google
+#endif // GOOGLE_PROTOBUF_PYTHON_CPP_DESCRIPTOR_DATABASE_H__
diff --git a/python/google/protobuf/pyext/descriptor_pool.cc b/python/google/protobuf/pyext/descriptor_pool.cc
new file mode 100644
index 00000000..cfd98690
--- /dev/null
+++ b/python/google/protobuf/pyext/descriptor_pool.cc
@@ -0,0 +1,631 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Implements the DescriptorPool, which collects all descriptors.
+
+#include <Python.h>
+
+#include <google/protobuf/descriptor.pb.h>
+#include <google/protobuf/dynamic_message.h>
+#include <google/protobuf/pyext/descriptor.h>
+#include <google/protobuf/pyext/descriptor_database.h>
+#include <google/protobuf/pyext/descriptor_pool.h>
+#include <google/protobuf/pyext/message.h>
+#include <google/protobuf/pyext/scoped_pyobject_ptr.h>
+
+#if PY_MAJOR_VERSION >= 3
+ #define PyString_FromStringAndSize PyUnicode_FromStringAndSize
+ #if PY_VERSION_HEX < 0x03030000
+ #error "Python 3.0 - 3.2 are not supported."
+ #endif
+ #define PyString_AsStringAndSize(ob, charpp, sizep) \
+ (PyUnicode_Check(ob)? \
+ ((*(charpp) = PyUnicode_AsUTF8AndSize(ob, (sizep))) == NULL? -1: 0): \
+ PyBytes_AsStringAndSize(ob, (charpp), (sizep)))
+#endif
+
+namespace google {
+namespace protobuf {
+namespace python {
+
+// A map to cache Python Pools per C++ pointer.
+// Pointers are not owned here, and belong to the PyDescriptorPool.
+static hash_map<const DescriptorPool*, PyDescriptorPool*> descriptor_pool_map;
+
+namespace cdescriptor_pool {
+
+// Create a Python DescriptorPool object, but does not fill the "pool"
+// attribute.
+static PyDescriptorPool* _CreateDescriptorPool() {
+ PyDescriptorPool* cpool = PyObject_New(
+ PyDescriptorPool, &PyDescriptorPool_Type);
+ if (cpool == NULL) {
+ return NULL;
+ }
+
+ cpool->underlay = NULL;
+ cpool->database = NULL;
+
+ DynamicMessageFactory* message_factory = new DynamicMessageFactory();
+ // This option might be the default some day.
+ message_factory->SetDelegateToGeneratedFactory(true);
+ cpool->message_factory = message_factory;
+
+ // TODO(amauryfa): Rewrite the SymbolDatabase in C so that it uses the same
+ // storage.
+ cpool->classes_by_descriptor =
+ new PyDescriptorPool::ClassesByMessageMap();
+ cpool->descriptor_options =
+ new hash_map<const void*, PyObject *>();
+
+ return cpool;
+}
+
+// Create a Python DescriptorPool, using the given pool as an underlay:
+// new messages will be added to a custom pool, not to the underlay.
+//
+// Ownership of the underlay is not transferred, its pointer should
+// stay alive.
+static PyDescriptorPool* PyDescriptorPool_NewWithUnderlay(
+ const DescriptorPool* underlay) {
+ PyDescriptorPool* cpool = _CreateDescriptorPool();
+ if (cpool == NULL) {
+ return NULL;
+ }
+ cpool->pool = new DescriptorPool(underlay);
+ cpool->underlay = underlay;
+
+ if (!descriptor_pool_map.insert(
+ std::make_pair(cpool->pool, cpool)).second) {
+ // Should never happen -- would indicate an internal error / bug.
+ PyErr_SetString(PyExc_ValueError, "DescriptorPool already registered");
+ return NULL;
+ }
+
+ return cpool;
+}
+
+static PyDescriptorPool* PyDescriptorPool_NewWithDatabase(
+ DescriptorDatabase* database) {
+ PyDescriptorPool* cpool = _CreateDescriptorPool();
+ if (cpool == NULL) {
+ return NULL;
+ }
+ if (database != NULL) {
+ cpool->pool = new DescriptorPool(database);
+ cpool->database = database;
+ } else {
+ cpool->pool = new DescriptorPool();
+ }
+
+ if (!descriptor_pool_map.insert(std::make_pair(cpool->pool, cpool)).second) {
+ // Should never happen -- would indicate an internal error / bug.
+ PyErr_SetString(PyExc_ValueError, "DescriptorPool already registered");
+ return NULL;
+ }
+
+ return cpool;
+}
+
+// The public DescriptorPool constructor.
+static PyObject* New(PyTypeObject* type,
+ PyObject* args, PyObject* kwargs) {
+ static char* kwlist[] = {"descriptor_db", 0};
+ PyObject* py_database = NULL;
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O", kwlist, &py_database)) {
+ return NULL;
+ }
+ DescriptorDatabase* database = NULL;
+ if (py_database && py_database != Py_None) {
+ database = new PyDescriptorDatabase(py_database);
+ }
+ return reinterpret_cast<PyObject*>(
+ PyDescriptorPool_NewWithDatabase(database));
+}
+
+static void Dealloc(PyDescriptorPool* self) {
+ typedef PyDescriptorPool::ClassesByMessageMap::iterator iterator;
+ descriptor_pool_map.erase(self->pool);
+ for (iterator it = self->classes_by_descriptor->begin();
+ it != self->classes_by_descriptor->end(); ++it) {
+ Py_DECREF(it->second);
+ }
+ delete self->classes_by_descriptor;
+ for (hash_map<const void*, PyObject*>::iterator it =
+ self->descriptor_options->begin();
+ it != self->descriptor_options->end(); ++it) {
+ Py_DECREF(it->second);
+ }
+ delete self->descriptor_options;
+ delete self->message_factory;
+ delete self->database;
+ delete self->pool;
+ Py_TYPE(self)->tp_free(reinterpret_cast<PyObject*>(self));
+}
+
+PyObject* FindMessageByName(PyDescriptorPool* self, PyObject* arg) {
+ Py_ssize_t name_size;
+ char* name;
+ if (PyString_AsStringAndSize(arg, &name, &name_size) < 0) {
+ return NULL;
+ }
+
+ const Descriptor* message_descriptor =
+ self->pool->FindMessageTypeByName(string(name, name_size));
+
+ if (message_descriptor == NULL) {
+ PyErr_Format(PyExc_KeyError, "Couldn't find message %.200s", name);
+ return NULL;
+ }
+
+ return PyMessageDescriptor_FromDescriptor(message_descriptor);
+}
+
+// Add a message class to our database.
+int RegisterMessageClass(PyDescriptorPool* self,
+ const Descriptor* message_descriptor,
+ CMessageClass* message_class) {
+ Py_INCREF(message_class);
+ typedef PyDescriptorPool::ClassesByMessageMap::iterator iterator;
+ std::pair<iterator, bool> ret = self->classes_by_descriptor->insert(
+ std::make_pair(message_descriptor, message_class));
+ if (!ret.second) {
+ // Update case: DECREF the previous value.
+ Py_DECREF(ret.first->second);
+ ret.first->second = message_class;
+ }
+ return 0;
+}
+
+// Retrieve the message class added to our database.
+CMessageClass* GetMessageClass(PyDescriptorPool* self,
+ const Descriptor* message_descriptor) {
+ typedef PyDescriptorPool::ClassesByMessageMap::iterator iterator;
+ iterator ret = self->classes_by_descriptor->find(message_descriptor);
+ if (ret == self->classes_by_descriptor->end()) {
+ PyErr_Format(PyExc_TypeError, "No message class registered for '%s'",
+ message_descriptor->full_name().c_str());
+ return NULL;
+ } else {
+ return ret->second;
+ }
+}
+
+PyObject* FindFileByName(PyDescriptorPool* self, PyObject* arg) {
+ Py_ssize_t name_size;
+ char* name;
+ if (PyString_AsStringAndSize(arg, &name, &name_size) < 0) {
+ return NULL;
+ }
+
+ const FileDescriptor* file_descriptor =
+ self->pool->FindFileByName(string(name, name_size));
+ if (file_descriptor == NULL) {
+ PyErr_Format(PyExc_KeyError, "Couldn't find file %.200s",
+ name);
+ return NULL;
+ }
+
+ return PyFileDescriptor_FromDescriptor(file_descriptor);
+}
+
+PyObject* FindFieldByName(PyDescriptorPool* self, PyObject* arg) {
+ Py_ssize_t name_size;
+ char* name;
+ if (PyString_AsStringAndSize(arg, &name, &name_size) < 0) {
+ return NULL;
+ }
+
+ const FieldDescriptor* field_descriptor =
+ self->pool->FindFieldByName(string(name, name_size));
+ if (field_descriptor == NULL) {
+ PyErr_Format(PyExc_KeyError, "Couldn't find field %.200s",
+ name);
+ return NULL;
+ }
+
+ return PyFieldDescriptor_FromDescriptor(field_descriptor);
+}
+
+PyObject* FindExtensionByName(PyDescriptorPool* self, PyObject* arg) {
+ Py_ssize_t name_size;
+ char* name;
+ if (PyString_AsStringAndSize(arg, &name, &name_size) < 0) {
+ return NULL;
+ }
+
+ const FieldDescriptor* field_descriptor =
+ self->pool->FindExtensionByName(string(name, name_size));
+ if (field_descriptor == NULL) {
+ PyErr_Format(PyExc_KeyError, "Couldn't find extension field %.200s", name);
+ return NULL;
+ }
+
+ return PyFieldDescriptor_FromDescriptor(field_descriptor);
+}
+
+PyObject* FindEnumTypeByName(PyDescriptorPool* self, PyObject* arg) {
+ Py_ssize_t name_size;
+ char* name;
+ if (PyString_AsStringAndSize(arg, &name, &name_size) < 0) {
+ return NULL;
+ }
+
+ const EnumDescriptor* enum_descriptor =
+ self->pool->FindEnumTypeByName(string(name, name_size));
+ if (enum_descriptor == NULL) {
+ PyErr_Format(PyExc_KeyError, "Couldn't find enum %.200s", name);
+ return NULL;
+ }
+
+ return PyEnumDescriptor_FromDescriptor(enum_descriptor);
+}
+
+PyObject* FindOneofByName(PyDescriptorPool* self, PyObject* arg) {
+ Py_ssize_t name_size;
+ char* name;
+ if (PyString_AsStringAndSize(arg, &name, &name_size) < 0) {
+ return NULL;
+ }
+
+ const OneofDescriptor* oneof_descriptor =
+ self->pool->FindOneofByName(string(name, name_size));
+ if (oneof_descriptor == NULL) {
+ PyErr_Format(PyExc_KeyError, "Couldn't find oneof %.200s", name);
+ return NULL;
+ }
+
+ return PyOneofDescriptor_FromDescriptor(oneof_descriptor);
+}
+
+PyObject* FindServiceByName(PyDescriptorPool* self, PyObject* arg) {
+ Py_ssize_t name_size;
+ char* name;
+ if (PyString_AsStringAndSize(arg, &name, &name_size) < 0) {
+ return NULL;
+ }
+
+ const ServiceDescriptor* service_descriptor =
+ self->pool->FindServiceByName(string(name, name_size));
+ if (service_descriptor == NULL) {
+ PyErr_Format(PyExc_KeyError, "Couldn't find service %.200s", name);
+ return NULL;
+ }
+
+ return PyServiceDescriptor_FromDescriptor(service_descriptor);
+}
+
+PyObject* FindMethodByName(PyDescriptorPool* self, PyObject* arg) {
+ Py_ssize_t name_size;
+ char* name;
+ if (PyString_AsStringAndSize(arg, &name, &name_size) < 0) {
+ return NULL;
+ }
+
+ const MethodDescriptor* method_descriptor =
+ self->pool->FindMethodByName(string(name, name_size));
+ if (method_descriptor == NULL) {
+ PyErr_Format(PyExc_KeyError, "Couldn't find method %.200s", name);
+ return NULL;
+ }
+
+ return PyMethodDescriptor_FromDescriptor(method_descriptor);
+}
+
+PyObject* FindFileContainingSymbol(PyDescriptorPool* self, PyObject* arg) {
+ Py_ssize_t name_size;
+ char* name;
+ if (PyString_AsStringAndSize(arg, &name, &name_size) < 0) {
+ return NULL;
+ }
+
+ const FileDescriptor* file_descriptor =
+ self->pool->FindFileContainingSymbol(string(name, name_size));
+ if (file_descriptor == NULL) {
+ PyErr_Format(PyExc_KeyError, "Couldn't find symbol %.200s", name);
+ return NULL;
+ }
+
+ return PyFileDescriptor_FromDescriptor(file_descriptor);
+}
+
+// These functions should not exist -- the only valid way to create
+// descriptors is to call Add() or AddSerializedFile().
+// But these AddDescriptor() functions were created in Python and some people
+// call them, so we support them for now for compatibility.
+// However we do check that the existing descriptor already exists in the pool,
+// which appears to always be true for existing calls -- but then why do people
+// call a function that will just be a no-op?
+// TODO(amauryfa): Need to investigate further.
+
+PyObject* AddFileDescriptor(PyDescriptorPool* self, PyObject* descriptor) {
+ const FileDescriptor* file_descriptor =
+ PyFileDescriptor_AsDescriptor(descriptor);
+ if (!file_descriptor) {
+ return NULL;
+ }
+ if (file_descriptor !=
+ self->pool->FindFileByName(file_descriptor->name())) {
+ PyErr_Format(PyExc_ValueError,
+ "The file descriptor %s does not belong to this pool",
+ file_descriptor->name().c_str());
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
+PyObject* AddDescriptor(PyDescriptorPool* self, PyObject* descriptor) {
+ const Descriptor* message_descriptor =
+ PyMessageDescriptor_AsDescriptor(descriptor);
+ if (!message_descriptor) {
+ return NULL;
+ }
+ if (message_descriptor !=
+ self->pool->FindMessageTypeByName(message_descriptor->full_name())) {
+ PyErr_Format(PyExc_ValueError,
+ "The message descriptor %s does not belong to this pool",
+ message_descriptor->full_name().c_str());
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
+PyObject* AddEnumDescriptor(PyDescriptorPool* self, PyObject* descriptor) {
+ const EnumDescriptor* enum_descriptor =
+ PyEnumDescriptor_AsDescriptor(descriptor);
+ if (!enum_descriptor) {
+ return NULL;
+ }
+ if (enum_descriptor !=
+ self->pool->FindEnumTypeByName(enum_descriptor->full_name())) {
+ PyErr_Format(PyExc_ValueError,
+ "The enum descriptor %s does not belong to this pool",
+ enum_descriptor->full_name().c_str());
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
+// The code below loads new Descriptors from a serialized FileDescriptorProto.
+
+
+// Collects errors that occur during proto file building to allow them to be
+// propagated in the python exception instead of only living in ERROR logs.
+class BuildFileErrorCollector : public DescriptorPool::ErrorCollector {
+ public:
+ BuildFileErrorCollector() : error_message(""), had_errors(false) {}
+
+ void AddError(const string& filename, const string& element_name,
+ const Message* descriptor, ErrorLocation location,
+ const string& message) {
+ // Replicates the logging behavior that happens in the C++ implementation
+ // when an error collector is not passed in.
+ if (!had_errors) {
+ error_message +=
+ ("Invalid proto descriptor for file \"" + filename + "\":\n");
+ had_errors = true;
+ }
+ // As this only happens on failure and will result in the program not
+ // running at all, no effort is made to optimize this string manipulation.
+ error_message += (" " + element_name + ": " + message + "\n");
+ }
+
+ string error_message;
+ bool had_errors;
+};
+
+PyObject* AddSerializedFile(PyDescriptorPool* self, PyObject* serialized_pb) {
+ char* message_type;
+ Py_ssize_t message_len;
+
+ if (self->database != NULL) {
+ PyErr_SetString(
+ PyExc_ValueError,
+ "Cannot call Add on a DescriptorPool that uses a DescriptorDatabase. "
+ "Add your file to the underlying database.");
+ return NULL;
+ }
+
+ if (PyBytes_AsStringAndSize(serialized_pb, &message_type, &message_len) < 0) {
+ return NULL;
+ }
+
+ FileDescriptorProto file_proto;
+ if (!file_proto.ParseFromArray(message_type, message_len)) {
+ PyErr_SetString(PyExc_TypeError, "Couldn't parse file content!");
+ return NULL;
+ }
+
+ // If the file was already part of a C++ library, all its descriptors are in
+ // the underlying pool. No need to do anything else.
+ const FileDescriptor* generated_file = NULL;
+ if (self->underlay) {
+ generated_file = self->underlay->FindFileByName(file_proto.name());
+ }
+ if (generated_file != NULL) {
+ return PyFileDescriptor_FromDescriptorWithSerializedPb(
+ generated_file, serialized_pb);
+ }
+
+ BuildFileErrorCollector error_collector;
+ const FileDescriptor* descriptor =
+ self->pool->BuildFileCollectingErrors(file_proto,
+ &error_collector);
+ if (descriptor == NULL) {
+ PyErr_Format(PyExc_TypeError,
+ "Couldn't build proto file into descriptor pool!\n%s",
+ error_collector.error_message.c_str());
+ return NULL;
+ }
+
+ return PyFileDescriptor_FromDescriptorWithSerializedPb(
+ descriptor, serialized_pb);
+}
+
+PyObject* Add(PyDescriptorPool* self, PyObject* file_descriptor_proto) {
+ ScopedPyObjectPtr serialized_pb(
+ PyObject_CallMethod(file_descriptor_proto, "SerializeToString", NULL));
+ if (serialized_pb == NULL) {
+ return NULL;
+ }
+ return AddSerializedFile(self, serialized_pb.get());
+}
+
+static PyMethodDef Methods[] = {
+ { "Add", (PyCFunction)Add, METH_O,
+ "Adds the FileDescriptorProto and its types to this pool." },
+ { "AddSerializedFile", (PyCFunction)AddSerializedFile, METH_O,
+ "Adds a serialized FileDescriptorProto to this pool." },
+
+ // TODO(amauryfa): Understand why the Python implementation differs from
+ // this one, ask users to use another API and deprecate these functions.
+ { "AddFileDescriptor", (PyCFunction)AddFileDescriptor, METH_O,
+ "No-op. Add() must have been called before." },
+ { "AddDescriptor", (PyCFunction)AddDescriptor, METH_O,
+ "No-op. Add() must have been called before." },
+ { "AddEnumDescriptor", (PyCFunction)AddEnumDescriptor, METH_O,
+ "No-op. Add() must have been called before." },
+
+ { "FindFileByName", (PyCFunction)FindFileByName, METH_O,
+ "Searches for a file descriptor by its .proto name." },
+ { "FindMessageTypeByName", (PyCFunction)FindMessageByName, METH_O,
+ "Searches for a message descriptor by full name." },
+ { "FindFieldByName", (PyCFunction)FindFieldByName, METH_O,
+ "Searches for a field descriptor by full name." },
+ { "FindExtensionByName", (PyCFunction)FindExtensionByName, METH_O,
+ "Searches for extension descriptor by full name." },
+ { "FindEnumTypeByName", (PyCFunction)FindEnumTypeByName, METH_O,
+ "Searches for enum type descriptor by full name." },
+ { "FindOneofByName", (PyCFunction)FindOneofByName, METH_O,
+ "Searches for oneof descriptor by full name." },
+ { "FindServiceByName", (PyCFunction)FindServiceByName, METH_O,
+ "Searches for service descriptor by full name." },
+ { "FindMethodByName", (PyCFunction)FindMethodByName, METH_O,
+ "Searches for method descriptor by full name." },
+
+ { "FindFileContainingSymbol", (PyCFunction)FindFileContainingSymbol, METH_O,
+ "Gets the FileDescriptor containing the specified symbol." },
+ {NULL}
+};
+
+} // namespace cdescriptor_pool
+
+PyTypeObject PyDescriptorPool_Type = {
+ PyVarObject_HEAD_INIT(&PyType_Type, 0)
+ FULL_MODULE_NAME ".DescriptorPool", // tp_name
+ sizeof(PyDescriptorPool), // tp_basicsize
+ 0, // tp_itemsize
+ (destructor)cdescriptor_pool::Dealloc, // tp_dealloc
+ 0, // tp_print
+ 0, // tp_getattr
+ 0, // tp_setattr
+ 0, // tp_compare
+ 0, // tp_repr
+ 0, // tp_as_number
+ 0, // tp_as_sequence
+ 0, // tp_as_mapping
+ 0, // tp_hash
+ 0, // tp_call
+ 0, // tp_str
+ 0, // tp_getattro
+ 0, // tp_setattro
+ 0, // tp_as_buffer
+ Py_TPFLAGS_DEFAULT, // tp_flags
+ "A Descriptor Pool", // tp_doc
+ 0, // tp_traverse
+ 0, // tp_clear
+ 0, // tp_richcompare
+ 0, // tp_weaklistoffset
+ 0, // tp_iter
+ 0, // tp_iternext
+ cdescriptor_pool::Methods, // tp_methods
+ 0, // tp_members
+ 0, // tp_getset
+ 0, // tp_base
+ 0, // tp_dict
+ 0, // tp_descr_get
+ 0, // tp_descr_set
+ 0, // tp_dictoffset
+ 0, // tp_init
+ 0, // tp_alloc
+ cdescriptor_pool::New, // tp_new
+ PyObject_Del, // tp_free
+};
+
+// This is the DescriptorPool which contains all the definitions from the
+// generated _pb2.py modules.
+static PyDescriptorPool* python_generated_pool = NULL;
+
+bool InitDescriptorPool() {
+ if (PyType_Ready(&PyDescriptorPool_Type) < 0)
+ return false;
+
+ // The Pool of messages declared in Python libraries.
+ // generated_pool() contains all messages already linked in C++ libraries, and
+ // is used as underlay.
+ python_generated_pool = cdescriptor_pool::PyDescriptorPool_NewWithUnderlay(
+ DescriptorPool::generated_pool());
+ if (python_generated_pool == NULL) {
+ return false;
+ }
+ // Register this pool to be found for C++-generated descriptors.
+ descriptor_pool_map.insert(
+ std::make_pair(DescriptorPool::generated_pool(),
+ python_generated_pool));
+
+ return true;
+}
+
+// The default DescriptorPool used everywhere in this module.
+// Today it's the python_generated_pool.
+// TODO(amauryfa): Remove all usages of this function: the pool should be
+// derived from the context.
+PyDescriptorPool* GetDefaultDescriptorPool() {
+ return python_generated_pool;
+}
+
+PyDescriptorPool* GetDescriptorPool_FromPool(const DescriptorPool* pool) {
+ // Fast path for standard descriptors.
+ if (pool == python_generated_pool->pool ||
+ pool == DescriptorPool::generated_pool()) {
+ return python_generated_pool;
+ }
+ hash_map<const DescriptorPool*, PyDescriptorPool*>::iterator it =
+ descriptor_pool_map.find(pool);
+ if (it == descriptor_pool_map.end()) {
+ PyErr_SetString(PyExc_KeyError, "Unknown descriptor pool");
+ return NULL;
+ }
+ return it->second;
+}
+
+} // namespace python
+} // namespace protobuf
+} // namespace google
diff --git a/python/google/protobuf/pyext/descriptor_pool.h b/python/google/protobuf/pyext/descriptor_pool.h
new file mode 100644
index 00000000..2a42c112
--- /dev/null
+++ b/python/google/protobuf/pyext/descriptor_pool.h
@@ -0,0 +1,167 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef GOOGLE_PROTOBUF_PYTHON_CPP_DESCRIPTOR_POOL_H__
+#define GOOGLE_PROTOBUF_PYTHON_CPP_DESCRIPTOR_POOL_H__
+
+#include <Python.h>
+
+#include <google/protobuf/stubs/hash.h>
+#include <google/protobuf/descriptor.h>
+
+namespace google {
+namespace protobuf {
+class MessageFactory;
+
+namespace python {
+
+// The (meta) type of all Messages classes.
+struct CMessageClass;
+
+// Wraps operations to the global DescriptorPool which contains information
+// about all messages and fields.
+//
+// There is normally one pool per process. We make it a Python object only
+// because it contains many Python references.
+// TODO(amauryfa): See whether such objects can appear in reference cycles, and
+// consider adding support for the cyclic GC.
+//
+// "Methods" that interacts with this DescriptorPool are in the cdescriptor_pool
+// namespace.
+typedef struct PyDescriptorPool {
+ PyObject_HEAD
+
+ // The C++ pool containing Descriptors.
+ DescriptorPool* pool;
+
+ // The C++ pool acting as an underlay. Can be NULL.
+ // This pointer is not owned and must stay alive.
+ const DescriptorPool* underlay;
+
+ // The C++ descriptor database used to fetch unknown protos. Can be NULL.
+ // This pointer is owned.
+ const DescriptorDatabase* database;
+
+ // DynamicMessageFactory used to create C++ instances of messages.
+ // This object cache the descriptors that were used, so the DescriptorPool
+ // needs to get rid of it before it can delete itself.
+ //
+ // Note: A C++ MessageFactory is different from the Python MessageFactory.
+ // The C++ one creates messages, when the Python one creates classes.
+ MessageFactory* message_factory;
+
+ // Make our own mapping to retrieve Python classes from C++ descriptors.
+ //
+ // Descriptor pointers stored here are owned by the DescriptorPool above.
+ // Python references to classes are owned by this PyDescriptorPool.
+ typedef hash_map<const Descriptor*, CMessageClass*> ClassesByMessageMap;
+ ClassesByMessageMap* classes_by_descriptor;
+
+ // Cache the options for any kind of descriptor.
+ // Descriptor pointers are owned by the DescriptorPool above.
+ // Python objects are owned by the map.
+ hash_map<const void*, PyObject*>* descriptor_options;
+} PyDescriptorPool;
+
+
+extern PyTypeObject PyDescriptorPool_Type;
+
+namespace cdescriptor_pool {
+
+// Looks up a message by name.
+// Returns a message Descriptor, or NULL if not found.
+const Descriptor* FindMessageTypeByName(PyDescriptorPool* self,
+ const string& name);
+
+// Registers a new Python class for the given message descriptor.
+// On error, returns -1 with a Python exception set.
+int RegisterMessageClass(PyDescriptorPool* self,
+ const Descriptor* message_descriptor,
+ CMessageClass* message_class);
+
+// Retrieves the Python class registered with the given message descriptor.
+//
+// Returns a *borrowed* reference if found, otherwise returns NULL with an
+// exception set.
+CMessageClass* GetMessageClass(PyDescriptorPool* self,
+ const Descriptor* message_descriptor);
+
+// The functions below are also exposed as methods of the DescriptorPool type.
+
+// Looks up a message by name. Returns a PyMessageDescriptor corresponding to
+// the field on success, or NULL on failure.
+//
+// Returns a new reference.
+PyObject* FindMessageByName(PyDescriptorPool* self, PyObject* name);
+
+// Looks up a field by name. Returns a PyFieldDescriptor corresponding to
+// the field on success, or NULL on failure.
+//
+// Returns a new reference.
+PyObject* FindFieldByName(PyDescriptorPool* self, PyObject* name);
+
+// Looks up an extension by name. Returns a PyFieldDescriptor corresponding
+// to the field on success, or NULL on failure.
+//
+// Returns a new reference.
+PyObject* FindExtensionByName(PyDescriptorPool* self, PyObject* arg);
+
+// Looks up an enum type by name. Returns a PyEnumDescriptor corresponding
+// to the field on success, or NULL on failure.
+//
+// Returns a new reference.
+PyObject* FindEnumTypeByName(PyDescriptorPool* self, PyObject* arg);
+
+// Looks up a oneof by name. Returns a COneofDescriptor corresponding
+// to the oneof on success, or NULL on failure.
+//
+// Returns a new reference.
+PyObject* FindOneofByName(PyDescriptorPool* self, PyObject* arg);
+
+} // namespace cdescriptor_pool
+
+// Retrieve the global descriptor pool owned by the _message module.
+// This is the one used by pb2.py generated modules.
+// Returns a *borrowed* reference.
+// "Default" pool used to register messages from _pb2.py modules.
+PyDescriptorPool* GetDefaultDescriptorPool();
+
+// Retrieve the python descriptor pool owning a C++ descriptor pool.
+// Returns a *borrowed* reference.
+PyDescriptorPool* GetDescriptorPool_FromPool(const DescriptorPool* pool);
+
+// Initialize objects used by this module.
+bool InitDescriptorPool();
+
+} // namespace python
+} // namespace protobuf
+
+} // namespace google
+#endif // GOOGLE_PROTOBUF_PYTHON_CPP_DESCRIPTOR_POOL_H__
diff --git a/python/google/protobuf/pyext/extension_dict.cc b/python/google/protobuf/pyext/extension_dict.cc
new file mode 100644
index 00000000..21bbb8c2
--- /dev/null
+++ b/python/google/protobuf/pyext/extension_dict.cc
@@ -0,0 +1,337 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Author: anuraag@google.com (Anuraag Agrawal)
+// Author: tibell@google.com (Johan Tibell)
+
+#include <google/protobuf/pyext/extension_dict.h>
+
+#include <google/protobuf/stubs/logging.h>
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/dynamic_message.h>
+#include <google/protobuf/message.h>
+#include <google/protobuf/pyext/descriptor.h>
+#include <google/protobuf/pyext/descriptor_pool.h>
+#include <google/protobuf/pyext/message.h>
+#include <google/protobuf/pyext/repeated_composite_container.h>
+#include <google/protobuf/pyext/repeated_scalar_container.h>
+#include <google/protobuf/pyext/scoped_pyobject_ptr.h>
+#include <google/protobuf/stubs/shared_ptr.h>
+
+namespace google {
+namespace protobuf {
+namespace python {
+
+namespace extension_dict {
+
+PyObject* len(ExtensionDict* self) {
+#if PY_MAJOR_VERSION >= 3
+ return PyLong_FromLong(PyDict_Size(self->values));
+#else
+ return PyInt_FromLong(PyDict_Size(self->values));
+#endif
+}
+
+// TODO(tibell): Use VisitCompositeField.
+int ReleaseExtension(ExtensionDict* self,
+ PyObject* extension,
+ const FieldDescriptor* descriptor) {
+ if (descriptor->label() == FieldDescriptor::LABEL_REPEATED) {
+ if (descriptor->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
+ if (repeated_composite_container::Release(
+ reinterpret_cast<RepeatedCompositeContainer*>(
+ extension)) < 0) {
+ return -1;
+ }
+ } else {
+ if (repeated_scalar_container::Release(
+ reinterpret_cast<RepeatedScalarContainer*>(
+ extension)) < 0) {
+ return -1;
+ }
+ }
+ } else if (descriptor->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
+ if (cmessage::ReleaseSubMessage(
+ self->parent, descriptor,
+ reinterpret_cast<CMessage*>(extension)) < 0) {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+PyObject* subscript(ExtensionDict* self, PyObject* key) {
+ const FieldDescriptor* descriptor = cmessage::GetExtensionDescriptor(key);
+ if (descriptor == NULL) {
+ return NULL;
+ }
+ if (!CheckFieldBelongsToMessage(descriptor, self->message)) {
+ return NULL;
+ }
+
+ if (descriptor->label() != FieldDescriptor::LABEL_REPEATED &&
+ descriptor->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE) {
+ return cmessage::InternalGetScalar(self->message, descriptor);
+ }
+
+ PyObject* value = PyDict_GetItem(self->values, key);
+ if (value != NULL) {
+ Py_INCREF(value);
+ return value;
+ }
+
+ if (self->parent == NULL) {
+ // We are in "detached" state. Don't allow further modifications.
+ // TODO(amauryfa): Support adding non-scalars to a detached extension dict.
+ // This probably requires to store the type of the main message.
+ PyErr_SetObject(PyExc_KeyError, key);
+ return NULL;
+ }
+
+ if (descriptor->label() != FieldDescriptor::LABEL_REPEATED &&
+ descriptor->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
+ PyObject* sub_message = cmessage::InternalGetSubMessage(
+ self->parent, descriptor);
+ if (sub_message == NULL) {
+ return NULL;
+ }
+ PyDict_SetItem(self->values, key, sub_message);
+ return sub_message;
+ }
+
+ if (descriptor->label() == FieldDescriptor::LABEL_REPEATED) {
+ if (descriptor->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
+ CMessageClass* message_class = cdescriptor_pool::GetMessageClass(
+ cmessage::GetDescriptorPoolForMessage(self->parent),
+ descriptor->message_type());
+ if (message_class == NULL) {
+ return NULL;
+ }
+ PyObject* py_container = repeated_composite_container::NewContainer(
+ self->parent, descriptor, message_class);
+ if (py_container == NULL) {
+ return NULL;
+ }
+ PyDict_SetItem(self->values, key, py_container);
+ return py_container;
+ } else {
+ PyObject* py_container = repeated_scalar_container::NewContainer(
+ self->parent, descriptor);
+ if (py_container == NULL) {
+ return NULL;
+ }
+ PyDict_SetItem(self->values, key, py_container);
+ return py_container;
+ }
+ }
+ PyErr_SetString(PyExc_ValueError, "control reached unexpected line");
+ return NULL;
+}
+
+int ass_subscript(ExtensionDict* self, PyObject* key, PyObject* value) {
+ const FieldDescriptor* descriptor = cmessage::GetExtensionDescriptor(key);
+ if (descriptor == NULL) {
+ return -1;
+ }
+ if (!CheckFieldBelongsToMessage(descriptor, self->message)) {
+ return -1;
+ }
+
+ if (descriptor->label() != FieldDescriptor::LABEL_OPTIONAL ||
+ descriptor->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
+ PyErr_SetString(PyExc_TypeError, "Extension is repeated and/or composite "
+ "type");
+ return -1;
+ }
+ if (self->parent) {
+ cmessage::AssureWritable(self->parent);
+ if (cmessage::InternalSetScalar(self->parent, descriptor, value) < 0) {
+ return -1;
+ }
+ }
+ // TODO(tibell): We shouldn't write scalars to the cache.
+ PyDict_SetItem(self->values, key, value);
+ return 0;
+}
+
+PyObject* ClearExtension(ExtensionDict* self, PyObject* extension) {
+ const FieldDescriptor* descriptor =
+ cmessage::GetExtensionDescriptor(extension);
+ if (descriptor == NULL) {
+ return NULL;
+ }
+ PyObject* value = PyDict_GetItem(self->values, extension);
+ if (self->parent) {
+ if (value != NULL) {
+ if (ReleaseExtension(self, value, descriptor) < 0) {
+ return NULL;
+ }
+ }
+ if (ScopedPyObjectPtr(cmessage::ClearFieldByDescriptor(
+ self->parent, descriptor)) == NULL) {
+ return NULL;
+ }
+ }
+ if (PyDict_DelItem(self->values, extension) < 0) {
+ PyErr_Clear();
+ }
+ Py_RETURN_NONE;
+}
+
+PyObject* HasExtension(ExtensionDict* self, PyObject* extension) {
+ const FieldDescriptor* descriptor =
+ cmessage::GetExtensionDescriptor(extension);
+ if (descriptor == NULL) {
+ return NULL;
+ }
+ if (self->parent) {
+ return cmessage::HasFieldByDescriptor(self->parent, descriptor);
+ } else {
+ int exists = PyDict_Contains(self->values, extension);
+ if (exists < 0) {
+ return NULL;
+ }
+ return PyBool_FromLong(exists);
+ }
+}
+
+PyObject* _FindExtensionByName(ExtensionDict* self, PyObject* name) {
+ ScopedPyObjectPtr extensions_by_name(PyObject_GetAttrString(
+ reinterpret_cast<PyObject*>(self->parent), "_extensions_by_name"));
+ if (extensions_by_name == NULL) {
+ return NULL;
+ }
+ PyObject* result = PyDict_GetItem(extensions_by_name.get(), name);
+ if (result == NULL) {
+ Py_RETURN_NONE;
+ } else {
+ Py_INCREF(result);
+ return result;
+ }
+}
+
+PyObject* _FindExtensionByNumber(ExtensionDict* self, PyObject* number) {
+ ScopedPyObjectPtr extensions_by_number(PyObject_GetAttrString(
+ reinterpret_cast<PyObject*>(self->parent), "_extensions_by_number"));
+ if (extensions_by_number == NULL) {
+ return NULL;
+ }
+ PyObject* result = PyDict_GetItem(extensions_by_number.get(), number);
+ if (result == NULL) {
+ Py_RETURN_NONE;
+ } else {
+ Py_INCREF(result);
+ return result;
+ }
+}
+
+ExtensionDict* NewExtensionDict(CMessage *parent) {
+ ExtensionDict* self = reinterpret_cast<ExtensionDict*>(
+ PyType_GenericAlloc(&ExtensionDict_Type, 0));
+ if (self == NULL) {
+ return NULL;
+ }
+
+ self->parent = parent; // Store a borrowed reference.
+ self->message = parent->message;
+ self->owner = parent->owner;
+ self->values = PyDict_New();
+ return self;
+}
+
+void dealloc(ExtensionDict* self) {
+ Py_CLEAR(self->values);
+ self->owner.reset();
+ Py_TYPE(self)->tp_free(reinterpret_cast<PyObject*>(self));
+}
+
+static PyMappingMethods MpMethods = {
+ (lenfunc)len, /* mp_length */
+ (binaryfunc)subscript, /* mp_subscript */
+ (objobjargproc)ass_subscript,/* mp_ass_subscript */
+};
+
+#define EDMETHOD(name, args, doc) { #name, (PyCFunction)name, args, doc }
+static PyMethodDef Methods[] = {
+ EDMETHOD(ClearExtension, METH_O, "Clears an extension from the object."),
+ EDMETHOD(HasExtension, METH_O, "Checks if the object has an extension."),
+ EDMETHOD(_FindExtensionByName, METH_O,
+ "Finds an extension by name."),
+ EDMETHOD(_FindExtensionByNumber, METH_O,
+ "Finds an extension by field number."),
+ { NULL, NULL }
+};
+
+} // namespace extension_dict
+
+PyTypeObject ExtensionDict_Type = {
+ PyVarObject_HEAD_INIT(&PyType_Type, 0)
+ FULL_MODULE_NAME ".ExtensionDict", // tp_name
+ sizeof(ExtensionDict), // tp_basicsize
+ 0, // tp_itemsize
+ (destructor)extension_dict::dealloc, // tp_dealloc
+ 0, // tp_print
+ 0, // tp_getattr
+ 0, // tp_setattr
+ 0, // tp_compare
+ 0, // tp_repr
+ 0, // tp_as_number
+ 0, // tp_as_sequence
+ &extension_dict::MpMethods, // tp_as_mapping
+ PyObject_HashNotImplemented, // tp_hash
+ 0, // tp_call
+ 0, // tp_str
+ 0, // tp_getattro
+ 0, // tp_setattro
+ 0, // tp_as_buffer
+ Py_TPFLAGS_DEFAULT, // tp_flags
+ "An extension dict", // tp_doc
+ 0, // tp_traverse
+ 0, // tp_clear
+ 0, // tp_richcompare
+ 0, // tp_weaklistoffset
+ 0, // tp_iter
+ 0, // tp_iternext
+ extension_dict::Methods, // tp_methods
+ 0, // tp_members
+ 0, // tp_getset
+ 0, // tp_base
+ 0, // tp_dict
+ 0, // tp_descr_get
+ 0, // tp_descr_set
+ 0, // tp_dictoffset
+ 0, // tp_init
+};
+
+} // namespace python
+} // namespace protobuf
+} // namespace google
diff --git a/python/google/protobuf/pyext/extension_dict.h b/python/google/protobuf/pyext/extension_dict.h
new file mode 100644
index 00000000..2456eda1
--- /dev/null
+++ b/python/google/protobuf/pyext/extension_dict.h
@@ -0,0 +1,137 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Author: anuraag@google.com (Anuraag Agrawal)
+// Author: tibell@google.com (Johan Tibell)
+
+#ifndef GOOGLE_PROTOBUF_PYTHON_CPP_EXTENSION_DICT_H__
+#define GOOGLE_PROTOBUF_PYTHON_CPP_EXTENSION_DICT_H__
+
+#include <Python.h>
+
+#include <memory>
+#ifndef _SHARED_PTR_H
+#include <google/protobuf/stubs/shared_ptr.h>
+#endif
+
+namespace google {
+namespace protobuf {
+
+class Message;
+class FieldDescriptor;
+
+#ifdef _SHARED_PTR_H
+using std::shared_ptr;
+#else
+using internal::shared_ptr;
+#endif
+
+namespace python {
+
+struct CMessage;
+
+typedef struct ExtensionDict {
+ PyObject_HEAD;
+
+ // This is the top-level C++ Message object that owns the whole
+ // proto tree. Every Python container class holds a
+ // reference to it in order to keep it alive as long as there's a
+ // Python object that references any part of the tree.
+ shared_ptr<Message> owner;
+
+ // Weak reference to parent message. Used to make sure
+ // the parent is writable when an extension field is modified.
+ CMessage* parent;
+
+ // Pointer to the C++ Message that this ExtensionDict extends.
+ // Not owned by us.
+ Message* message;
+
+ // A dict of child messages, indexed by Extension descriptors.
+ // Similar to CMessage::composite_fields.
+ PyObject* values;
+} ExtensionDict;
+
+extern PyTypeObject ExtensionDict_Type;
+
+namespace extension_dict {
+
+// Builds an Extensions dict for a specific message.
+ExtensionDict* NewExtensionDict(CMessage *parent);
+
+// Gets the number of extension values in this ExtensionDict as a python object.
+//
+// Returns a new reference.
+PyObject* len(ExtensionDict* self);
+
+// Releases extensions referenced outside this dictionary to keep outside
+// references alive.
+//
+// Returns 0 on success, -1 on failure.
+int ReleaseExtension(ExtensionDict* self,
+ PyObject* extension,
+ const FieldDescriptor* descriptor);
+
+// Gets an extension from the dict for the given extension descriptor.
+//
+// Returns a new reference.
+PyObject* subscript(ExtensionDict* self, PyObject* key);
+
+// Assigns a value to an extension in the dict. Can only be used for singular
+// simple types.
+//
+// Returns 0 on success, -1 on failure.
+int ass_subscript(ExtensionDict* self, PyObject* key, PyObject* value);
+
+// Clears an extension from the dict. Will release the extension if there
+// is still an external reference left to it.
+//
+// Returns None on success.
+PyObject* ClearExtension(ExtensionDict* self,
+ PyObject* extension);
+
+// Gets an extension from the dict given the extension name as opposed to
+// descriptor.
+//
+// Returns a new reference.
+PyObject* _FindExtensionByName(ExtensionDict* self, PyObject* name);
+
+// Gets an extension from the dict given the extension field number as
+// opposed to descriptor.
+//
+// Returns a new reference.
+PyObject* _FindExtensionByNumber(ExtensionDict* self, PyObject* number);
+
+} // namespace extension_dict
+} // namespace python
+} // namespace protobuf
+
+} // namespace google
+#endif // GOOGLE_PROTOBUF_PYTHON_CPP_EXTENSION_DICT_H__
diff --git a/python/google/protobuf/pyext/map_container.cc b/python/google/protobuf/pyext/map_container.cc
new file mode 100644
index 00000000..90438df1
--- /dev/null
+++ b/python/google/protobuf/pyext/map_container.cc
@@ -0,0 +1,969 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Author: haberman@google.com (Josh Haberman)
+
+#include <google/protobuf/pyext/map_container.h>
+
+#include <memory>
+#ifndef _SHARED_PTR_H
+#include <google/protobuf/stubs/shared_ptr.h>
+#endif
+
+#include <google/protobuf/stubs/logging.h>
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/map_field.h>
+#include <google/protobuf/map.h>
+#include <google/protobuf/message.h>
+#include <google/protobuf/pyext/message.h>
+#include <google/protobuf/pyext/scoped_pyobject_ptr.h>
+
+#if PY_MAJOR_VERSION >= 3
+ #define PyInt_FromLong PyLong_FromLong
+ #define PyInt_FromSize_t PyLong_FromSize_t
+#endif
+
+namespace google {
+namespace protobuf {
+namespace python {
+
+// Functions that need access to map reflection functionality.
+// They need to be contained in this class because it is friended.
+class MapReflectionFriend {
+ public:
+ // Methods that are in common between the map types.
+ static PyObject* Contains(PyObject* _self, PyObject* key);
+ static Py_ssize_t Length(PyObject* _self);
+ static PyObject* GetIterator(PyObject *_self);
+ static PyObject* IterNext(PyObject* _self);
+
+ // Methods that differ between the map types.
+ static PyObject* ScalarMapGetItem(PyObject* _self, PyObject* key);
+ static PyObject* MessageMapGetItem(PyObject* _self, PyObject* key);
+ static int ScalarMapSetItem(PyObject* _self, PyObject* key, PyObject* v);
+ static int MessageMapSetItem(PyObject* _self, PyObject* key, PyObject* v);
+};
+
+struct MapIterator {
+ PyObject_HEAD;
+
+ google::protobuf::scoped_ptr< ::google::protobuf::MapIterator> iter;
+
+ // A pointer back to the container, so we can notice changes to the version.
+ // We own a ref on this.
+ MapContainer* container;
+
+ // We need to keep a ref on the Message* too, because
+ // MapIterator::~MapIterator() accesses it. Normally this would be ok because
+ // the ref on container (above) would guarantee outlive semantics. However in
+ // the case of ClearField(), InitializeAndCopyToParentContainer() resets the
+ // message pointer (and the owner) to a different message, a copy of the
+ // original. But our iterator still points to the original, which could now
+ // get deleted before us.
+ //
+ // To prevent this, we ensure that the Message will always stay alive as long
+ // as this iterator does. This is solely for the benefit of the MapIterator
+ // destructor -- we should never actually access the iterator in this state
+ // except to delete it.
+ shared_ptr<Message> owner;
+
+ // The version of the map when we took the iterator to it.
+ //
+ // We store this so that if the map is modified during iteration we can throw
+ // an error.
+ uint64 version;
+
+ // True if the container is empty. We signal this separately to avoid calling
+ // any of the iteration methods, which are non-const.
+ bool empty;
+};
+
+Message* MapContainer::GetMutableMessage() {
+ cmessage::AssureWritable(parent);
+ return const_cast<Message*>(message);
+}
+
+// Consumes a reference on the Python string object.
+static bool PyStringToSTL(PyObject* py_string, string* stl_string) {
+ char *value;
+ Py_ssize_t value_len;
+
+ if (!py_string) {
+ return false;
+ }
+ if (PyBytes_AsStringAndSize(py_string, &value, &value_len) < 0) {
+ Py_DECREF(py_string);
+ return false;
+ } else {
+ stl_string->assign(value, value_len);
+ Py_DECREF(py_string);
+ return true;
+ }
+}
+
+static bool PythonToMapKey(PyObject* obj,
+ const FieldDescriptor* field_descriptor,
+ MapKey* key) {
+ switch (field_descriptor->cpp_type()) {
+ case FieldDescriptor::CPPTYPE_INT32: {
+ GOOGLE_CHECK_GET_INT32(obj, value, false);
+ key->SetInt32Value(value);
+ break;
+ }
+ case FieldDescriptor::CPPTYPE_INT64: {
+ GOOGLE_CHECK_GET_INT64(obj, value, false);
+ key->SetInt64Value(value);
+ break;
+ }
+ case FieldDescriptor::CPPTYPE_UINT32: {
+ GOOGLE_CHECK_GET_UINT32(obj, value, false);
+ key->SetUInt32Value(value);
+ break;
+ }
+ case FieldDescriptor::CPPTYPE_UINT64: {
+ GOOGLE_CHECK_GET_UINT64(obj, value, false);
+ key->SetUInt64Value(value);
+ break;
+ }
+ case FieldDescriptor::CPPTYPE_BOOL: {
+ GOOGLE_CHECK_GET_BOOL(obj, value, false);
+ key->SetBoolValue(value);
+ break;
+ }
+ case FieldDescriptor::CPPTYPE_STRING: {
+ string str;
+ if (!PyStringToSTL(CheckString(obj, field_descriptor), &str)) {
+ return false;
+ }
+ key->SetStringValue(str);
+ break;
+ }
+ default:
+ PyErr_Format(
+ PyExc_SystemError, "Type %d cannot be a map key",
+ field_descriptor->cpp_type());
+ return false;
+ }
+ return true;
+}
+
+static PyObject* MapKeyToPython(const FieldDescriptor* field_descriptor,
+ const MapKey& key) {
+ switch (field_descriptor->cpp_type()) {
+ case FieldDescriptor::CPPTYPE_INT32:
+ return PyInt_FromLong(key.GetInt32Value());
+ case FieldDescriptor::CPPTYPE_INT64:
+ return PyLong_FromLongLong(key.GetInt64Value());
+ case FieldDescriptor::CPPTYPE_UINT32:
+ return PyInt_FromSize_t(key.GetUInt32Value());
+ case FieldDescriptor::CPPTYPE_UINT64:
+ return PyLong_FromUnsignedLongLong(key.GetUInt64Value());
+ case FieldDescriptor::CPPTYPE_BOOL:
+ return PyBool_FromLong(key.GetBoolValue());
+ case FieldDescriptor::CPPTYPE_STRING:
+ return ToStringObject(field_descriptor, key.GetStringValue());
+ default:
+ PyErr_Format(
+ PyExc_SystemError, "Couldn't convert type %d to value",
+ field_descriptor->cpp_type());
+ return NULL;
+ }
+}
+
+// This is only used for ScalarMap, so we don't need to handle the
+// CPPTYPE_MESSAGE case.
+PyObject* MapValueRefToPython(const FieldDescriptor* field_descriptor,
+ MapValueRef* value) {
+ switch (field_descriptor->cpp_type()) {
+ case FieldDescriptor::CPPTYPE_INT32:
+ return PyInt_FromLong(value->GetInt32Value());
+ case FieldDescriptor::CPPTYPE_INT64:
+ return PyLong_FromLongLong(value->GetInt64Value());
+ case FieldDescriptor::CPPTYPE_UINT32:
+ return PyInt_FromSize_t(value->GetUInt32Value());
+ case FieldDescriptor::CPPTYPE_UINT64:
+ return PyLong_FromUnsignedLongLong(value->GetUInt64Value());
+ case FieldDescriptor::CPPTYPE_FLOAT:
+ return PyFloat_FromDouble(value->GetFloatValue());
+ case FieldDescriptor::CPPTYPE_DOUBLE:
+ return PyFloat_FromDouble(value->GetDoubleValue());
+ case FieldDescriptor::CPPTYPE_BOOL:
+ return PyBool_FromLong(value->GetBoolValue());
+ case FieldDescriptor::CPPTYPE_STRING:
+ return ToStringObject(field_descriptor, value->GetStringValue());
+ case FieldDescriptor::CPPTYPE_ENUM:
+ return PyInt_FromLong(value->GetEnumValue());
+ default:
+ PyErr_Format(
+ PyExc_SystemError, "Couldn't convert type %d to value",
+ field_descriptor->cpp_type());
+ return NULL;
+ }
+}
+
+// This is only used for ScalarMap, so we don't need to handle the
+// CPPTYPE_MESSAGE case.
+static bool PythonToMapValueRef(PyObject* obj,
+ const FieldDescriptor* field_descriptor,
+ bool allow_unknown_enum_values,
+ MapValueRef* value_ref) {
+ switch (field_descriptor->cpp_type()) {
+ case FieldDescriptor::CPPTYPE_INT32: {
+ GOOGLE_CHECK_GET_INT32(obj, value, false);
+ value_ref->SetInt32Value(value);
+ return true;
+ }
+ case FieldDescriptor::CPPTYPE_INT64: {
+ GOOGLE_CHECK_GET_INT64(obj, value, false);
+ value_ref->SetInt64Value(value);
+ return true;
+ }
+ case FieldDescriptor::CPPTYPE_UINT32: {
+ GOOGLE_CHECK_GET_UINT32(obj, value, false);
+ value_ref->SetUInt32Value(value);
+ return true;
+ }
+ case FieldDescriptor::CPPTYPE_UINT64: {
+ GOOGLE_CHECK_GET_UINT64(obj, value, false);
+ value_ref->SetUInt64Value(value);
+ return true;
+ }
+ case FieldDescriptor::CPPTYPE_FLOAT: {
+ GOOGLE_CHECK_GET_FLOAT(obj, value, false);
+ value_ref->SetFloatValue(value);
+ return true;
+ }
+ case FieldDescriptor::CPPTYPE_DOUBLE: {
+ GOOGLE_CHECK_GET_DOUBLE(obj, value, false);
+ value_ref->SetDoubleValue(value);
+ return true;
+ }
+ case FieldDescriptor::CPPTYPE_BOOL: {
+ GOOGLE_CHECK_GET_BOOL(obj, value, false);
+ value_ref->SetBoolValue(value);
+ return true;;
+ }
+ case FieldDescriptor::CPPTYPE_STRING: {
+ string str;
+ if (!PyStringToSTL(CheckString(obj, field_descriptor), &str)) {
+ return false;
+ }
+ value_ref->SetStringValue(str);
+ return true;
+ }
+ case FieldDescriptor::CPPTYPE_ENUM: {
+ GOOGLE_CHECK_GET_INT32(obj, value, false);
+ if (allow_unknown_enum_values) {
+ value_ref->SetEnumValue(value);
+ return true;
+ } else {
+ const EnumDescriptor* enum_descriptor = field_descriptor->enum_type();
+ const EnumValueDescriptor* enum_value =
+ enum_descriptor->FindValueByNumber(value);
+ if (enum_value != NULL) {
+ value_ref->SetEnumValue(value);
+ return true;
+ } else {
+ PyErr_Format(PyExc_ValueError, "Unknown enum value: %d", value);
+ return false;
+ }
+ }
+ break;
+ }
+ default:
+ PyErr_Format(
+ PyExc_SystemError, "Setting value to a field of unknown type %d",
+ field_descriptor->cpp_type());
+ return false;
+ }
+}
+
+// Map methods common to ScalarMap and MessageMap //////////////////////////////
+
+static MapContainer* GetMap(PyObject* obj) {
+ return reinterpret_cast<MapContainer*>(obj);
+}
+
+Py_ssize_t MapReflectionFriend::Length(PyObject* _self) {
+ MapContainer* self = GetMap(_self);
+ const google::protobuf::Message* message = self->message;
+ return message->GetReflection()->MapSize(*message,
+ self->parent_field_descriptor);
+}
+
+PyObject* Clear(PyObject* _self) {
+ MapContainer* self = GetMap(_self);
+ Message* message = self->GetMutableMessage();
+ const Reflection* reflection = message->GetReflection();
+
+ reflection->ClearField(message, self->parent_field_descriptor);
+
+ Py_RETURN_NONE;
+}
+
+PyObject* MapReflectionFriend::Contains(PyObject* _self, PyObject* key) {
+ MapContainer* self = GetMap(_self);
+
+ const Message* message = self->message;
+ const Reflection* reflection = message->GetReflection();
+ MapKey map_key;
+
+ if (!PythonToMapKey(key, self->key_field_descriptor, &map_key)) {
+ return NULL;
+ }
+
+ if (reflection->ContainsMapKey(*message, self->parent_field_descriptor,
+ map_key)) {
+ Py_RETURN_TRUE;
+ } else {
+ Py_RETURN_FALSE;
+ }
+}
+
+// Initializes the underlying Message object of "to" so it becomes a new parent
+// repeated scalar, and copies all the values from "from" to it. A child scalar
+// container can be released by passing it as both from and to (e.g. making it
+// the recipient of the new parent message and copying the values from itself).
+static int InitializeAndCopyToParentContainer(MapContainer* from,
+ MapContainer* to) {
+ // For now we require from == to, re-evaluate if we want to support deep copy
+ // as in repeated_scalar_container.cc.
+ GOOGLE_DCHECK(from == to);
+ Message* new_message = from->message->New();
+
+ if (MapReflectionFriend::Length(reinterpret_cast<PyObject*>(from)) > 0) {
+ // A somewhat roundabout way of copying just one field from old_message to
+ // new_message. This is the best we can do with what Reflection gives us.
+ Message* mutable_old = from->GetMutableMessage();
+ vector<const FieldDescriptor*> fields;
+ fields.push_back(from->parent_field_descriptor);
+
+ // Move the map field into the new message.
+ mutable_old->GetReflection()->SwapFields(mutable_old, new_message, fields);
+
+ // If/when we support from != to, this will be required also to copy the
+ // map field back into the existing message:
+ // mutable_old->MergeFrom(*new_message);
+ }
+
+ // If from == to this could delete old_message.
+ to->owner.reset(new_message);
+
+ to->parent = NULL;
+ to->parent_field_descriptor = from->parent_field_descriptor;
+ to->message = new_message;
+
+ // Invalidate iterators, since they point to the old copy of the field.
+ to->version++;
+
+ return 0;
+}
+
+int MapContainer::Release() {
+ return InitializeAndCopyToParentContainer(this, this);
+}
+
+
+// ScalarMap ///////////////////////////////////////////////////////////////////
+
+PyObject *NewScalarMapContainer(
+ CMessage* parent, const google::protobuf::FieldDescriptor* parent_field_descriptor) {
+ if (!CheckFieldBelongsToMessage(parent_field_descriptor, parent->message)) {
+ return NULL;
+ }
+
+#if PY_MAJOR_VERSION >= 3
+ ScopedPyObjectPtr obj(PyType_GenericAlloc(
+ reinterpret_cast<PyTypeObject *>(ScalarMapContainer_Type), 0));
+#else
+ ScopedPyObjectPtr obj(PyType_GenericAlloc(&ScalarMapContainer_Type, 0));
+#endif
+ if (obj.get() == NULL) {
+ return PyErr_Format(PyExc_RuntimeError,
+ "Could not allocate new container.");
+ }
+
+ MapContainer* self = GetMap(obj.get());
+
+ self->message = parent->message;
+ self->parent = parent;
+ self->parent_field_descriptor = parent_field_descriptor;
+ self->owner = parent->owner;
+ self->version = 0;
+
+ self->key_field_descriptor =
+ parent_field_descriptor->message_type()->FindFieldByName("key");
+ self->value_field_descriptor =
+ parent_field_descriptor->message_type()->FindFieldByName("value");
+
+ if (self->key_field_descriptor == NULL ||
+ self->value_field_descriptor == NULL) {
+ return PyErr_Format(PyExc_KeyError,
+ "Map entry descriptor did not have key/value fields");
+ }
+
+ return obj.release();
+}
+
+PyObject* MapReflectionFriend::ScalarMapGetItem(PyObject* _self,
+ PyObject* key) {
+ MapContainer* self = GetMap(_self);
+
+ Message* message = self->GetMutableMessage();
+ const Reflection* reflection = message->GetReflection();
+ MapKey map_key;
+ MapValueRef value;
+
+ if (!PythonToMapKey(key, self->key_field_descriptor, &map_key)) {
+ return NULL;
+ }
+
+ if (reflection->InsertOrLookupMapValue(message, self->parent_field_descriptor,
+ map_key, &value)) {
+ self->version++;
+ }
+
+ return MapValueRefToPython(self->value_field_descriptor, &value);
+}
+
+int MapReflectionFriend::ScalarMapSetItem(PyObject* _self, PyObject* key,
+ PyObject* v) {
+ MapContainer* self = GetMap(_self);
+
+ Message* message = self->GetMutableMessage();
+ const Reflection* reflection = message->GetReflection();
+ MapKey map_key;
+ MapValueRef value;
+
+ if (!PythonToMapKey(key, self->key_field_descriptor, &map_key)) {
+ return -1;
+ }
+
+ self->version++;
+
+ if (v) {
+ // Set item to v.
+ reflection->InsertOrLookupMapValue(message, self->parent_field_descriptor,
+ map_key, &value);
+
+ return PythonToMapValueRef(v, self->value_field_descriptor,
+ reflection->SupportsUnknownEnumValues(), &value)
+ ? 0
+ : -1;
+ } else {
+ // Delete key from map.
+ if (reflection->DeleteMapValue(message, self->parent_field_descriptor,
+ map_key)) {
+ return 0;
+ } else {
+ PyErr_Format(PyExc_KeyError, "Key not present in map");
+ return -1;
+ }
+ }
+}
+
+static PyObject* ScalarMapGet(PyObject* self, PyObject* args) {
+ PyObject* key;
+ PyObject* default_value = NULL;
+ if (PyArg_ParseTuple(args, "O|O", &key, &default_value) < 0) {
+ return NULL;
+ }
+
+ ScopedPyObjectPtr is_present(MapReflectionFriend::Contains(self, key));
+ if (is_present.get() == NULL) {
+ return NULL;
+ }
+
+ if (PyObject_IsTrue(is_present.get())) {
+ return MapReflectionFriend::ScalarMapGetItem(self, key);
+ } else {
+ if (default_value != NULL) {
+ Py_INCREF(default_value);
+ return default_value;
+ } else {
+ Py_RETURN_NONE;
+ }
+ }
+}
+
+static void ScalarMapDealloc(PyObject* _self) {
+ MapContainer* self = GetMap(_self);
+ self->owner.reset();
+ Py_TYPE(_self)->tp_free(_self);
+}
+
+static PyMethodDef ScalarMapMethods[] = {
+ { "__contains__", MapReflectionFriend::Contains, METH_O,
+ "Tests whether a key is a member of the map." },
+ { "clear", (PyCFunction)Clear, METH_NOARGS,
+ "Removes all elements from the map." },
+ { "get", ScalarMapGet, METH_VARARGS,
+ "Gets the value for the given key if present, or otherwise a default" },
+ /*
+ { "__deepcopy__", (PyCFunction)DeepCopy, METH_VARARGS,
+ "Makes a deep copy of the class." },
+ { "__reduce__", (PyCFunction)Reduce, METH_NOARGS,
+ "Outputs picklable representation of the repeated field." },
+ */
+ {NULL, NULL},
+};
+
+#if PY_MAJOR_VERSION >= 3
+ static PyType_Slot ScalarMapContainer_Type_slots[] = {
+ {Py_tp_dealloc, (void *)ScalarMapDealloc},
+ {Py_mp_length, (void *)MapReflectionFriend::Length},
+ {Py_mp_subscript, (void *)MapReflectionFriend::ScalarMapGetItem},
+ {Py_mp_ass_subscript, (void *)MapReflectionFriend::ScalarMapSetItem},
+ {Py_tp_methods, (void *)ScalarMapMethods},
+ {Py_tp_iter, (void *)MapReflectionFriend::GetIterator},
+ {0, 0},
+ };
+
+ PyType_Spec ScalarMapContainer_Type_spec = {
+ FULL_MODULE_NAME ".ScalarMapContainer",
+ sizeof(MapContainer),
+ 0,
+ Py_TPFLAGS_DEFAULT,
+ ScalarMapContainer_Type_slots
+ };
+ PyObject *ScalarMapContainer_Type;
+#else
+ static PyMappingMethods ScalarMapMappingMethods = {
+ MapReflectionFriend::Length, // mp_length
+ MapReflectionFriend::ScalarMapGetItem, // mp_subscript
+ MapReflectionFriend::ScalarMapSetItem, // mp_ass_subscript
+ };
+
+ PyTypeObject ScalarMapContainer_Type = {
+ PyVarObject_HEAD_INIT(&PyType_Type, 0)
+ FULL_MODULE_NAME ".ScalarMapContainer", // tp_name
+ sizeof(MapContainer), // tp_basicsize
+ 0, // tp_itemsize
+ ScalarMapDealloc, // tp_dealloc
+ 0, // tp_print
+ 0, // tp_getattr
+ 0, // tp_setattr
+ 0, // tp_compare
+ 0, // tp_repr
+ 0, // tp_as_number
+ 0, // tp_as_sequence
+ &ScalarMapMappingMethods, // tp_as_mapping
+ 0, // tp_hash
+ 0, // tp_call
+ 0, // tp_str
+ 0, // tp_getattro
+ 0, // tp_setattro
+ 0, // tp_as_buffer
+ Py_TPFLAGS_DEFAULT, // tp_flags
+ "A scalar map container", // tp_doc
+ 0, // tp_traverse
+ 0, // tp_clear
+ 0, // tp_richcompare
+ 0, // tp_weaklistoffset
+ MapReflectionFriend::GetIterator, // tp_iter
+ 0, // tp_iternext
+ ScalarMapMethods, // tp_methods
+ 0, // tp_members
+ 0, // tp_getset
+ 0, // tp_base
+ 0, // tp_dict
+ 0, // tp_descr_get
+ 0, // tp_descr_set
+ 0, // tp_dictoffset
+ 0, // tp_init
+ };
+#endif
+
+
+// MessageMap //////////////////////////////////////////////////////////////////
+
+static MessageMapContainer* GetMessageMap(PyObject* obj) {
+ return reinterpret_cast<MessageMapContainer*>(obj);
+}
+
+static PyObject* GetCMessage(MessageMapContainer* self, Message* message) {
+ // Get or create the CMessage object corresponding to this message.
+ ScopedPyObjectPtr key(PyLong_FromVoidPtr(message));
+ PyObject* ret = PyDict_GetItem(self->message_dict, key.get());
+
+ if (ret == NULL) {
+ CMessage* cmsg = cmessage::NewEmptyMessage(self->message_class);
+ ret = reinterpret_cast<PyObject*>(cmsg);
+
+ if (cmsg == NULL) {
+ return NULL;
+ }
+ cmsg->owner = self->owner;
+ cmsg->message = message;
+ cmsg->parent = self->parent;
+
+ if (PyDict_SetItem(self->message_dict, key.get(), ret) < 0) {
+ Py_DECREF(ret);
+ return NULL;
+ }
+ } else {
+ Py_INCREF(ret);
+ }
+
+ return ret;
+}
+
+PyObject* NewMessageMapContainer(
+ CMessage* parent, const google::protobuf::FieldDescriptor* parent_field_descriptor,
+ CMessageClass* message_class) {
+ if (!CheckFieldBelongsToMessage(parent_field_descriptor, parent->message)) {
+ return NULL;
+ }
+
+#if PY_MAJOR_VERSION >= 3
+ PyObject* obj = PyType_GenericAlloc(
+ reinterpret_cast<PyTypeObject *>(MessageMapContainer_Type), 0);
+#else
+ PyObject* obj = PyType_GenericAlloc(&MessageMapContainer_Type, 0);
+#endif
+ if (obj == NULL) {
+ return PyErr_Format(PyExc_RuntimeError,
+ "Could not allocate new container.");
+ }
+
+ MessageMapContainer* self = GetMessageMap(obj);
+
+ self->message = parent->message;
+ self->parent = parent;
+ self->parent_field_descriptor = parent_field_descriptor;
+ self->owner = parent->owner;
+ self->version = 0;
+
+ self->key_field_descriptor =
+ parent_field_descriptor->message_type()->FindFieldByName("key");
+ self->value_field_descriptor =
+ parent_field_descriptor->message_type()->FindFieldByName("value");
+
+ self->message_dict = PyDict_New();
+ if (self->message_dict == NULL) {
+ return PyErr_Format(PyExc_RuntimeError,
+ "Could not allocate message dict.");
+ }
+
+ Py_INCREF(message_class);
+ self->message_class = message_class;
+
+ if (self->key_field_descriptor == NULL ||
+ self->value_field_descriptor == NULL) {
+ Py_DECREF(obj);
+ return PyErr_Format(PyExc_KeyError,
+ "Map entry descriptor did not have key/value fields");
+ }
+
+ return obj;
+}
+
+int MapReflectionFriend::MessageMapSetItem(PyObject* _self, PyObject* key,
+ PyObject* v) {
+ if (v) {
+ PyErr_Format(PyExc_ValueError,
+ "Direct assignment of submessage not allowed");
+ return -1;
+ }
+
+ // Now we know that this is a delete, not a set.
+
+ MessageMapContainer* self = GetMessageMap(_self);
+ Message* message = self->GetMutableMessage();
+ const Reflection* reflection = message->GetReflection();
+ MapKey map_key;
+ MapValueRef value;
+
+ self->version++;
+
+ if (!PythonToMapKey(key, self->key_field_descriptor, &map_key)) {
+ return -1;
+ }
+
+ // Delete key from map.
+ if (reflection->DeleteMapValue(message, self->parent_field_descriptor,
+ map_key)) {
+ return 0;
+ } else {
+ PyErr_Format(PyExc_KeyError, "Key not present in map");
+ return -1;
+ }
+}
+
+PyObject* MapReflectionFriend::MessageMapGetItem(PyObject* _self,
+ PyObject* key) {
+ MessageMapContainer* self = GetMessageMap(_self);
+
+ Message* message = self->GetMutableMessage();
+ const Reflection* reflection = message->GetReflection();
+ MapKey map_key;
+ MapValueRef value;
+
+ if (!PythonToMapKey(key, self->key_field_descriptor, &map_key)) {
+ return NULL;
+ }
+
+ if (reflection->InsertOrLookupMapValue(message, self->parent_field_descriptor,
+ map_key, &value)) {
+ self->version++;
+ }
+
+ return GetCMessage(self, value.MutableMessageValue());
+}
+
+PyObject* MessageMapGet(PyObject* self, PyObject* args) {
+ PyObject* key;
+ PyObject* default_value = NULL;
+ if (PyArg_ParseTuple(args, "O|O", &key, &default_value) < 0) {
+ return NULL;
+ }
+
+ ScopedPyObjectPtr is_present(MapReflectionFriend::Contains(self, key));
+ if (is_present.get() == NULL) {
+ return NULL;
+ }
+
+ if (PyObject_IsTrue(is_present.get())) {
+ return MapReflectionFriend::MessageMapGetItem(self, key);
+ } else {
+ if (default_value != NULL) {
+ Py_INCREF(default_value);
+ return default_value;
+ } else {
+ Py_RETURN_NONE;
+ }
+ }
+}
+
+static void MessageMapDealloc(PyObject* _self) {
+ MessageMapContainer* self = GetMessageMap(_self);
+ self->owner.reset();
+ Py_DECREF(self->message_dict);
+ Py_DECREF(self->message_class);
+ Py_TYPE(_self)->tp_free(_self);
+}
+
+static PyMethodDef MessageMapMethods[] = {
+ { "__contains__", (PyCFunction)MapReflectionFriend::Contains, METH_O,
+ "Tests whether the map contains this element."},
+ { "clear", (PyCFunction)Clear, METH_NOARGS,
+ "Removes all elements from the map."},
+ { "get", MessageMapGet, METH_VARARGS,
+ "Gets the value for the given key if present, or otherwise a default" },
+ { "get_or_create", MapReflectionFriend::MessageMapGetItem, METH_O,
+ "Alias for getitem, useful to make explicit that the map is mutated." },
+ /*
+ { "__deepcopy__", (PyCFunction)DeepCopy, METH_VARARGS,
+ "Makes a deep copy of the class." },
+ { "__reduce__", (PyCFunction)Reduce, METH_NOARGS,
+ "Outputs picklable representation of the repeated field." },
+ */
+ {NULL, NULL},
+};
+
+#if PY_MAJOR_VERSION >= 3
+ static PyType_Slot MessageMapContainer_Type_slots[] = {
+ {Py_tp_dealloc, (void *)MessageMapDealloc},
+ {Py_mp_length, (void *)MapReflectionFriend::Length},
+ {Py_mp_subscript, (void *)MapReflectionFriend::MessageMapGetItem},
+ {Py_mp_ass_subscript, (void *)MapReflectionFriend::MessageMapSetItem},
+ {Py_tp_methods, (void *)MessageMapMethods},
+ {Py_tp_iter, (void *)MapReflectionFriend::GetIterator},
+ {0, 0}
+ };
+
+ PyType_Spec MessageMapContainer_Type_spec = {
+ FULL_MODULE_NAME ".MessageMapContainer",
+ sizeof(MessageMapContainer),
+ 0,
+ Py_TPFLAGS_DEFAULT,
+ MessageMapContainer_Type_slots
+ };
+
+ PyObject *MessageMapContainer_Type;
+#else
+ static PyMappingMethods MessageMapMappingMethods = {
+ MapReflectionFriend::Length, // mp_length
+ MapReflectionFriend::MessageMapGetItem, // mp_subscript
+ MapReflectionFriend::MessageMapSetItem, // mp_ass_subscript
+ };
+
+ PyTypeObject MessageMapContainer_Type = {
+ PyVarObject_HEAD_INIT(&PyType_Type, 0)
+ FULL_MODULE_NAME ".MessageMapContainer", // tp_name
+ sizeof(MessageMapContainer), // tp_basicsize
+ 0, // tp_itemsize
+ MessageMapDealloc, // tp_dealloc
+ 0, // tp_print
+ 0, // tp_getattr
+ 0, // tp_setattr
+ 0, // tp_compare
+ 0, // tp_repr
+ 0, // tp_as_number
+ 0, // tp_as_sequence
+ &MessageMapMappingMethods, // tp_as_mapping
+ 0, // tp_hash
+ 0, // tp_call
+ 0, // tp_str
+ 0, // tp_getattro
+ 0, // tp_setattro
+ 0, // tp_as_buffer
+ Py_TPFLAGS_DEFAULT, // tp_flags
+ "A map container for message", // tp_doc
+ 0, // tp_traverse
+ 0, // tp_clear
+ 0, // tp_richcompare
+ 0, // tp_weaklistoffset
+ MapReflectionFriend::GetIterator, // tp_iter
+ 0, // tp_iternext
+ MessageMapMethods, // tp_methods
+ 0, // tp_members
+ 0, // tp_getset
+ 0, // tp_base
+ 0, // tp_dict
+ 0, // tp_descr_get
+ 0, // tp_descr_set
+ 0, // tp_dictoffset
+ 0, // tp_init
+ };
+#endif
+
+// MapIterator /////////////////////////////////////////////////////////////////
+
+static MapIterator* GetIter(PyObject* obj) {
+ return reinterpret_cast<MapIterator*>(obj);
+}
+
+PyObject* MapReflectionFriend::GetIterator(PyObject *_self) {
+ MapContainer* self = GetMap(_self);
+
+ ScopedPyObjectPtr obj(PyType_GenericAlloc(&MapIterator_Type, 0));
+ if (obj == NULL) {
+ return PyErr_Format(PyExc_KeyError, "Could not allocate iterator");
+ }
+
+ MapIterator* iter = GetIter(obj.get());
+
+ Py_INCREF(self);
+ iter->container = self;
+ iter->version = self->version;
+ iter->owner = self->owner;
+
+ if (MapReflectionFriend::Length(_self) > 0) {
+ Message* message = self->GetMutableMessage();
+ const Reflection* reflection = message->GetReflection();
+
+ iter->iter.reset(new ::google::protobuf::MapIterator(
+ reflection->MapBegin(message, self->parent_field_descriptor)));
+ }
+
+ return obj.release();
+}
+
+PyObject* MapReflectionFriend::IterNext(PyObject* _self) {
+ MapIterator* self = GetIter(_self);
+
+ // This won't catch mutations to the map performed by MergeFrom(); no easy way
+ // to address that.
+ if (self->version != self->container->version) {
+ return PyErr_Format(PyExc_RuntimeError,
+ "Map modified during iteration.");
+ }
+
+ if (self->iter.get() == NULL) {
+ return NULL;
+ }
+
+ Message* message = self->container->GetMutableMessage();
+ const Reflection* reflection = message->GetReflection();
+
+ if (*self->iter ==
+ reflection->MapEnd(message, self->container->parent_field_descriptor)) {
+ return NULL;
+ }
+
+ PyObject* ret = MapKeyToPython(self->container->key_field_descriptor,
+ self->iter->GetKey());
+
+ ++(*self->iter);
+
+ return ret;
+}
+
+static void DeallocMapIterator(PyObject* _self) {
+ MapIterator* self = GetIter(_self);
+ self->iter.reset();
+ self->owner.reset();
+ Py_XDECREF(self->container);
+ Py_TYPE(_self)->tp_free(_self);
+}
+
+PyTypeObject MapIterator_Type = {
+ PyVarObject_HEAD_INIT(&PyType_Type, 0)
+ FULL_MODULE_NAME ".MapIterator", // tp_name
+ sizeof(MapIterator), // tp_basicsize
+ 0, // tp_itemsize
+ DeallocMapIterator, // tp_dealloc
+ 0, // tp_print
+ 0, // tp_getattr
+ 0, // tp_setattr
+ 0, // tp_compare
+ 0, // tp_repr
+ 0, // tp_as_number
+ 0, // tp_as_sequence
+ 0, // tp_as_mapping
+ 0, // tp_hash
+ 0, // tp_call
+ 0, // tp_str
+ 0, // tp_getattro
+ 0, // tp_setattro
+ 0, // tp_as_buffer
+ Py_TPFLAGS_DEFAULT, // tp_flags
+ "A scalar map iterator", // tp_doc
+ 0, // tp_traverse
+ 0, // tp_clear
+ 0, // tp_richcompare
+ 0, // tp_weaklistoffset
+ PyObject_SelfIter, // tp_iter
+ MapReflectionFriend::IterNext, // tp_iternext
+ 0, // tp_methods
+ 0, // tp_members
+ 0, // tp_getset
+ 0, // tp_base
+ 0, // tp_dict
+ 0, // tp_descr_get
+ 0, // tp_descr_set
+ 0, // tp_dictoffset
+ 0, // tp_init
+};
+
+} // namespace python
+} // namespace protobuf
+} // namespace google
diff --git a/python/google/protobuf/pyext/map_container.h b/python/google/protobuf/pyext/map_container.h
new file mode 100644
index 00000000..fbd6713f
--- /dev/null
+++ b/python/google/protobuf/pyext/map_container.h
@@ -0,0 +1,142 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef GOOGLE_PROTOBUF_PYTHON_CPP_MAP_CONTAINER_H__
+#define GOOGLE_PROTOBUF_PYTHON_CPP_MAP_CONTAINER_H__
+
+#include <Python.h>
+
+#include <memory>
+#ifndef _SHARED_PTR_H
+#include <google/protobuf/stubs/shared_ptr.h>
+#endif
+
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/message.h>
+
+namespace google {
+namespace protobuf {
+
+class Message;
+
+#ifdef _SHARED_PTR_H
+using std::shared_ptr;
+#else
+using internal::shared_ptr;
+#endif
+
+namespace python {
+
+struct CMessage;
+struct CMessageClass;
+
+// This struct is used directly for ScalarMap, and is the base class of
+// MessageMapContainer, which is used for MessageMap.
+struct MapContainer {
+ PyObject_HEAD;
+
+ // This is the top-level C++ Message object that owns the whole
+ // proto tree. Every Python MapContainer holds a
+ // reference to it in order to keep it alive as long as there's a
+ // Python object that references any part of the tree.
+ shared_ptr<Message> owner;
+
+ // Pointer to the C++ Message that contains this container. The
+ // MapContainer does not own this pointer.
+ const Message* message;
+
+ // Use to get a mutable message when necessary.
+ Message* GetMutableMessage();
+
+ // Weak reference to a parent CMessage object (i.e. may be NULL.)
+ //
+ // Used to make sure all ancestors are also mutable when first
+ // modifying the container.
+ CMessage* parent;
+
+ // Pointer to the parent's descriptor that describes this
+ // field. Used together with the parent's message when making a
+ // default message instance mutable.
+ // The pointer is owned by the global DescriptorPool.
+ const FieldDescriptor* parent_field_descriptor;
+ const FieldDescriptor* key_field_descriptor;
+ const FieldDescriptor* value_field_descriptor;
+
+ // We bump this whenever we perform a mutation, to invalidate existing
+ // iterators.
+ uint64 version;
+
+ // Releases the messages in the container to a new message.
+ //
+ // Returns 0 on success, -1 on failure.
+ int Release();
+
+ // Set the owner field of self and any children of self.
+ void SetOwner(const shared_ptr<Message>& new_owner) {
+ owner = new_owner;
+ }
+};
+
+struct MessageMapContainer : public MapContainer {
+ // The type used to create new child messages.
+ CMessageClass* message_class;
+
+ // A dict mapping Message* -> CMessage.
+ PyObject* message_dict;
+};
+
+#if PY_MAJOR_VERSION >= 3
+ extern PyObject *MessageMapContainer_Type;
+ extern PyType_Spec MessageMapContainer_Type_spec;
+ extern PyObject *ScalarMapContainer_Type;
+ extern PyType_Spec ScalarMapContainer_Type_spec;
+#else
+ extern PyTypeObject MessageMapContainer_Type;
+ extern PyTypeObject ScalarMapContainer_Type;
+#endif
+
+extern PyTypeObject MapIterator_Type; // Both map types use the same iterator.
+
+// Builds a MapContainer object, from a parent message and a
+// field descriptor.
+extern PyObject* NewScalarMapContainer(
+ CMessage* parent, const FieldDescriptor* parent_field_descriptor);
+
+// Builds a MessageMap object, from a parent message and a
+// field descriptor.
+extern PyObject* NewMessageMapContainer(
+ CMessage* parent, const FieldDescriptor* parent_field_descriptor,
+ CMessageClass* message_class);
+
+} // namespace python
+} // namespace protobuf
+
+} // namespace google
+#endif // GOOGLE_PROTOBUF_PYTHON_CPP_MAP_CONTAINER_H__
diff --git a/python/google/protobuf/pyext/message.cc b/python/google/protobuf/pyext/message.cc
new file mode 100644
index 00000000..a9261f20
--- /dev/null
+++ b/python/google/protobuf/pyext/message.cc
@@ -0,0 +1,3085 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Author: anuraag@google.com (Anuraag Agrawal)
+// Author: tibell@google.com (Johan Tibell)
+
+#include <google/protobuf/pyext/message.h>
+
+#include <map>
+#include <memory>
+#ifndef _SHARED_PTR_H
+#include <google/protobuf/stubs/shared_ptr.h>
+#endif
+#include <string>
+#include <vector>
+#include <structmember.h> // A Python header file.
+
+#ifndef PyVarObject_HEAD_INIT
+#define PyVarObject_HEAD_INIT(type, size) PyObject_HEAD_INIT(type) size,
+#endif
+#ifndef Py_TYPE
+#define Py_TYPE(ob) (((PyObject*)(ob))->ob_type)
+#endif
+#include <google/protobuf/descriptor.pb.h>
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/logging.h>
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/util/message_differencer.h>
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/message.h>
+#include <google/protobuf/text_format.h>
+#include <google/protobuf/unknown_field_set.h>
+#include <google/protobuf/pyext/descriptor.h>
+#include <google/protobuf/pyext/descriptor_pool.h>
+#include <google/protobuf/pyext/extension_dict.h>
+#include <google/protobuf/pyext/repeated_composite_container.h>
+#include <google/protobuf/pyext/repeated_scalar_container.h>
+#include <google/protobuf/pyext/map_container.h>
+#include <google/protobuf/pyext/scoped_pyobject_ptr.h>
+#include <google/protobuf/stubs/strutil.h>
+
+#if PY_MAJOR_VERSION >= 3
+ #define PyInt_Check PyLong_Check
+ #define PyInt_AsLong PyLong_AsLong
+ #define PyInt_FromLong PyLong_FromLong
+ #define PyInt_FromSize_t PyLong_FromSize_t
+ #define PyString_Check PyUnicode_Check
+ #define PyString_FromString PyUnicode_FromString
+ #define PyString_FromStringAndSize PyUnicode_FromStringAndSize
+ #if PY_VERSION_HEX < 0x03030000
+ #error "Python 3.0 - 3.2 are not supported."
+ #else
+ #define PyString_AsString(ob) \
+ (PyUnicode_Check(ob)? PyUnicode_AsUTF8(ob): PyBytes_AsString(ob))
+ #define PyString_AsStringAndSize(ob, charpp, sizep) \
+ (PyUnicode_Check(ob)? \
+ ((*(charpp) = PyUnicode_AsUTF8AndSize(ob, (sizep))) == NULL? -1: 0): \
+ PyBytes_AsStringAndSize(ob, (charpp), (sizep)))
+ #endif
+#endif
+
+namespace google {
+namespace protobuf {
+namespace python {
+
+static PyObject* kDESCRIPTOR;
+static PyObject* k_extensions_by_name;
+static PyObject* k_extensions_by_number;
+PyObject* EnumTypeWrapper_class;
+static PyObject* PythonMessage_class;
+static PyObject* kEmptyWeakref;
+static PyObject* WKT_classes = NULL;
+
+namespace message_meta {
+
+static int InsertEmptyWeakref(PyTypeObject* base);
+
+// Add the number of a field descriptor to the containing message class.
+// Equivalent to:
+// _cls.<field>_FIELD_NUMBER = <number>
+static bool AddFieldNumberToClass(
+ PyObject* cls, const FieldDescriptor* field_descriptor) {
+ string constant_name = field_descriptor->name() + "_FIELD_NUMBER";
+ UpperString(&constant_name);
+ ScopedPyObjectPtr attr_name(PyString_FromStringAndSize(
+ constant_name.c_str(), constant_name.size()));
+ if (attr_name == NULL) {
+ return false;
+ }
+ ScopedPyObjectPtr number(PyInt_FromLong(field_descriptor->number()));
+ if (number == NULL) {
+ return false;
+ }
+ if (PyObject_SetAttr(cls, attr_name.get(), number.get()) == -1) {
+ return false;
+ }
+ return true;
+}
+
+
+// Finalize the creation of the Message class.
+static int AddDescriptors(PyObject* cls, const Descriptor* descriptor) {
+ // If there are extension_ranges, the message is "extendable", and extension
+ // classes will register themselves in this class.
+ if (descriptor->extension_range_count() > 0) {
+ ScopedPyObjectPtr by_name(PyDict_New());
+ if (PyObject_SetAttr(cls, k_extensions_by_name, by_name.get()) < 0) {
+ return -1;
+ }
+ ScopedPyObjectPtr by_number(PyDict_New());
+ if (PyObject_SetAttr(cls, k_extensions_by_number, by_number.get()) < 0) {
+ return -1;
+ }
+ }
+
+ // For each field set: cls.<field>_FIELD_NUMBER = <number>
+ for (int i = 0; i < descriptor->field_count(); ++i) {
+ if (!AddFieldNumberToClass(cls, descriptor->field(i))) {
+ return -1;
+ }
+ }
+
+ // For each enum set cls.<enum name> = EnumTypeWrapper(<enum descriptor>).
+ for (int i = 0; i < descriptor->enum_type_count(); ++i) {
+ const EnumDescriptor* enum_descriptor = descriptor->enum_type(i);
+ ScopedPyObjectPtr enum_type(
+ PyEnumDescriptor_FromDescriptor(enum_descriptor));
+ if (enum_type == NULL) {
+ return -1;
+ }
+ // Add wrapped enum type to message class.
+ ScopedPyObjectPtr wrapped(PyObject_CallFunctionObjArgs(
+ EnumTypeWrapper_class, enum_type.get(), NULL));
+ if (wrapped == NULL) {
+ return -1;
+ }
+ if (PyObject_SetAttrString(
+ cls, enum_descriptor->name().c_str(), wrapped.get()) == -1) {
+ return -1;
+ }
+
+ // For each enum value add cls.<name> = <number>
+ for (int j = 0; j < enum_descriptor->value_count(); ++j) {
+ const EnumValueDescriptor* enum_value_descriptor =
+ enum_descriptor->value(j);
+ ScopedPyObjectPtr value_number(PyInt_FromLong(
+ enum_value_descriptor->number()));
+ if (value_number == NULL) {
+ return -1;
+ }
+ if (PyObject_SetAttrString(cls, enum_value_descriptor->name().c_str(),
+ value_number.get()) == -1) {
+ return -1;
+ }
+ }
+ }
+
+ // For each extension set cls.<extension name> = <extension descriptor>.
+ //
+ // Extension descriptors come from
+ // <message descriptor>.extensions_by_name[name]
+ // which was defined previously.
+ for (int i = 0; i < descriptor->extension_count(); ++i) {
+ const google::protobuf::FieldDescriptor* field = descriptor->extension(i);
+ ScopedPyObjectPtr extension_field(PyFieldDescriptor_FromDescriptor(field));
+ if (extension_field == NULL) {
+ return -1;
+ }
+
+ // Add the extension field to the message class.
+ if (PyObject_SetAttrString(
+ cls, field->name().c_str(), extension_field.get()) == -1) {
+ return -1;
+ }
+
+ // For each extension set cls.<extension name>_FIELD_NUMBER = <number>.
+ if (!AddFieldNumberToClass(cls, field)) {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static PyObject* New(PyTypeObject* type,
+ PyObject* args, PyObject* kwargs) {
+ static char *kwlist[] = {"name", "bases", "dict", 0};
+ PyObject *bases, *dict;
+ const char* name;
+
+ // Check arguments: (name, bases, dict)
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sO!O!:type", kwlist,
+ &name,
+ &PyTuple_Type, &bases,
+ &PyDict_Type, &dict)) {
+ return NULL;
+ }
+
+ // Check bases: only (), or (message.Message,) are allowed
+ if (!(PyTuple_GET_SIZE(bases) == 0 ||
+ (PyTuple_GET_SIZE(bases) == 1 &&
+ PyTuple_GET_ITEM(bases, 0) == PythonMessage_class))) {
+ PyErr_SetString(PyExc_TypeError,
+ "A Message class can only inherit from Message");
+ return NULL;
+ }
+
+ // Check dict['DESCRIPTOR']
+ PyObject* py_descriptor = PyDict_GetItem(dict, kDESCRIPTOR);
+ if (py_descriptor == NULL) {
+ PyErr_SetString(PyExc_TypeError, "Message class has no DESCRIPTOR");
+ return NULL;
+ }
+ if (!PyObject_TypeCheck(py_descriptor, &PyMessageDescriptor_Type)) {
+ PyErr_Format(PyExc_TypeError, "Expected a message Descriptor, got %s",
+ py_descriptor->ob_type->tp_name);
+ return NULL;
+ }
+
+ // Build the arguments to the base metaclass.
+ // We change the __bases__ classes.
+ ScopedPyObjectPtr new_args;
+ const Descriptor* message_descriptor =
+ PyMessageDescriptor_AsDescriptor(py_descriptor);
+ if (message_descriptor == NULL) {
+ return NULL;
+ }
+
+ if (WKT_classes == NULL) {
+ ScopedPyObjectPtr well_known_types(PyImport_ImportModule(
+ "google.protobuf.internal.well_known_types"));
+ GOOGLE_DCHECK(well_known_types != NULL);
+
+ WKT_classes = PyObject_GetAttrString(well_known_types.get(), "WKTBASES");
+ GOOGLE_DCHECK(WKT_classes != NULL);
+ }
+
+ PyObject* well_known_class = PyDict_GetItemString(
+ WKT_classes, message_descriptor->full_name().c_str());
+ if (well_known_class == NULL) {
+ new_args.reset(Py_BuildValue("s(OO)O", name, &CMessage_Type,
+ PythonMessage_class, dict));
+ } else {
+ new_args.reset(Py_BuildValue("s(OOO)O", name, &CMessage_Type,
+ PythonMessage_class, well_known_class, dict));
+ }
+
+ if (new_args == NULL) {
+ return NULL;
+ }
+ // Call the base metaclass.
+ ScopedPyObjectPtr result(PyType_Type.tp_new(type, new_args.get(), NULL));
+ if (result == NULL) {
+ return NULL;
+ }
+ CMessageClass* newtype = reinterpret_cast<CMessageClass*>(result.get());
+
+ // Insert the empty weakref into the base classes.
+ if (InsertEmptyWeakref(
+ reinterpret_cast<PyTypeObject*>(PythonMessage_class)) < 0 ||
+ InsertEmptyWeakref(&CMessage_Type) < 0) {
+ return NULL;
+ }
+
+ // Cache the descriptor, both as Python object and as C++ pointer.
+ const Descriptor* descriptor =
+ PyMessageDescriptor_AsDescriptor(py_descriptor);
+ if (descriptor == NULL) {
+ return NULL;
+ }
+ Py_INCREF(py_descriptor);
+ newtype->py_message_descriptor = py_descriptor;
+ newtype->message_descriptor = descriptor;
+ // TODO(amauryfa): Don't always use the canonical pool of the descriptor,
+ // use the MessageFactory optionally passed in the class dict.
+ newtype->py_descriptor_pool = GetDescriptorPool_FromPool(
+ descriptor->file()->pool());
+ if (newtype->py_descriptor_pool == NULL) {
+ return NULL;
+ }
+ Py_INCREF(newtype->py_descriptor_pool);
+
+ // Add the message to the DescriptorPool.
+ if (cdescriptor_pool::RegisterMessageClass(newtype->py_descriptor_pool,
+ descriptor, newtype) < 0) {
+ return NULL;
+ }
+
+ // Continue with type initialization: add other descriptors, enum values...
+ if (AddDescriptors(result.get(), descriptor) < 0) {
+ return NULL;
+ }
+ return result.release();
+}
+
+static void Dealloc(CMessageClass *self) {
+ Py_DECREF(self->py_message_descriptor);
+ Py_DECREF(self->py_descriptor_pool);
+ Py_TYPE(self)->tp_free(reinterpret_cast<PyObject*>(self));
+}
+
+
+// This function inserts and empty weakref at the end of the list of
+// subclasses for the main protocol buffer Message class.
+//
+// This eliminates a O(n^2) behaviour in the internal add_subclass
+// routine.
+static int InsertEmptyWeakref(PyTypeObject *base_type) {
+#if PY_MAJOR_VERSION >= 3
+ // Python 3.4 has already included the fix for the issue that this
+ // hack addresses. For further background and the fix please see
+ // https://bugs.python.org/issue17936.
+ return 0;
+#else
+ PyObject *subclasses = base_type->tp_subclasses;
+ if (subclasses && PyList_CheckExact(subclasses)) {
+ return PyList_Append(subclasses, kEmptyWeakref);
+ }
+ return 0;
+#endif // PY_MAJOR_VERSION >= 3
+}
+
+} // namespace message_meta
+
+PyTypeObject CMessageClass_Type = {
+ PyVarObject_HEAD_INIT(&PyType_Type, 0)
+ FULL_MODULE_NAME ".MessageMeta", // tp_name
+ sizeof(CMessageClass), // tp_basicsize
+ 0, // tp_itemsize
+ (destructor)message_meta::Dealloc, // tp_dealloc
+ 0, // tp_print
+ 0, // tp_getattr
+ 0, // tp_setattr
+ 0, // tp_compare
+ 0, // tp_repr
+ 0, // tp_as_number
+ 0, // tp_as_sequence
+ 0, // tp_as_mapping
+ 0, // tp_hash
+ 0, // tp_call
+ 0, // tp_str
+ 0, // tp_getattro
+ 0, // tp_setattro
+ 0, // tp_as_buffer
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, // tp_flags
+ "The metaclass of ProtocolMessages", // tp_doc
+ 0, // tp_traverse
+ 0, // tp_clear
+ 0, // tp_richcompare
+ 0, // tp_weaklistoffset
+ 0, // tp_iter
+ 0, // tp_iternext
+ 0, // tp_methods
+ 0, // tp_members
+ 0, // tp_getset
+ 0, // tp_base
+ 0, // tp_dict
+ 0, // tp_descr_get
+ 0, // tp_descr_set
+ 0, // tp_dictoffset
+ 0, // tp_init
+ 0, // tp_alloc
+ message_meta::New, // tp_new
+};
+
+static CMessageClass* CheckMessageClass(PyTypeObject* cls) {
+ if (!PyObject_TypeCheck(cls, &CMessageClass_Type)) {
+ PyErr_Format(PyExc_TypeError, "Class %s is not a Message", cls->tp_name);
+ return NULL;
+ }
+ return reinterpret_cast<CMessageClass*>(cls);
+}
+
+static const Descriptor* GetMessageDescriptor(PyTypeObject* cls) {
+ CMessageClass* type = CheckMessageClass(cls);
+ if (type == NULL) {
+ return NULL;
+ }
+ return type->message_descriptor;
+}
+
+// Forward declarations
+namespace cmessage {
+int InternalReleaseFieldByDescriptor(
+ CMessage* self,
+ const FieldDescriptor* field_descriptor,
+ PyObject* composite_field);
+} // namespace cmessage
+
+// ---------------------------------------------------------------------
+// Visiting the composite children of a CMessage
+
+struct ChildVisitor {
+ // Returns 0 on success, -1 on failure.
+ int VisitRepeatedCompositeContainer(RepeatedCompositeContainer* container) {
+ return 0;
+ }
+
+ // Returns 0 on success, -1 on failure.
+ int VisitRepeatedScalarContainer(RepeatedScalarContainer* container) {
+ return 0;
+ }
+
+ // Returns 0 on success, -1 on failure.
+ int VisitCMessage(CMessage* cmessage,
+ const FieldDescriptor* field_descriptor) {
+ return 0;
+ }
+};
+
+// Apply a function to a composite field. Does nothing if child is of
+// non-composite type.
+template<class Visitor>
+static int VisitCompositeField(const FieldDescriptor* descriptor,
+ PyObject* child,
+ Visitor visitor) {
+ if (descriptor->label() == FieldDescriptor::LABEL_REPEATED) {
+ if (descriptor->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
+ if (descriptor->is_map()) {
+ MapContainer* container = reinterpret_cast<MapContainer*>(child);
+ if (visitor.VisitMapContainer(container) == -1) {
+ return -1;
+ }
+ } else {
+ RepeatedCompositeContainer* container =
+ reinterpret_cast<RepeatedCompositeContainer*>(child);
+ if (visitor.VisitRepeatedCompositeContainer(container) == -1)
+ return -1;
+ }
+ } else {
+ RepeatedScalarContainer* container =
+ reinterpret_cast<RepeatedScalarContainer*>(child);
+ if (visitor.VisitRepeatedScalarContainer(container) == -1)
+ return -1;
+ }
+ } else if (descriptor->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
+ CMessage* cmsg = reinterpret_cast<CMessage*>(child);
+ if (visitor.VisitCMessage(cmsg, descriptor) == -1)
+ return -1;
+ }
+ // The ExtensionDict might contain non-composite fields, which we
+ // skip here.
+ return 0;
+}
+
+// Visit each composite field and extension field of this CMessage.
+// Returns -1 on error and 0 on success.
+template<class Visitor>
+int ForEachCompositeField(CMessage* self, Visitor visitor) {
+ Py_ssize_t pos = 0;
+ PyObject* key;
+ PyObject* field;
+
+ // Visit normal fields.
+ if (self->composite_fields) {
+ // Never use self->message in this function, it may be already freed.
+ const Descriptor* message_descriptor =
+ GetMessageDescriptor(Py_TYPE(self));
+ while (PyDict_Next(self->composite_fields, &pos, &key, &field)) {
+ Py_ssize_t key_str_size;
+ char *key_str_data;
+ if (PyString_AsStringAndSize(key, &key_str_data, &key_str_size) != 0)
+ return -1;
+ const string key_str(key_str_data, key_str_size);
+ const FieldDescriptor* descriptor =
+ message_descriptor->FindFieldByName(key_str);
+ if (descriptor != NULL) {
+ if (VisitCompositeField(descriptor, field, visitor) == -1)
+ return -1;
+ }
+ }
+ }
+
+ // Visit extension fields.
+ if (self->extensions != NULL) {
+ pos = 0;
+ while (PyDict_Next(self->extensions->values, &pos, &key, &field)) {
+ const FieldDescriptor* descriptor = cmessage::GetExtensionDescriptor(key);
+ if (descriptor == NULL)
+ return -1;
+ if (VisitCompositeField(descriptor, field, visitor) == -1)
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+// ---------------------------------------------------------------------
+
+// Constants used for integer type range checking.
+PyObject* kPythonZero;
+PyObject* kint32min_py;
+PyObject* kint32max_py;
+PyObject* kuint32max_py;
+PyObject* kint64min_py;
+PyObject* kint64max_py;
+PyObject* kuint64max_py;
+
+PyObject* EncodeError_class;
+PyObject* DecodeError_class;
+PyObject* PickleError_class;
+
+// Constant PyString values used for GetAttr/GetItem.
+static PyObject* k_cdescriptor;
+static PyObject* kfull_name;
+
+/* Is 64bit */
+void FormatTypeError(PyObject* arg, char* expected_types) {
+ PyObject* repr = PyObject_Repr(arg);
+ if (repr) {
+ PyErr_Format(PyExc_TypeError,
+ "%.100s has type %.100s, but expected one of: %s",
+ PyString_AsString(repr),
+ Py_TYPE(arg)->tp_name,
+ expected_types);
+ Py_DECREF(repr);
+ }
+}
+
+template<class T>
+bool CheckAndGetInteger(
+ PyObject* arg, T* value, PyObject* min, PyObject* max) {
+ bool is_long = PyLong_Check(arg);
+#if PY_MAJOR_VERSION < 3
+ if (!PyInt_Check(arg) && !is_long) {
+ FormatTypeError(arg, "int, long");
+ return false;
+ }
+ if (PyObject_Compare(min, arg) > 0 || PyObject_Compare(max, arg) < 0) {
+#else
+ if (!is_long) {
+ FormatTypeError(arg, "int");
+ return false;
+ }
+ if (PyObject_RichCompareBool(min, arg, Py_LE) != 1 ||
+ PyObject_RichCompareBool(max, arg, Py_GE) != 1) {
+#endif
+ if (!PyErr_Occurred()) {
+ PyObject *s = PyObject_Str(arg);
+ if (s) {
+ PyErr_Format(PyExc_ValueError,
+ "Value out of range: %s",
+ PyString_AsString(s));
+ Py_DECREF(s);
+ }
+ }
+ return false;
+ }
+#if PY_MAJOR_VERSION < 3
+ if (!is_long) {
+ *value = static_cast<T>(PyInt_AsLong(arg));
+ } else // NOLINT
+#endif
+ {
+ if (min == kPythonZero) {
+ *value = static_cast<T>(PyLong_AsUnsignedLongLong(arg));
+ } else {
+ *value = static_cast<T>(PyLong_AsLongLong(arg));
+ }
+ }
+ return true;
+}
+
+// These are referenced by repeated_scalar_container, and must
+// be explicitly instantiated.
+template bool CheckAndGetInteger<int32>(
+ PyObject*, int32*, PyObject*, PyObject*);
+template bool CheckAndGetInteger<int64>(
+ PyObject*, int64*, PyObject*, PyObject*);
+template bool CheckAndGetInteger<uint32>(
+ PyObject*, uint32*, PyObject*, PyObject*);
+template bool CheckAndGetInteger<uint64>(
+ PyObject*, uint64*, PyObject*, PyObject*);
+
+bool CheckAndGetDouble(PyObject* arg, double* value) {
+ if (!PyInt_Check(arg) && !PyLong_Check(arg) &&
+ !PyFloat_Check(arg)) {
+ FormatTypeError(arg, "int, long, float");
+ return false;
+ }
+ *value = PyFloat_AsDouble(arg);
+ return true;
+}
+
+bool CheckAndGetFloat(PyObject* arg, float* value) {
+ double double_value;
+ if (!CheckAndGetDouble(arg, &double_value)) {
+ return false;
+ }
+ *value = static_cast<float>(double_value);
+ return true;
+}
+
+bool CheckAndGetBool(PyObject* arg, bool* value) {
+ if (!PyInt_Check(arg) && !PyBool_Check(arg) && !PyLong_Check(arg)) {
+ FormatTypeError(arg, "int, long, bool");
+ return false;
+ }
+ *value = static_cast<bool>(PyInt_AsLong(arg));
+ return true;
+}
+
+// Checks whether the given object (which must be "bytes" or "unicode") contains
+// valid UTF-8.
+bool IsValidUTF8(PyObject* obj) {
+ if (PyBytes_Check(obj)) {
+ PyObject* unicode = PyUnicode_FromEncodedObject(obj, "utf-8", NULL);
+
+ // Clear the error indicator; we report our own error when desired.
+ PyErr_Clear();
+
+ if (unicode) {
+ Py_DECREF(unicode);
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ // Unicode object, known to be valid UTF-8.
+ return true;
+ }
+}
+
+bool AllowInvalidUTF8(const FieldDescriptor* field) { return false; }
+
+PyObject* CheckString(PyObject* arg, const FieldDescriptor* descriptor) {
+ GOOGLE_DCHECK(descriptor->type() == FieldDescriptor::TYPE_STRING ||
+ descriptor->type() == FieldDescriptor::TYPE_BYTES);
+ if (descriptor->type() == FieldDescriptor::TYPE_STRING) {
+ if (!PyBytes_Check(arg) && !PyUnicode_Check(arg)) {
+ FormatTypeError(arg, "bytes, unicode");
+ return NULL;
+ }
+
+ if (!IsValidUTF8(arg) && !AllowInvalidUTF8(descriptor)) {
+ PyObject* repr = PyObject_Repr(arg);
+ PyErr_Format(PyExc_ValueError,
+ "%s has type str, but isn't valid UTF-8 "
+ "encoding. Non-UTF-8 strings must be converted to "
+ "unicode objects before being added.",
+ PyString_AsString(repr));
+ Py_DECREF(repr);
+ return NULL;
+ }
+ } else if (!PyBytes_Check(arg)) {
+ FormatTypeError(arg, "bytes");
+ return NULL;
+ }
+
+ PyObject* encoded_string = NULL;
+ if (descriptor->type() == FieldDescriptor::TYPE_STRING) {
+ if (PyBytes_Check(arg)) {
+ // The bytes were already validated as correctly encoded UTF-8 above.
+ encoded_string = arg; // Already encoded.
+ Py_INCREF(encoded_string);
+ } else {
+ encoded_string = PyUnicode_AsEncodedObject(arg, "utf-8", NULL);
+ }
+ } else {
+ // In this case field type is "bytes".
+ encoded_string = arg;
+ Py_INCREF(encoded_string);
+ }
+
+ return encoded_string;
+}
+
+bool CheckAndSetString(
+ PyObject* arg, Message* message,
+ const FieldDescriptor* descriptor,
+ const Reflection* reflection,
+ bool append,
+ int index) {
+ ScopedPyObjectPtr encoded_string(CheckString(arg, descriptor));
+
+ if (encoded_string.get() == NULL) {
+ return false;
+ }
+
+ char* value;
+ Py_ssize_t value_len;
+ if (PyBytes_AsStringAndSize(encoded_string.get(), &value, &value_len) < 0) {
+ return false;
+ }
+
+ string value_string(value, value_len);
+ if (append) {
+ reflection->AddString(message, descriptor, value_string);
+ } else if (index < 0) {
+ reflection->SetString(message, descriptor, value_string);
+ } else {
+ reflection->SetRepeatedString(message, descriptor, index, value_string);
+ }
+ return true;
+}
+
+PyObject* ToStringObject(const FieldDescriptor* descriptor, string value) {
+ if (descriptor->type() != FieldDescriptor::TYPE_STRING) {
+ return PyBytes_FromStringAndSize(value.c_str(), value.length());
+ }
+
+ PyObject* result = PyUnicode_DecodeUTF8(value.c_str(), value.length(), NULL);
+ // If the string can't be decoded in UTF-8, just return a string object that
+ // contains the raw bytes. This can't happen if the value was assigned using
+ // the members of the Python message object, but can happen if the values were
+ // parsed from the wire (binary).
+ if (result == NULL) {
+ PyErr_Clear();
+ result = PyBytes_FromStringAndSize(value.c_str(), value.length());
+ }
+ return result;
+}
+
+bool CheckFieldBelongsToMessage(const FieldDescriptor* field_descriptor,
+ const Message* message) {
+ if (message->GetDescriptor() == field_descriptor->containing_type()) {
+ return true;
+ }
+ PyErr_Format(PyExc_KeyError, "Field '%s' does not belong to message '%s'",
+ field_descriptor->full_name().c_str(),
+ message->GetDescriptor()->full_name().c_str());
+ return false;
+}
+
+namespace cmessage {
+
+PyDescriptorPool* GetDescriptorPoolForMessage(CMessage* message) {
+ // No need to check the type: the type of instances of CMessage is always
+ // an instance of CMessageClass. Let's prove it with a debug-only check.
+ GOOGLE_DCHECK(PyObject_TypeCheck(message, &CMessage_Type));
+ return reinterpret_cast<CMessageClass*>(Py_TYPE(message))->py_descriptor_pool;
+}
+
+MessageFactory* GetFactoryForMessage(CMessage* message) {
+ return GetDescriptorPoolForMessage(message)->message_factory;
+}
+
+static int MaybeReleaseOverlappingOneofField(
+ CMessage* cmessage,
+ const FieldDescriptor* field) {
+#ifdef GOOGLE_PROTOBUF_HAS_ONEOF
+ Message* message = cmessage->message;
+ const Reflection* reflection = message->GetReflection();
+ if (!field->containing_oneof() ||
+ !reflection->HasOneof(*message, field->containing_oneof()) ||
+ reflection->HasField(*message, field)) {
+ // No other field in this oneof, no need to release.
+ return 0;
+ }
+
+ const OneofDescriptor* oneof = field->containing_oneof();
+ const FieldDescriptor* existing_field =
+ reflection->GetOneofFieldDescriptor(*message, oneof);
+ if (existing_field->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE) {
+ // Non-message fields don't need to be released.
+ return 0;
+ }
+ const char* field_name = existing_field->name().c_str();
+ PyObject* child_message = cmessage->composite_fields ?
+ PyDict_GetItemString(cmessage->composite_fields, field_name) : NULL;
+ if (child_message == NULL) {
+ // No python reference to this field so no need to release.
+ return 0;
+ }
+
+ if (InternalReleaseFieldByDescriptor(
+ cmessage, existing_field, child_message) < 0) {
+ return -1;
+ }
+ return PyDict_DelItemString(cmessage->composite_fields, field_name);
+#else
+ return 0;
+#endif
+}
+
+// ---------------------------------------------------------------------
+// Making a message writable
+
+static Message* GetMutableMessage(
+ CMessage* parent,
+ const FieldDescriptor* parent_field) {
+ Message* parent_message = parent->message;
+ const Reflection* reflection = parent_message->GetReflection();
+ if (MaybeReleaseOverlappingOneofField(parent, parent_field) < 0) {
+ return NULL;
+ }
+ return reflection->MutableMessage(
+ parent_message, parent_field, GetFactoryForMessage(parent));
+}
+
+struct FixupMessageReference : public ChildVisitor {
+ // message must outlive this object.
+ explicit FixupMessageReference(Message* message) :
+ message_(message) {}
+
+ int VisitRepeatedCompositeContainer(RepeatedCompositeContainer* container) {
+ container->message = message_;
+ return 0;
+ }
+
+ int VisitRepeatedScalarContainer(RepeatedScalarContainer* container) {
+ container->message = message_;
+ return 0;
+ }
+
+ int VisitMapContainer(MapContainer* container) {
+ container->message = message_;
+ return 0;
+ }
+
+ private:
+ Message* message_;
+};
+
+int AssureWritable(CMessage* self) {
+ if (self == NULL || !self->read_only) {
+ return 0;
+ }
+
+ if (self->parent == NULL) {
+ // If parent is NULL but we are trying to modify a read-only message, this
+ // is a reference to a constant default instance that needs to be replaced
+ // with a mutable top-level message.
+ self->message = self->message->New();
+ self->owner.reset(self->message);
+ // Cascade the new owner to eventual children: even if this message is
+ // empty, some submessages or repeated containers might exist already.
+ SetOwner(self, self->owner);
+ } else {
+ // Otherwise, we need a mutable child message.
+ if (AssureWritable(self->parent) == -1)
+ return -1;
+
+ // Make self->message writable.
+ Message* mutable_message = GetMutableMessage(
+ self->parent,
+ self->parent_field_descriptor);
+ if (mutable_message == NULL) {
+ return -1;
+ }
+ self->message = mutable_message;
+ }
+ self->read_only = false;
+
+ // When a CMessage is made writable its Message pointer is updated
+ // to point to a new mutable Message. When that happens we need to
+ // update any references to the old, read-only CMessage. There are
+ // four places such references occur: RepeatedScalarContainer,
+ // RepeatedCompositeContainer, MapContainer, and ExtensionDict.
+ if (self->extensions != NULL)
+ self->extensions->message = self->message;
+ if (ForEachCompositeField(self, FixupMessageReference(self->message)) == -1)
+ return -1;
+
+ return 0;
+}
+
+// --- Globals:
+
+// Retrieve a C++ FieldDescriptor for a message attribute.
+// The C++ message must be valid.
+// TODO(amauryfa): This function should stay internal, because exception
+// handling is not consistent.
+static const FieldDescriptor* GetFieldDescriptor(
+ CMessage* self, PyObject* name) {
+ const Descriptor *message_descriptor = self->message->GetDescriptor();
+ char* field_name;
+ Py_ssize_t size;
+ if (PyString_AsStringAndSize(name, &field_name, &size) < 0) {
+ return NULL;
+ }
+ const FieldDescriptor *field_descriptor =
+ message_descriptor->FindFieldByName(string(field_name, size));
+ if (field_descriptor == NULL) {
+ // Note: No exception is set!
+ return NULL;
+ }
+ return field_descriptor;
+}
+
+// Retrieve a C++ FieldDescriptor for an extension handle.
+const FieldDescriptor* GetExtensionDescriptor(PyObject* extension) {
+ ScopedPyObjectPtr cdescriptor;
+ if (!PyObject_TypeCheck(extension, &PyFieldDescriptor_Type)) {
+ // Most callers consider extensions as a plain dictionary. We should
+ // allow input which is not a field descriptor, and simply pretend it does
+ // not exist.
+ PyErr_SetObject(PyExc_KeyError, extension);
+ return NULL;
+ }
+ return PyFieldDescriptor_AsDescriptor(extension);
+}
+
+// If value is a string, convert it into an enum value based on the labels in
+// descriptor, otherwise simply return value. Always returns a new reference.
+static PyObject* GetIntegerEnumValue(const FieldDescriptor& descriptor,
+ PyObject* value) {
+ if (PyString_Check(value) || PyUnicode_Check(value)) {
+ const EnumDescriptor* enum_descriptor = descriptor.enum_type();
+ if (enum_descriptor == NULL) {
+ PyErr_SetString(PyExc_TypeError, "not an enum field");
+ return NULL;
+ }
+ char* enum_label;
+ Py_ssize_t size;
+ if (PyString_AsStringAndSize(value, &enum_label, &size) < 0) {
+ return NULL;
+ }
+ const EnumValueDescriptor* enum_value_descriptor =
+ enum_descriptor->FindValueByName(string(enum_label, size));
+ if (enum_value_descriptor == NULL) {
+ PyErr_SetString(PyExc_ValueError, "unknown enum label");
+ return NULL;
+ }
+ return PyInt_FromLong(enum_value_descriptor->number());
+ }
+ Py_INCREF(value);
+ return value;
+}
+
+// If cmessage_list is not NULL, this function releases values into the
+// container CMessages instead of just removing. Repeated composite container
+// needs to do this to make sure CMessages stay alive if they're still
+// referenced after deletion. Repeated scalar container doesn't need to worry.
+int InternalDeleteRepeatedField(
+ CMessage* self,
+ const FieldDescriptor* field_descriptor,
+ PyObject* slice,
+ PyObject* cmessage_list) {
+ Message* message = self->message;
+ Py_ssize_t length, from, to, step, slice_length;
+ const Reflection* reflection = message->GetReflection();
+ int min, max;
+ length = reflection->FieldSize(*message, field_descriptor);
+
+ if (PyInt_Check(slice) || PyLong_Check(slice)) {
+ from = to = PyLong_AsLong(slice);
+ if (from < 0) {
+ from = to = length + from;
+ }
+ step = 1;
+ min = max = from;
+
+ // Range check.
+ if (from < 0 || from >= length) {
+ PyErr_Format(PyExc_IndexError, "list assignment index out of range");
+ return -1;
+ }
+ } else if (PySlice_Check(slice)) {
+ from = to = step = slice_length = 0;
+ PySlice_GetIndicesEx(
+#if PY_MAJOR_VERSION < 3
+ reinterpret_cast<PySliceObject*>(slice),
+#else
+ slice,
+#endif
+ length, &from, &to, &step, &slice_length);
+ if (from < to) {
+ min = from;
+ max = to - 1;
+ } else {
+ min = to + 1;
+ max = from;
+ }
+ } else {
+ PyErr_SetString(PyExc_TypeError, "list indices must be integers");
+ return -1;
+ }
+
+ Py_ssize_t i = from;
+ std::vector<bool> to_delete(length, false);
+ while (i >= min && i <= max) {
+ to_delete[i] = true;
+ i += step;
+ }
+
+ to = 0;
+ for (i = 0; i < length; ++i) {
+ if (!to_delete[i]) {
+ if (i != to) {
+ reflection->SwapElements(message, field_descriptor, i, to);
+ if (cmessage_list != NULL) {
+ // If a list of cmessages is passed in (i.e. from a repeated
+ // composite container), swap those as well to correspond to the
+ // swaps in the underlying message so they're in the right order
+ // when we start releasing.
+ PyObject* tmp = PyList_GET_ITEM(cmessage_list, i);
+ PyList_SET_ITEM(cmessage_list, i,
+ PyList_GET_ITEM(cmessage_list, to));
+ PyList_SET_ITEM(cmessage_list, to, tmp);
+ }
+ }
+ ++to;
+ }
+ }
+
+ while (i > to) {
+ if (cmessage_list == NULL) {
+ reflection->RemoveLast(message, field_descriptor);
+ } else {
+ CMessage* last_cmessage = reinterpret_cast<CMessage*>(
+ PyList_GET_ITEM(cmessage_list, PyList_GET_SIZE(cmessage_list) - 1));
+ repeated_composite_container::ReleaseLastTo(
+ self, field_descriptor, last_cmessage);
+ if (PySequence_DelItem(cmessage_list, -1) < 0) {
+ return -1;
+ }
+ }
+ --i;
+ }
+
+ return 0;
+}
+
+// Initializes fields of a message. Used in constructors.
+int InitAttributes(CMessage* self, PyObject* kwargs) {
+ if (kwargs == NULL) {
+ return 0;
+ }
+
+ Py_ssize_t pos = 0;
+ PyObject* name;
+ PyObject* value;
+ while (PyDict_Next(kwargs, &pos, &name, &value)) {
+ if (!PyString_Check(name)) {
+ PyErr_SetString(PyExc_ValueError, "Field name must be a string");
+ return -1;
+ }
+ const FieldDescriptor* descriptor = GetFieldDescriptor(self, name);
+ if (descriptor == NULL) {
+ PyErr_Format(PyExc_ValueError, "Protocol message %s has no \"%s\" field.",
+ self->message->GetDescriptor()->name().c_str(),
+ PyString_AsString(name));
+ return -1;
+ }
+ if (value == Py_None) {
+ // field=None is the same as no field at all.
+ continue;
+ }
+ if (descriptor->is_map()) {
+ ScopedPyObjectPtr map(GetAttr(self, name));
+ const FieldDescriptor* value_descriptor =
+ descriptor->message_type()->FindFieldByName("value");
+ if (value_descriptor->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
+ Py_ssize_t map_pos = 0;
+ PyObject* map_key;
+ PyObject* map_value;
+ while (PyDict_Next(value, &map_pos, &map_key, &map_value)) {
+ ScopedPyObjectPtr function_return;
+ function_return.reset(PyObject_GetItem(map.get(), map_key));
+ if (function_return.get() == NULL) {
+ return -1;
+ }
+ ScopedPyObjectPtr ok(PyObject_CallMethod(
+ function_return.get(), "MergeFrom", "O", map_value));
+ if (ok.get() == NULL) {
+ return -1;
+ }
+ }
+ } else {
+ ScopedPyObjectPtr function_return;
+ function_return.reset(
+ PyObject_CallMethod(map.get(), "update", "O", value));
+ if (function_return.get() == NULL) {
+ return -1;
+ }
+ }
+ } else if (descriptor->label() == FieldDescriptor::LABEL_REPEATED) {
+ ScopedPyObjectPtr container(GetAttr(self, name));
+ if (container == NULL) {
+ return -1;
+ }
+ if (descriptor->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
+ RepeatedCompositeContainer* rc_container =
+ reinterpret_cast<RepeatedCompositeContainer*>(container.get());
+ ScopedPyObjectPtr iter(PyObject_GetIter(value));
+ if (iter == NULL) {
+ PyErr_SetString(PyExc_TypeError, "Value must be iterable");
+ return -1;
+ }
+ ScopedPyObjectPtr next;
+ while ((next.reset(PyIter_Next(iter.get()))) != NULL) {
+ PyObject* kwargs = (PyDict_Check(next.get()) ? next.get() : NULL);
+ ScopedPyObjectPtr new_msg(
+ repeated_composite_container::Add(rc_container, NULL, kwargs));
+ if (new_msg == NULL) {
+ return -1;
+ }
+ if (kwargs == NULL) {
+ // next was not a dict, it's a message we need to merge
+ ScopedPyObjectPtr merged(MergeFrom(
+ reinterpret_cast<CMessage*>(new_msg.get()), next.get()));
+ if (merged.get() == NULL) {
+ return -1;
+ }
+ }
+ }
+ if (PyErr_Occurred()) {
+ // Check to see how PyIter_Next() exited.
+ return -1;
+ }
+ } else if (descriptor->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) {
+ RepeatedScalarContainer* rs_container =
+ reinterpret_cast<RepeatedScalarContainer*>(container.get());
+ ScopedPyObjectPtr iter(PyObject_GetIter(value));
+ if (iter == NULL) {
+ PyErr_SetString(PyExc_TypeError, "Value must be iterable");
+ return -1;
+ }
+ ScopedPyObjectPtr next;
+ while ((next.reset(PyIter_Next(iter.get()))) != NULL) {
+ ScopedPyObjectPtr enum_value(
+ GetIntegerEnumValue(*descriptor, next.get()));
+ if (enum_value == NULL) {
+ return -1;
+ }
+ ScopedPyObjectPtr new_msg(repeated_scalar_container::Append(
+ rs_container, enum_value.get()));
+ if (new_msg == NULL) {
+ return -1;
+ }
+ }
+ if (PyErr_Occurred()) {
+ // Check to see how PyIter_Next() exited.
+ return -1;
+ }
+ } else {
+ if (ScopedPyObjectPtr(repeated_scalar_container::Extend(
+ reinterpret_cast<RepeatedScalarContainer*>(container.get()),
+ value)) ==
+ NULL) {
+ return -1;
+ }
+ }
+ } else if (descriptor->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
+ ScopedPyObjectPtr message(GetAttr(self, name));
+ if (message == NULL) {
+ return -1;
+ }
+ CMessage* cmessage = reinterpret_cast<CMessage*>(message.get());
+ if (PyDict_Check(value)) {
+ if (InitAttributes(cmessage, value) < 0) {
+ return -1;
+ }
+ } else {
+ ScopedPyObjectPtr merged(MergeFrom(cmessage, value));
+ if (merged == NULL) {
+ return -1;
+ }
+ }
+ } else {
+ ScopedPyObjectPtr new_val;
+ if (descriptor->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) {
+ new_val.reset(GetIntegerEnumValue(*descriptor, value));
+ if (new_val == NULL) {
+ return -1;
+ }
+ }
+ if (SetAttr(self, name, (new_val.get() == NULL) ? value : new_val.get()) <
+ 0) {
+ return -1;
+ }
+ }
+ }
+ return 0;
+}
+
+// Allocates an incomplete Python Message: the caller must fill self->message,
+// self->owner and eventually self->parent.
+CMessage* NewEmptyMessage(CMessageClass* type) {
+ CMessage* self = reinterpret_cast<CMessage*>(
+ PyType_GenericAlloc(&type->super.ht_type, 0));
+ if (self == NULL) {
+ return NULL;
+ }
+
+ self->message = NULL;
+ self->parent = NULL;
+ self->parent_field_descriptor = NULL;
+ self->read_only = false;
+ self->extensions = NULL;
+
+ self->composite_fields = NULL;
+
+ return self;
+}
+
+// The __new__ method of Message classes.
+// Creates a new C++ message and takes ownership.
+static PyObject* New(PyTypeObject* cls,
+ PyObject* unused_args, PyObject* unused_kwargs) {
+ CMessageClass* type = CheckMessageClass(cls);
+ if (type == NULL) {
+ return NULL;
+ }
+ // Retrieve the message descriptor and the default instance (=prototype).
+ const Descriptor* message_descriptor = type->message_descriptor;
+ if (message_descriptor == NULL) {
+ return NULL;
+ }
+ const Message* default_message = type->py_descriptor_pool->message_factory
+ ->GetPrototype(message_descriptor);
+ if (default_message == NULL) {
+ PyErr_SetString(PyExc_TypeError, message_descriptor->full_name().c_str());
+ return NULL;
+ }
+
+ CMessage* self = NewEmptyMessage(type);
+ if (self == NULL) {
+ return NULL;
+ }
+ self->message = default_message->New();
+ self->owner.reset(self->message);
+ return reinterpret_cast<PyObject*>(self);
+}
+
+// The __init__ method of Message classes.
+// It initializes fields from keywords passed to the constructor.
+static int Init(CMessage* self, PyObject* args, PyObject* kwargs) {
+ if (PyTuple_Size(args) != 0) {
+ PyErr_SetString(PyExc_TypeError, "No positional arguments allowed");
+ return -1;
+ }
+
+ return InitAttributes(self, kwargs);
+}
+
+// ---------------------------------------------------------------------
+// Deallocating a CMessage
+//
+// Deallocating a CMessage requires that we clear any weak references
+// from children to the message being deallocated.
+
+// Clear the weak reference from the child to the parent.
+struct ClearWeakReferences : public ChildVisitor {
+ int VisitRepeatedCompositeContainer(RepeatedCompositeContainer* container) {
+ container->parent = NULL;
+ // The elements in the container have the same parent as the
+ // container itself, so NULL out that pointer as well.
+ const Py_ssize_t n = PyList_GET_SIZE(container->child_messages);
+ for (Py_ssize_t i = 0; i < n; ++i) {
+ CMessage* child_cmessage = reinterpret_cast<CMessage*>(
+ PyList_GET_ITEM(container->child_messages, i));
+ child_cmessage->parent = NULL;
+ }
+ return 0;
+ }
+
+ int VisitRepeatedScalarContainer(RepeatedScalarContainer* container) {
+ container->parent = NULL;
+ return 0;
+ }
+
+ int VisitMapContainer(MapContainer* container) {
+ container->parent = NULL;
+ return 0;
+ }
+
+ int VisitCMessage(CMessage* cmessage,
+ const FieldDescriptor* field_descriptor) {
+ cmessage->parent = NULL;
+ return 0;
+ }
+};
+
+static void Dealloc(CMessage* self) {
+ // Null out all weak references from children to this message.
+ GOOGLE_CHECK_EQ(0, ForEachCompositeField(self, ClearWeakReferences()));
+ if (self->extensions) {
+ self->extensions->parent = NULL;
+ }
+
+ Py_CLEAR(self->extensions);
+ Py_CLEAR(self->composite_fields);
+ self->owner.reset();
+ Py_TYPE(self)->tp_free(reinterpret_cast<PyObject*>(self));
+}
+
+// ---------------------------------------------------------------------
+
+
+PyObject* IsInitialized(CMessage* self, PyObject* args) {
+ PyObject* errors = NULL;
+ if (PyArg_ParseTuple(args, "|O", &errors) < 0) {
+ return NULL;
+ }
+ if (self->message->IsInitialized()) {
+ Py_RETURN_TRUE;
+ }
+ if (errors != NULL) {
+ ScopedPyObjectPtr initialization_errors(
+ FindInitializationErrors(self));
+ if (initialization_errors == NULL) {
+ return NULL;
+ }
+ ScopedPyObjectPtr extend_name(PyString_FromString("extend"));
+ if (extend_name == NULL) {
+ return NULL;
+ }
+ ScopedPyObjectPtr result(PyObject_CallMethodObjArgs(
+ errors,
+ extend_name.get(),
+ initialization_errors.get(),
+ NULL));
+ if (result == NULL) {
+ return NULL;
+ }
+ }
+ Py_RETURN_FALSE;
+}
+
+PyObject* HasFieldByDescriptor(
+ CMessage* self, const FieldDescriptor* field_descriptor) {
+ Message* message = self->message;
+ if (!CheckFieldBelongsToMessage(field_descriptor, message)) {
+ return NULL;
+ }
+ if (field_descriptor->label() == FieldDescriptor::LABEL_REPEATED) {
+ PyErr_SetString(PyExc_KeyError,
+ "Field is repeated. A singular method is required.");
+ return NULL;
+ }
+ bool has_field =
+ message->GetReflection()->HasField(*message, field_descriptor);
+ return PyBool_FromLong(has_field ? 1 : 0);
+}
+
+const FieldDescriptor* FindFieldWithOneofs(
+ const Message* message, const string& field_name, bool* in_oneof) {
+ *in_oneof = false;
+ const Descriptor* descriptor = message->GetDescriptor();
+ const FieldDescriptor* field_descriptor =
+ descriptor->FindFieldByName(field_name);
+ if (field_descriptor != NULL) {
+ return field_descriptor;
+ }
+ const OneofDescriptor* oneof_desc =
+ descriptor->FindOneofByName(field_name);
+ if (oneof_desc != NULL) {
+ *in_oneof = true;
+ return message->GetReflection()->GetOneofFieldDescriptor(*message,
+ oneof_desc);
+ }
+ return NULL;
+}
+
+bool CheckHasPresence(const FieldDescriptor* field_descriptor, bool in_oneof) {
+ if (field_descriptor->label() == FieldDescriptor::LABEL_REPEATED) {
+ PyErr_Format(PyExc_ValueError,
+ "Protocol message has no singular \"%s\" field.",
+ field_descriptor->name().c_str());
+ return false;
+ }
+
+ if (field_descriptor->file()->syntax() == FileDescriptor::SYNTAX_PROTO3) {
+ // HasField() for a oneof *itself* isn't supported.
+ if (in_oneof) {
+ PyErr_Format(PyExc_ValueError,
+ "Can't test oneof field \"%s\" for presence in proto3, use "
+ "WhichOneof instead.",
+ field_descriptor->containing_oneof()->name().c_str());
+ return false;
+ }
+
+ // ...but HasField() for fields *in* a oneof is supported.
+ if (field_descriptor->containing_oneof() != NULL) {
+ return true;
+ }
+
+ if (field_descriptor->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE) {
+ PyErr_Format(
+ PyExc_ValueError,
+ "Can't test non-submessage field \"%s\" for presence in proto3.",
+ field_descriptor->name().c_str());
+ return false;
+ }
+ }
+
+ return true;
+}
+
+PyObject* HasField(CMessage* self, PyObject* arg) {
+ char* field_name;
+ Py_ssize_t size;
+#if PY_MAJOR_VERSION < 3
+ if (PyString_AsStringAndSize(arg, &field_name, &size) < 0) {
+ return NULL;
+ }
+#else
+ field_name = PyUnicode_AsUTF8AndSize(arg, &size);
+ if (!field_name) {
+ return NULL;
+ }
+#endif
+
+ Message* message = self->message;
+ bool is_in_oneof;
+ const FieldDescriptor* field_descriptor =
+ FindFieldWithOneofs(message, string(field_name, size), &is_in_oneof);
+ if (field_descriptor == NULL) {
+ if (!is_in_oneof) {
+ PyErr_Format(PyExc_ValueError, "Unknown field %s.", field_name);
+ return NULL;
+ } else {
+ Py_RETURN_FALSE;
+ }
+ }
+
+ if (!CheckHasPresence(field_descriptor, is_in_oneof)) {
+ return NULL;
+ }
+
+ if (message->GetReflection()->HasField(*message, field_descriptor)) {
+ Py_RETURN_TRUE;
+ }
+ if (!message->GetReflection()->SupportsUnknownEnumValues() &&
+ field_descriptor->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) {
+ // Special case: Python HasField() differs in semantics from C++
+ // slightly: we return HasField('enum_field') == true if there is
+ // an unknown enum value present. To implement this we have to
+ // look in the UnknownFieldSet.
+ const UnknownFieldSet& unknown_field_set =
+ message->GetReflection()->GetUnknownFields(*message);
+ for (int i = 0; i < unknown_field_set.field_count(); ++i) {
+ if (unknown_field_set.field(i).number() == field_descriptor->number()) {
+ Py_RETURN_TRUE;
+ }
+ }
+ }
+ Py_RETURN_FALSE;
+}
+
+PyObject* ClearExtension(CMessage* self, PyObject* extension) {
+ if (self->extensions != NULL) {
+ return extension_dict::ClearExtension(self->extensions, extension);
+ } else {
+ const FieldDescriptor* descriptor = GetExtensionDescriptor(extension);
+ if (descriptor == NULL) {
+ return NULL;
+ }
+ if (ScopedPyObjectPtr(ClearFieldByDescriptor(self, descriptor)) == NULL) {
+ return NULL;
+ }
+ }
+ Py_RETURN_NONE;
+}
+
+PyObject* HasExtension(CMessage* self, PyObject* extension) {
+ const FieldDescriptor* descriptor = GetExtensionDescriptor(extension);
+ if (descriptor == NULL) {
+ return NULL;
+ }
+ return HasFieldByDescriptor(self, descriptor);
+}
+
+// ---------------------------------------------------------------------
+// Releasing messages
+//
+// The Python API's ClearField() and Clear() methods behave
+// differently than their C++ counterparts. While the C++ versions
+// clears the children the Python versions detaches the children,
+// without touching their content. This impedance mismatch causes
+// some complexity in the implementation, which is captured in this
+// section.
+//
+// When a CMessage field is cleared we need to:
+//
+// * Release the Message used as the backing store for the CMessage
+// from its parent.
+//
+// * Change the owner field of the released CMessage and all of its
+// children to point to the newly released Message.
+//
+// * Clear the weak references from the released CMessage to the
+// parent.
+//
+// When a RepeatedCompositeContainer field is cleared we need to:
+//
+// * Release all the Message used as the backing store for the
+// CMessages stored in the container.
+//
+// * Change the owner field of all the released CMessage and all of
+// their children to point to the newly released Messages.
+//
+// * Clear the weak references from the released container to the
+// parent.
+
+struct SetOwnerVisitor : public ChildVisitor {
+ // new_owner must outlive this object.
+ explicit SetOwnerVisitor(const shared_ptr<Message>& new_owner)
+ : new_owner_(new_owner) {}
+
+ int VisitRepeatedCompositeContainer(RepeatedCompositeContainer* container) {
+ repeated_composite_container::SetOwner(container, new_owner_);
+ return 0;
+ }
+
+ int VisitRepeatedScalarContainer(RepeatedScalarContainer* container) {
+ repeated_scalar_container::SetOwner(container, new_owner_);
+ return 0;
+ }
+
+ int VisitMapContainer(MapContainer* container) {
+ container->SetOwner(new_owner_);
+ return 0;
+ }
+
+ int VisitCMessage(CMessage* cmessage,
+ const FieldDescriptor* field_descriptor) {
+ return SetOwner(cmessage, new_owner_);
+ }
+
+ private:
+ const shared_ptr<Message>& new_owner_;
+};
+
+// Change the owner of this CMessage and all its children, recursively.
+int SetOwner(CMessage* self, const shared_ptr<Message>& new_owner) {
+ self->owner = new_owner;
+ if (ForEachCompositeField(self, SetOwnerVisitor(new_owner)) == -1)
+ return -1;
+ return 0;
+}
+
+// Releases the message specified by 'field' and returns the
+// pointer. If the field does not exist a new message is created using
+// 'descriptor'. The caller takes ownership of the returned pointer.
+Message* ReleaseMessage(CMessage* self,
+ const Descriptor* descriptor,
+ const FieldDescriptor* field_descriptor) {
+ MessageFactory* message_factory = GetFactoryForMessage(self);
+ Message* released_message = self->message->GetReflection()->ReleaseMessage(
+ self->message, field_descriptor, message_factory);
+ // ReleaseMessage will return NULL which differs from
+ // child_cmessage->message, if the field does not exist. In this case,
+ // the latter points to the default instance via a const_cast<>, so we
+ // have to reset it to a new mutable object since we are taking ownership.
+ if (released_message == NULL) {
+ const Message* prototype = message_factory->GetPrototype(descriptor);
+ GOOGLE_DCHECK(prototype != NULL);
+ released_message = prototype->New();
+ }
+
+ return released_message;
+}
+
+int ReleaseSubMessage(CMessage* self,
+ const FieldDescriptor* field_descriptor,
+ CMessage* child_cmessage) {
+ // Release the Message
+ shared_ptr<Message> released_message(ReleaseMessage(
+ self, child_cmessage->message->GetDescriptor(), field_descriptor));
+ child_cmessage->message = released_message.get();
+ child_cmessage->owner.swap(released_message);
+ child_cmessage->parent = NULL;
+ child_cmessage->parent_field_descriptor = NULL;
+ child_cmessage->read_only = false;
+ return ForEachCompositeField(child_cmessage,
+ SetOwnerVisitor(child_cmessage->owner));
+}
+
+struct ReleaseChild : public ChildVisitor {
+ // message must outlive this object.
+ explicit ReleaseChild(CMessage* parent) :
+ parent_(parent) {}
+
+ int VisitRepeatedCompositeContainer(RepeatedCompositeContainer* container) {
+ return repeated_composite_container::Release(container);
+ }
+
+ int VisitRepeatedScalarContainer(RepeatedScalarContainer* container) {
+ return repeated_scalar_container::Release(container);
+ }
+
+ int VisitMapContainer(MapContainer* container) {
+ return container->Release();
+ }
+
+ int VisitCMessage(CMessage* cmessage,
+ const FieldDescriptor* field_descriptor) {
+ return ReleaseSubMessage(parent_, field_descriptor, cmessage);
+ }
+
+ CMessage* parent_;
+};
+
+int InternalReleaseFieldByDescriptor(
+ CMessage* self,
+ const FieldDescriptor* field_descriptor,
+ PyObject* composite_field) {
+ return VisitCompositeField(
+ field_descriptor,
+ composite_field,
+ ReleaseChild(self));
+}
+
+PyObject* ClearFieldByDescriptor(
+ CMessage* self,
+ const FieldDescriptor* descriptor) {
+ if (!CheckFieldBelongsToMessage(descriptor, self->message)) {
+ return NULL;
+ }
+ AssureWritable(self);
+ self->message->GetReflection()->ClearField(self->message, descriptor);
+ Py_RETURN_NONE;
+}
+
+PyObject* ClearField(CMessage* self, PyObject* arg) {
+ if (!PyString_Check(arg)) {
+ PyErr_SetString(PyExc_TypeError, "field name must be a string");
+ return NULL;
+ }
+#if PY_MAJOR_VERSION < 3
+ const char* field_name = PyString_AS_STRING(arg);
+ Py_ssize_t size = PyString_GET_SIZE(arg);
+#else
+ Py_ssize_t size;
+ const char* field_name = PyUnicode_AsUTF8AndSize(arg, &size);
+#endif
+ AssureWritable(self);
+ Message* message = self->message;
+ ScopedPyObjectPtr arg_in_oneof;
+ bool is_in_oneof;
+ const FieldDescriptor* field_descriptor =
+ FindFieldWithOneofs(message, string(field_name, size), &is_in_oneof);
+ if (field_descriptor == NULL) {
+ if (!is_in_oneof) {
+ PyErr_Format(PyExc_ValueError,
+ "Protocol message has no \"%s\" field.", field_name);
+ return NULL;
+ } else {
+ Py_RETURN_NONE;
+ }
+ } else if (is_in_oneof) {
+ const string& name = field_descriptor->name();
+ arg_in_oneof.reset(PyString_FromStringAndSize(name.c_str(), name.size()));
+ arg = arg_in_oneof.get();
+ }
+
+ PyObject* composite_field = self->composite_fields ?
+ PyDict_GetItem(self->composite_fields, arg) : NULL;
+
+ // Only release the field if there's a possibility that there are
+ // references to it.
+ if (composite_field != NULL) {
+ if (InternalReleaseFieldByDescriptor(self, field_descriptor,
+ composite_field) < 0) {
+ return NULL;
+ }
+ PyDict_DelItem(self->composite_fields, arg);
+ }
+ message->GetReflection()->ClearField(message, field_descriptor);
+ if (field_descriptor->cpp_type() == FieldDescriptor::CPPTYPE_ENUM &&
+ !message->GetReflection()->SupportsUnknownEnumValues()) {
+ UnknownFieldSet* unknown_field_set =
+ message->GetReflection()->MutableUnknownFields(message);
+ unknown_field_set->DeleteByNumber(field_descriptor->number());
+ }
+
+ Py_RETURN_NONE;
+}
+
+PyObject* Clear(CMessage* self) {
+ AssureWritable(self);
+ if (ForEachCompositeField(self, ReleaseChild(self)) == -1)
+ return NULL;
+ Py_CLEAR(self->extensions);
+ if (self->composite_fields) {
+ PyDict_Clear(self->composite_fields);
+ }
+ self->message->Clear();
+ Py_RETURN_NONE;
+}
+
+// ---------------------------------------------------------------------
+
+static string GetMessageName(CMessage* self) {
+ if (self->parent_field_descriptor != NULL) {
+ return self->parent_field_descriptor->full_name();
+ } else {
+ return self->message->GetDescriptor()->full_name();
+ }
+}
+
+static PyObject* SerializeToString(CMessage* self, PyObject* args) {
+ if (!self->message->IsInitialized()) {
+ ScopedPyObjectPtr errors(FindInitializationErrors(self));
+ if (errors == NULL) {
+ return NULL;
+ }
+ ScopedPyObjectPtr comma(PyString_FromString(","));
+ if (comma == NULL) {
+ return NULL;
+ }
+ ScopedPyObjectPtr joined(
+ PyObject_CallMethod(comma.get(), "join", "O", errors.get()));
+ if (joined == NULL) {
+ return NULL;
+ }
+
+ // TODO(haberman): this is a (hopefully temporary) hack. The unit testing
+ // infrastructure reloads all pure-Python modules for every test, but not
+ // C++ modules (because that's generally impossible:
+ // http://bugs.python.org/issue1144263). But if we cache EncodeError, we'll
+ // return the EncodeError from a previous load of the module, which won't
+ // match a user's attempt to catch EncodeError. So we have to look it up
+ // again every time.
+ ScopedPyObjectPtr message_module(PyImport_ImportModule(
+ "google.protobuf.message"));
+ if (message_module.get() == NULL) {
+ return NULL;
+ }
+
+ ScopedPyObjectPtr encode_error(
+ PyObject_GetAttrString(message_module.get(), "EncodeError"));
+ if (encode_error.get() == NULL) {
+ return NULL;
+ }
+ PyErr_Format(encode_error.get(),
+ "Message %s is missing required fields: %s",
+ GetMessageName(self).c_str(), PyString_AsString(joined.get()));
+ return NULL;
+ }
+ int size = self->message->ByteSize();
+ if (size <= 0) {
+ return PyBytes_FromString("");
+ }
+ PyObject* result = PyBytes_FromStringAndSize(NULL, size);
+ if (result == NULL) {
+ return NULL;
+ }
+ char* buffer = PyBytes_AS_STRING(result);
+ self->message->SerializeWithCachedSizesToArray(
+ reinterpret_cast<uint8*>(buffer));
+ return result;
+}
+
+static PyObject* SerializePartialToString(CMessage* self) {
+ string contents;
+ self->message->SerializePartialToString(&contents);
+ return PyBytes_FromStringAndSize(contents.c_str(), contents.size());
+}
+
+// Formats proto fields for ascii dumps using python formatting functions where
+// appropriate.
+class PythonFieldValuePrinter : public TextFormat::FieldValuePrinter {
+ public:
+ // Python has some differences from C++ when printing floating point numbers.
+ //
+ // 1) Trailing .0 is always printed.
+ // 2) (Python2) Output is rounded to 12 digits.
+ // 3) (Python3) The full precision of the double is preserved (and Python uses
+ // David M. Gay's dtoa(), when the C++ code uses SimpleDtoa. There are some
+ // differences, but they rarely happen)
+ //
+ // We override floating point printing with the C-API function for printing
+ // Python floats to ensure consistency.
+ string PrintFloat(float value) const { return PrintDouble(value); }
+ string PrintDouble(double value) const {
+ // This implementation is not highly optimized (it allocates two temporary
+ // Python objects) but it is simple and portable. If this is shown to be a
+ // performance bottleneck, we can optimize it, but the results will likely
+ // be more complicated to accommodate the differing behavior of double
+ // formatting between Python 2 and Python 3.
+ //
+ // (Though a valid question is: do we really want to make out output
+ // dependent on the Python version?)
+ ScopedPyObjectPtr py_value(PyFloat_FromDouble(value));
+ if (!py_value.get()) {
+ return string();
+ }
+
+ ScopedPyObjectPtr py_str(PyObject_Str(py_value.get()));
+ if (!py_str.get()) {
+ return string();
+ }
+
+ return string(PyString_AsString(py_str.get()));
+ }
+};
+
+static PyObject* ToStr(CMessage* self) {
+ TextFormat::Printer printer;
+ // Passes ownership
+ printer.SetDefaultFieldValuePrinter(new PythonFieldValuePrinter());
+ printer.SetHideUnknownFields(true);
+ string output;
+ if (!printer.PrintToString(*self->message, &output)) {
+ PyErr_SetString(PyExc_ValueError, "Unable to convert message to str");
+ return NULL;
+ }
+ return PyString_FromString(output.c_str());
+}
+
+PyObject* MergeFrom(CMessage* self, PyObject* arg) {
+ CMessage* other_message;
+ if (!PyObject_TypeCheck(arg, &CMessage_Type)) {
+ PyErr_Format(PyExc_TypeError,
+ "Parameter to MergeFrom() must be instance of same class: "
+ "expected %s got %s.",
+ self->message->GetDescriptor()->full_name().c_str(),
+ Py_TYPE(arg)->tp_name);
+ return NULL;
+ }
+
+ other_message = reinterpret_cast<CMessage*>(arg);
+ if (other_message->message->GetDescriptor() !=
+ self->message->GetDescriptor()) {
+ PyErr_Format(PyExc_TypeError,
+ "Parameter to MergeFrom() must be instance of same class: "
+ "expected %s got %s.",
+ self->message->GetDescriptor()->full_name().c_str(),
+ other_message->message->GetDescriptor()->full_name().c_str());
+ return NULL;
+ }
+ AssureWritable(self);
+
+ // TODO(tibell): Message::MergeFrom might turn some child Messages
+ // into mutable messages, invalidating the message field in the
+ // corresponding CMessages. We should run a FixupMessageReferences
+ // pass here.
+
+ self->message->MergeFrom(*other_message->message);
+ Py_RETURN_NONE;
+}
+
+static PyObject* CopyFrom(CMessage* self, PyObject* arg) {
+ CMessage* other_message;
+ if (!PyObject_TypeCheck(arg, &CMessage_Type)) {
+ PyErr_Format(PyExc_TypeError,
+ "Parameter to CopyFrom() must be instance of same class: "
+ "expected %s got %s.",
+ self->message->GetDescriptor()->full_name().c_str(),
+ Py_TYPE(arg)->tp_name);
+ return NULL;
+ }
+
+ other_message = reinterpret_cast<CMessage*>(arg);
+
+ if (self == other_message) {
+ Py_RETURN_NONE;
+ }
+
+ if (other_message->message->GetDescriptor() !=
+ self->message->GetDescriptor()) {
+ PyErr_Format(PyExc_TypeError,
+ "Parameter to CopyFrom() must be instance of same class: "
+ "expected %s got %s.",
+ self->message->GetDescriptor()->full_name().c_str(),
+ other_message->message->GetDescriptor()->full_name().c_str());
+ return NULL;
+ }
+
+ AssureWritable(self);
+
+ // CopyFrom on the message will not clean up self->composite_fields,
+ // which can leave us in an inconsistent state, so clear it out here.
+ (void)ScopedPyObjectPtr(Clear(self));
+
+ self->message->CopyFrom(*other_message->message);
+
+ Py_RETURN_NONE;
+}
+
+// Protobuf has a 64MB limit built in, this variable will override this. Please
+// do not enable this unless you fully understand the implications: protobufs
+// must all be kept in memory at the same time, so if they grow too big you may
+// get OOM errors. The protobuf APIs do not provide any tools for processing
+// protobufs in chunks. If you have protos this big you should break them up if
+// it is at all convenient to do so.
+static bool allow_oversize_protos = false;
+
+// Provide a method in the module to set allow_oversize_protos to a boolean
+// value. This method returns the newly value of allow_oversize_protos.
+PyObject* SetAllowOversizeProtos(PyObject* m, PyObject* arg) {
+ if (!arg || !PyBool_Check(arg)) {
+ PyErr_SetString(PyExc_TypeError,
+ "Argument to SetAllowOversizeProtos must be boolean");
+ return NULL;
+ }
+ allow_oversize_protos = PyObject_IsTrue(arg);
+ if (allow_oversize_protos) {
+ Py_RETURN_TRUE;
+ } else {
+ Py_RETURN_FALSE;
+ }
+}
+
+static PyObject* MergeFromString(CMessage* self, PyObject* arg) {
+ const void* data;
+ Py_ssize_t data_length;
+ if (PyObject_AsReadBuffer(arg, &data, &data_length) < 0) {
+ return NULL;
+ }
+
+ AssureWritable(self);
+ io::CodedInputStream input(
+ reinterpret_cast<const uint8*>(data), data_length);
+ if (allow_oversize_protos) {
+ input.SetTotalBytesLimit(INT_MAX, INT_MAX);
+ }
+ PyDescriptorPool* pool = GetDescriptorPoolForMessage(self);
+ input.SetExtensionRegistry(pool->pool, pool->message_factory);
+ bool success = self->message->MergePartialFromCodedStream(&input);
+ if (success) {
+ return PyInt_FromLong(input.CurrentPosition());
+ } else {
+ PyErr_Format(DecodeError_class, "Error parsing message");
+ return NULL;
+ }
+}
+
+static PyObject* ParseFromString(CMessage* self, PyObject* arg) {
+ if (ScopedPyObjectPtr(Clear(self)) == NULL) {
+ return NULL;
+ }
+ return MergeFromString(self, arg);
+}
+
+static PyObject* ByteSize(CMessage* self, PyObject* args) {
+ return PyLong_FromLong(self->message->ByteSize());
+}
+
+static PyObject* RegisterExtension(PyObject* cls,
+ PyObject* extension_handle) {
+ const FieldDescriptor* descriptor =
+ GetExtensionDescriptor(extension_handle);
+ if (descriptor == NULL) {
+ return NULL;
+ }
+
+ ScopedPyObjectPtr extensions_by_name(
+ PyObject_GetAttr(cls, k_extensions_by_name));
+ if (extensions_by_name == NULL) {
+ PyErr_SetString(PyExc_TypeError, "no extensions_by_name on class");
+ return NULL;
+ }
+ ScopedPyObjectPtr full_name(PyObject_GetAttr(extension_handle, kfull_name));
+ if (full_name == NULL) {
+ return NULL;
+ }
+
+ // If the extension was already registered, check that it is the same.
+ PyObject* existing_extension =
+ PyDict_GetItem(extensions_by_name.get(), full_name.get());
+ if (existing_extension != NULL) {
+ const FieldDescriptor* existing_extension_descriptor =
+ GetExtensionDescriptor(existing_extension);
+ if (existing_extension_descriptor != descriptor) {
+ PyErr_SetString(PyExc_ValueError, "Double registration of Extensions");
+ return NULL;
+ }
+ // Nothing else to do.
+ Py_RETURN_NONE;
+ }
+
+ if (PyDict_SetItem(extensions_by_name.get(), full_name.get(),
+ extension_handle) < 0) {
+ return NULL;
+ }
+
+ // Also store a mapping from extension number to implementing class.
+ ScopedPyObjectPtr extensions_by_number(
+ PyObject_GetAttr(cls, k_extensions_by_number));
+ if (extensions_by_number == NULL) {
+ PyErr_SetString(PyExc_TypeError, "no extensions_by_number on class");
+ return NULL;
+ }
+
+ ScopedPyObjectPtr number(PyObject_GetAttrString(extension_handle, "number"));
+ if (number == NULL) {
+ return NULL;
+ }
+
+ // If the extension was already registered by number, check that it is the
+ // same.
+ existing_extension = PyDict_GetItem(extensions_by_number.get(), number.get());
+ if (existing_extension != NULL) {
+ const FieldDescriptor* existing_extension_descriptor =
+ GetExtensionDescriptor(existing_extension);
+ if (existing_extension_descriptor != descriptor) {
+ const Descriptor* msg_desc = GetMessageDescriptor(
+ reinterpret_cast<PyTypeObject*>(cls));
+ PyErr_Format(
+ PyExc_ValueError,
+ "Extensions \"%s\" and \"%s\" both try to extend message type "
+ "\"%s\" with field number %ld.",
+ existing_extension_descriptor->full_name().c_str(),
+ descriptor->full_name().c_str(),
+ msg_desc->full_name().c_str(),
+ PyInt_AsLong(number.get()));
+ return NULL;
+ }
+ // Nothing else to do.
+ Py_RETURN_NONE;
+ }
+ if (PyDict_SetItem(extensions_by_number.get(), number.get(),
+ extension_handle) < 0) {
+ return NULL;
+ }
+
+ // Check if it's a message set
+ if (descriptor->is_extension() &&
+ descriptor->containing_type()->options().message_set_wire_format() &&
+ descriptor->type() == FieldDescriptor::TYPE_MESSAGE &&
+ descriptor->label() == FieldDescriptor::LABEL_OPTIONAL) {
+ ScopedPyObjectPtr message_name(PyString_FromStringAndSize(
+ descriptor->message_type()->full_name().c_str(),
+ descriptor->message_type()->full_name().size()));
+ if (message_name == NULL) {
+ return NULL;
+ }
+ PyDict_SetItem(extensions_by_name.get(), message_name.get(),
+ extension_handle);
+ }
+
+ Py_RETURN_NONE;
+}
+
+static PyObject* SetInParent(CMessage* self, PyObject* args) {
+ AssureWritable(self);
+ Py_RETURN_NONE;
+}
+
+static PyObject* WhichOneof(CMessage* self, PyObject* arg) {
+ Py_ssize_t name_size;
+ char *name_data;
+ if (PyString_AsStringAndSize(arg, &name_data, &name_size) < 0)
+ return NULL;
+ string oneof_name = string(name_data, name_size);
+ const OneofDescriptor* oneof_desc =
+ self->message->GetDescriptor()->FindOneofByName(oneof_name);
+ if (oneof_desc == NULL) {
+ PyErr_Format(PyExc_ValueError,
+ "Protocol message has no oneof \"%s\" field.",
+ oneof_name.c_str());
+ return NULL;
+ }
+ const FieldDescriptor* field_in_oneof =
+ self->message->GetReflection()->GetOneofFieldDescriptor(
+ *self->message, oneof_desc);
+ if (field_in_oneof == NULL) {
+ Py_RETURN_NONE;
+ } else {
+ const string& name = field_in_oneof->name();
+ return PyString_FromStringAndSize(name.c_str(), name.size());
+ }
+}
+
+static PyObject* GetExtensionDict(CMessage* self, void *closure);
+
+static PyObject* ListFields(CMessage* self) {
+ vector<const FieldDescriptor*> fields;
+ self->message->GetReflection()->ListFields(*self->message, &fields);
+
+ // Normally, the list will be exactly the size of the fields.
+ ScopedPyObjectPtr all_fields(PyList_New(fields.size()));
+ if (all_fields == NULL) {
+ return NULL;
+ }
+
+ // When there are unknown extensions, the py list will *not* contain
+ // the field information. Thus the actual size of the py list will be
+ // smaller than the size of fields. Set the actual size at the end.
+ Py_ssize_t actual_size = 0;
+ for (size_t i = 0; i < fields.size(); ++i) {
+ ScopedPyObjectPtr t(PyTuple_New(2));
+ if (t == NULL) {
+ return NULL;
+ }
+
+ if (fields[i]->is_extension()) {
+ ScopedPyObjectPtr extension_field(
+ PyFieldDescriptor_FromDescriptor(fields[i]));
+ if (extension_field == NULL) {
+ return NULL;
+ }
+ // With C++ descriptors, the field can always be retrieved, but for
+ // unknown extensions which have not been imported in Python code, there
+ // is no message class and we cannot retrieve the value.
+ // TODO(amauryfa): consider building the class on the fly!
+ if (fields[i]->message_type() != NULL &&
+ cdescriptor_pool::GetMessageClass(
+ GetDescriptorPoolForMessage(self),
+ fields[i]->message_type()) == NULL) {
+ PyErr_Clear();
+ continue;
+ }
+ ScopedPyObjectPtr extensions(GetExtensionDict(self, NULL));
+ if (extensions == NULL) {
+ return NULL;
+ }
+ // 'extension' reference later stolen by PyTuple_SET_ITEM.
+ PyObject* extension = PyObject_GetItem(
+ extensions.get(), extension_field.get());
+ if (extension == NULL) {
+ return NULL;
+ }
+ PyTuple_SET_ITEM(t.get(), 0, extension_field.release());
+ // Steals reference to 'extension'
+ PyTuple_SET_ITEM(t.get(), 1, extension);
+ } else {
+ // Normal field
+ const string& field_name = fields[i]->name();
+ ScopedPyObjectPtr py_field_name(PyString_FromStringAndSize(
+ field_name.c_str(), field_name.length()));
+ if (py_field_name == NULL) {
+ PyErr_SetString(PyExc_ValueError, "bad string");
+ return NULL;
+ }
+ ScopedPyObjectPtr field_descriptor(
+ PyFieldDescriptor_FromDescriptor(fields[i]));
+ if (field_descriptor == NULL) {
+ return NULL;
+ }
+
+ PyObject* field_value = GetAttr(self, py_field_name.get());
+ if (field_value == NULL) {
+ PyErr_SetObject(PyExc_ValueError, py_field_name.get());
+ return NULL;
+ }
+ PyTuple_SET_ITEM(t.get(), 0, field_descriptor.release());
+ PyTuple_SET_ITEM(t.get(), 1, field_value);
+ }
+ PyList_SET_ITEM(all_fields.get(), actual_size, t.release());
+ ++actual_size;
+ }
+ if (static_cast<size_t>(actual_size) != fields.size() &&
+ (PyList_SetSlice(all_fields.get(), actual_size, fields.size(), NULL) <
+ 0)) {
+ return NULL;
+ }
+ return all_fields.release();
+}
+
+static PyObject* DiscardUnknownFields(CMessage* self) {
+ AssureWritable(self);
+ self->message->DiscardUnknownFields();
+ Py_RETURN_NONE;
+}
+
+PyObject* FindInitializationErrors(CMessage* self) {
+ Message* message = self->message;
+ vector<string> errors;
+ message->FindInitializationErrors(&errors);
+
+ PyObject* error_list = PyList_New(errors.size());
+ if (error_list == NULL) {
+ return NULL;
+ }
+ for (size_t i = 0; i < errors.size(); ++i) {
+ const string& error = errors[i];
+ PyObject* error_string = PyString_FromStringAndSize(
+ error.c_str(), error.length());
+ if (error_string == NULL) {
+ Py_DECREF(error_list);
+ return NULL;
+ }
+ PyList_SET_ITEM(error_list, i, error_string);
+ }
+ return error_list;
+}
+
+static PyObject* RichCompare(CMessage* self, PyObject* other, int opid) {
+ // Only equality comparisons are implemented.
+ if (opid != Py_EQ && opid != Py_NE) {
+ Py_INCREF(Py_NotImplemented);
+ return Py_NotImplemented;
+ }
+ bool equals = true;
+ // If other is not a message, it cannot be equal.
+ if (!PyObject_TypeCheck(other, &CMessage_Type)) {
+ equals = false;
+ }
+ const google::protobuf::Message* other_message =
+ reinterpret_cast<CMessage*>(other)->message;
+ // If messages don't have the same descriptors, they are not equal.
+ if (equals &&
+ self->message->GetDescriptor() != other_message->GetDescriptor()) {
+ equals = false;
+ }
+ // Check the message contents.
+ if (equals && !google::protobuf::util::MessageDifferencer::Equals(
+ *self->message,
+ *reinterpret_cast<CMessage*>(other)->message)) {
+ equals = false;
+ }
+ if (equals ^ (opid == Py_EQ)) {
+ Py_RETURN_FALSE;
+ } else {
+ Py_RETURN_TRUE;
+ }
+}
+
+PyObject* InternalGetScalar(const Message* message,
+ const FieldDescriptor* field_descriptor) {
+ const Reflection* reflection = message->GetReflection();
+
+ if (!CheckFieldBelongsToMessage(field_descriptor, message)) {
+ return NULL;
+ }
+
+ PyObject* result = NULL;
+ switch (field_descriptor->cpp_type()) {
+ case FieldDescriptor::CPPTYPE_INT32: {
+ int32 value = reflection->GetInt32(*message, field_descriptor);
+ result = PyInt_FromLong(value);
+ break;
+ }
+ case FieldDescriptor::CPPTYPE_INT64: {
+ int64 value = reflection->GetInt64(*message, field_descriptor);
+ result = PyLong_FromLongLong(value);
+ break;
+ }
+ case FieldDescriptor::CPPTYPE_UINT32: {
+ uint32 value = reflection->GetUInt32(*message, field_descriptor);
+ result = PyInt_FromSize_t(value);
+ break;
+ }
+ case FieldDescriptor::CPPTYPE_UINT64: {
+ uint64 value = reflection->GetUInt64(*message, field_descriptor);
+ result = PyLong_FromUnsignedLongLong(value);
+ break;
+ }
+ case FieldDescriptor::CPPTYPE_FLOAT: {
+ float value = reflection->GetFloat(*message, field_descriptor);
+ result = PyFloat_FromDouble(value);
+ break;
+ }
+ case FieldDescriptor::CPPTYPE_DOUBLE: {
+ double value = reflection->GetDouble(*message, field_descriptor);
+ result = PyFloat_FromDouble(value);
+ break;
+ }
+ case FieldDescriptor::CPPTYPE_BOOL: {
+ bool value = reflection->GetBool(*message, field_descriptor);
+ result = PyBool_FromLong(value);
+ break;
+ }
+ case FieldDescriptor::CPPTYPE_STRING: {
+ string value = reflection->GetString(*message, field_descriptor);
+ result = ToStringObject(field_descriptor, value);
+ break;
+ }
+ case FieldDescriptor::CPPTYPE_ENUM: {
+ if (!message->GetReflection()->SupportsUnknownEnumValues() &&
+ !message->GetReflection()->HasField(*message, field_descriptor)) {
+ // Look for the value in the unknown fields.
+ const UnknownFieldSet& unknown_field_set =
+ message->GetReflection()->GetUnknownFields(*message);
+ for (int i = 0; i < unknown_field_set.field_count(); ++i) {
+ if (unknown_field_set.field(i).number() ==
+ field_descriptor->number() &&
+ unknown_field_set.field(i).type() ==
+ google::protobuf::UnknownField::TYPE_VARINT) {
+ result = PyInt_FromLong(unknown_field_set.field(i).varint());
+ break;
+ }
+ }
+ }
+
+ if (result == NULL) {
+ const EnumValueDescriptor* enum_value =
+ message->GetReflection()->GetEnum(*message, field_descriptor);
+ result = PyInt_FromLong(enum_value->number());
+ }
+ break;
+ }
+ default:
+ PyErr_Format(
+ PyExc_SystemError, "Getting a value from a field of unknown type %d",
+ field_descriptor->cpp_type());
+ }
+
+ return result;
+}
+
+PyObject* InternalGetSubMessage(
+ CMessage* self, const FieldDescriptor* field_descriptor) {
+ const Reflection* reflection = self->message->GetReflection();
+ PyDescriptorPool* pool = GetDescriptorPoolForMessage(self);
+ const Message& sub_message = reflection->GetMessage(
+ *self->message, field_descriptor, pool->message_factory);
+
+ CMessageClass* message_class = cdescriptor_pool::GetMessageClass(
+ pool, field_descriptor->message_type());
+ if (message_class == NULL) {
+ return NULL;
+ }
+
+ CMessage* cmsg = cmessage::NewEmptyMessage(message_class);
+ if (cmsg == NULL) {
+ return NULL;
+ }
+
+ cmsg->owner = self->owner;
+ cmsg->parent = self;
+ cmsg->parent_field_descriptor = field_descriptor;
+ cmsg->read_only = !reflection->HasField(*self->message, field_descriptor);
+ cmsg->message = const_cast<Message*>(&sub_message);
+
+ return reinterpret_cast<PyObject*>(cmsg);
+}
+
+int InternalSetNonOneofScalar(
+ Message* message,
+ const FieldDescriptor* field_descriptor,
+ PyObject* arg) {
+ const Reflection* reflection = message->GetReflection();
+
+ if (!CheckFieldBelongsToMessage(field_descriptor, message)) {
+ return -1;
+ }
+
+ switch (field_descriptor->cpp_type()) {
+ case FieldDescriptor::CPPTYPE_INT32: {
+ GOOGLE_CHECK_GET_INT32(arg, value, -1);
+ reflection->SetInt32(message, field_descriptor, value);
+ break;
+ }
+ case FieldDescriptor::CPPTYPE_INT64: {
+ GOOGLE_CHECK_GET_INT64(arg, value, -1);
+ reflection->SetInt64(message, field_descriptor, value);
+ break;
+ }
+ case FieldDescriptor::CPPTYPE_UINT32: {
+ GOOGLE_CHECK_GET_UINT32(arg, value, -1);
+ reflection->SetUInt32(message, field_descriptor, value);
+ break;
+ }
+ case FieldDescriptor::CPPTYPE_UINT64: {
+ GOOGLE_CHECK_GET_UINT64(arg, value, -1);
+ reflection->SetUInt64(message, field_descriptor, value);
+ break;
+ }
+ case FieldDescriptor::CPPTYPE_FLOAT: {
+ GOOGLE_CHECK_GET_FLOAT(arg, value, -1);
+ reflection->SetFloat(message, field_descriptor, value);
+ break;
+ }
+ case FieldDescriptor::CPPTYPE_DOUBLE: {
+ GOOGLE_CHECK_GET_DOUBLE(arg, value, -1);
+ reflection->SetDouble(message, field_descriptor, value);
+ break;
+ }
+ case FieldDescriptor::CPPTYPE_BOOL: {
+ GOOGLE_CHECK_GET_BOOL(arg, value, -1);
+ reflection->SetBool(message, field_descriptor, value);
+ break;
+ }
+ case FieldDescriptor::CPPTYPE_STRING: {
+ if (!CheckAndSetString(
+ arg, message, field_descriptor, reflection, false, -1)) {
+ return -1;
+ }
+ break;
+ }
+ case FieldDescriptor::CPPTYPE_ENUM: {
+ GOOGLE_CHECK_GET_INT32(arg, value, -1);
+ if (reflection->SupportsUnknownEnumValues()) {
+ reflection->SetEnumValue(message, field_descriptor, value);
+ } else {
+ const EnumDescriptor* enum_descriptor = field_descriptor->enum_type();
+ const EnumValueDescriptor* enum_value =
+ enum_descriptor->FindValueByNumber(value);
+ if (enum_value != NULL) {
+ reflection->SetEnum(message, field_descriptor, enum_value);
+ } else {
+ PyErr_Format(PyExc_ValueError, "Unknown enum value: %d", value);
+ return -1;
+ }
+ }
+ break;
+ }
+ default:
+ PyErr_Format(
+ PyExc_SystemError, "Setting value to a field of unknown type %d",
+ field_descriptor->cpp_type());
+ return -1;
+ }
+
+ return 0;
+}
+
+int InternalSetScalar(
+ CMessage* self,
+ const FieldDescriptor* field_descriptor,
+ PyObject* arg) {
+ if (!CheckFieldBelongsToMessage(field_descriptor, self->message)) {
+ return -1;
+ }
+
+ if (MaybeReleaseOverlappingOneofField(self, field_descriptor) < 0) {
+ return -1;
+ }
+
+ return InternalSetNonOneofScalar(self->message, field_descriptor, arg);
+}
+
+PyObject* FromString(PyTypeObject* cls, PyObject* serialized) {
+ PyObject* py_cmsg = PyObject_CallObject(
+ reinterpret_cast<PyObject*>(cls), NULL);
+ if (py_cmsg == NULL) {
+ return NULL;
+ }
+ CMessage* cmsg = reinterpret_cast<CMessage*>(py_cmsg);
+
+ ScopedPyObjectPtr py_length(MergeFromString(cmsg, serialized));
+ if (py_length == NULL) {
+ Py_DECREF(py_cmsg);
+ return NULL;
+ }
+
+ return py_cmsg;
+}
+
+PyObject* DeepCopy(CMessage* self, PyObject* arg) {
+ PyObject* clone = PyObject_CallObject(
+ reinterpret_cast<PyObject*>(Py_TYPE(self)), NULL);
+ if (clone == NULL) {
+ return NULL;
+ }
+ if (!PyObject_TypeCheck(clone, &CMessage_Type)) {
+ Py_DECREF(clone);
+ return NULL;
+ }
+ if (ScopedPyObjectPtr(MergeFrom(
+ reinterpret_cast<CMessage*>(clone),
+ reinterpret_cast<PyObject*>(self))) == NULL) {
+ Py_DECREF(clone);
+ return NULL;
+ }
+ return clone;
+}
+
+PyObject* ToUnicode(CMessage* self) {
+ // Lazy import to prevent circular dependencies
+ ScopedPyObjectPtr text_format(
+ PyImport_ImportModule("google.protobuf.text_format"));
+ if (text_format == NULL) {
+ return NULL;
+ }
+ ScopedPyObjectPtr method_name(PyString_FromString("MessageToString"));
+ if (method_name == NULL) {
+ return NULL;
+ }
+ Py_INCREF(Py_True);
+ ScopedPyObjectPtr encoded(PyObject_CallMethodObjArgs(
+ text_format.get(), method_name.get(), self, Py_True, NULL));
+ Py_DECREF(Py_True);
+ if (encoded == NULL) {
+ return NULL;
+ }
+#if PY_MAJOR_VERSION < 3
+ PyObject* decoded = PyString_AsDecodedObject(encoded.get(), "utf-8", NULL);
+#else
+ PyObject* decoded = PyUnicode_FromEncodedObject(encoded.get(), "utf-8", NULL);
+#endif
+ if (decoded == NULL) {
+ return NULL;
+ }
+ return decoded;
+}
+
+PyObject* Reduce(CMessage* self) {
+ ScopedPyObjectPtr constructor(reinterpret_cast<PyObject*>(Py_TYPE(self)));
+ constructor.inc();
+ ScopedPyObjectPtr args(PyTuple_New(0));
+ if (args == NULL) {
+ return NULL;
+ }
+ ScopedPyObjectPtr state(PyDict_New());
+ if (state == NULL) {
+ return NULL;
+ }
+ ScopedPyObjectPtr serialized(SerializePartialToString(self));
+ if (serialized == NULL) {
+ return NULL;
+ }
+ if (PyDict_SetItemString(state.get(), "serialized", serialized.get()) < 0) {
+ return NULL;
+ }
+ return Py_BuildValue("OOO", constructor.get(), args.get(), state.get());
+}
+
+PyObject* SetState(CMessage* self, PyObject* state) {
+ if (!PyDict_Check(state)) {
+ PyErr_SetString(PyExc_TypeError, "state not a dict");
+ return NULL;
+ }
+ PyObject* serialized = PyDict_GetItemString(state, "serialized");
+ if (serialized == NULL) {
+ return NULL;
+ }
+ if (ScopedPyObjectPtr(ParseFromString(self, serialized)) == NULL) {
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
+// CMessage static methods:
+PyObject* _CheckCalledFromGeneratedFile(PyObject* unused,
+ PyObject* unused_arg) {
+ if (!_CalledFromGeneratedFile(1)) {
+ PyErr_SetString(PyExc_TypeError,
+ "Descriptors should not be created directly, "
+ "but only retrieved from their parent.");
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
+static PyObject* GetExtensionDict(CMessage* self, void *closure) {
+ if (self->extensions) {
+ Py_INCREF(self->extensions);
+ return reinterpret_cast<PyObject*>(self->extensions);
+ }
+
+ // If there are extension_ranges, the message is "extendable". Allocate a
+ // dictionary to store the extension fields.
+ const Descriptor* descriptor = GetMessageDescriptor(Py_TYPE(self));
+ if (descriptor->extension_range_count() > 0) {
+ ExtensionDict* extension_dict = extension_dict::NewExtensionDict(self);
+ if (extension_dict == NULL) {
+ return NULL;
+ }
+ self->extensions = extension_dict;
+ Py_INCREF(self->extensions);
+ return reinterpret_cast<PyObject*>(self->extensions);
+ }
+
+ PyErr_SetNone(PyExc_AttributeError);
+ return NULL;
+}
+
+static PyGetSetDef Getters[] = {
+ {"Extensions", (getter)GetExtensionDict, NULL, "Extension dict"},
+ {NULL}
+};
+
+static PyMethodDef Methods[] = {
+ { "__deepcopy__", (PyCFunction)DeepCopy, METH_VARARGS,
+ "Makes a deep copy of the class." },
+ { "__reduce__", (PyCFunction)Reduce, METH_NOARGS,
+ "Outputs picklable representation of the message." },
+ { "__setstate__", (PyCFunction)SetState, METH_O,
+ "Inputs picklable representation of the message." },
+ { "__unicode__", (PyCFunction)ToUnicode, METH_NOARGS,
+ "Outputs a unicode representation of the message." },
+ { "ByteSize", (PyCFunction)ByteSize, METH_NOARGS,
+ "Returns the size of the message in bytes." },
+ { "Clear", (PyCFunction)Clear, METH_NOARGS,
+ "Clears the message." },
+ { "ClearExtension", (PyCFunction)ClearExtension, METH_O,
+ "Clears a message field." },
+ { "ClearField", (PyCFunction)ClearField, METH_O,
+ "Clears a message field." },
+ { "CopyFrom", (PyCFunction)CopyFrom, METH_O,
+ "Copies a protocol message into the current message." },
+ { "DiscardUnknownFields", (PyCFunction)DiscardUnknownFields, METH_NOARGS,
+ "Discards the unknown fields." },
+ { "FindInitializationErrors", (PyCFunction)FindInitializationErrors,
+ METH_NOARGS,
+ "Finds unset required fields." },
+ { "FromString", (PyCFunction)FromString, METH_O | METH_CLASS,
+ "Creates new method instance from given serialized data." },
+ { "HasExtension", (PyCFunction)HasExtension, METH_O,
+ "Checks if a message field is set." },
+ { "HasField", (PyCFunction)HasField, METH_O,
+ "Checks if a message field is set." },
+ { "IsInitialized", (PyCFunction)IsInitialized, METH_VARARGS,
+ "Checks if all required fields of a protocol message are set." },
+ { "ListFields", (PyCFunction)ListFields, METH_NOARGS,
+ "Lists all set fields of a message." },
+ { "MergeFrom", (PyCFunction)MergeFrom, METH_O,
+ "Merges a protocol message into the current message." },
+ { "MergeFromString", (PyCFunction)MergeFromString, METH_O,
+ "Merges a serialized message into the current message." },
+ { "ParseFromString", (PyCFunction)ParseFromString, METH_O,
+ "Parses a serialized message into the current message." },
+ { "RegisterExtension", (PyCFunction)RegisterExtension, METH_O | METH_CLASS,
+ "Registers an extension with the current message." },
+ { "SerializePartialToString", (PyCFunction)SerializePartialToString,
+ METH_NOARGS,
+ "Serializes the message to a string, even if it isn't initialized." },
+ { "SerializeToString", (PyCFunction)SerializeToString, METH_NOARGS,
+ "Serializes the message to a string, only for initialized messages." },
+ { "SetInParent", (PyCFunction)SetInParent, METH_NOARGS,
+ "Sets the has bit of the given field in its parent message." },
+ { "WhichOneof", (PyCFunction)WhichOneof, METH_O,
+ "Returns the name of the field set inside a oneof, "
+ "or None if no field is set." },
+
+ // Static Methods.
+ { "_CheckCalledFromGeneratedFile", (PyCFunction)_CheckCalledFromGeneratedFile,
+ METH_NOARGS | METH_STATIC,
+ "Raises TypeError if the caller is not in a _pb2.py file."},
+ { NULL, NULL}
+};
+
+static bool SetCompositeField(
+ CMessage* self, PyObject* name, PyObject* value) {
+ if (self->composite_fields == NULL) {
+ self->composite_fields = PyDict_New();
+ if (self->composite_fields == NULL) {
+ return false;
+ }
+ }
+ return PyDict_SetItem(self->composite_fields, name, value) == 0;
+}
+
+PyObject* GetAttr(CMessage* self, PyObject* name) {
+ PyObject* value = self->composite_fields ?
+ PyDict_GetItem(self->composite_fields, name) : NULL;
+ if (value != NULL) {
+ Py_INCREF(value);
+ return value;
+ }
+
+ const FieldDescriptor* field_descriptor = GetFieldDescriptor(self, name);
+ if (field_descriptor == NULL) {
+ return CMessage_Type.tp_base->tp_getattro(
+ reinterpret_cast<PyObject*>(self), name);
+ }
+
+ if (field_descriptor->is_map()) {
+ PyObject* py_container = NULL;
+ const Descriptor* entry_type = field_descriptor->message_type();
+ const FieldDescriptor* value_type = entry_type->FindFieldByName("value");
+ if (value_type->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
+ CMessageClass* value_class = cdescriptor_pool::GetMessageClass(
+ GetDescriptorPoolForMessage(self), value_type->message_type());
+ if (value_class == NULL) {
+ return NULL;
+ }
+ py_container =
+ NewMessageMapContainer(self, field_descriptor, value_class);
+ } else {
+ py_container = NewScalarMapContainer(self, field_descriptor);
+ }
+ if (py_container == NULL) {
+ return NULL;
+ }
+ if (!SetCompositeField(self, name, py_container)) {
+ Py_DECREF(py_container);
+ return NULL;
+ }
+ return py_container;
+ }
+
+ if (field_descriptor->label() == FieldDescriptor::LABEL_REPEATED) {
+ PyObject* py_container = NULL;
+ if (field_descriptor->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
+ CMessageClass* message_class = cdescriptor_pool::GetMessageClass(
+ GetDescriptorPoolForMessage(self), field_descriptor->message_type());
+ if (message_class == NULL) {
+ return NULL;
+ }
+ py_container = repeated_composite_container::NewContainer(
+ self, field_descriptor, message_class);
+ } else {
+ py_container = repeated_scalar_container::NewContainer(
+ self, field_descriptor);
+ }
+ if (py_container == NULL) {
+ return NULL;
+ }
+ if (!SetCompositeField(self, name, py_container)) {
+ Py_DECREF(py_container);
+ return NULL;
+ }
+ return py_container;
+ }
+
+ if (field_descriptor->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
+ PyObject* sub_message = InternalGetSubMessage(self, field_descriptor);
+ if (sub_message == NULL) {
+ return NULL;
+ }
+ if (!SetCompositeField(self, name, sub_message)) {
+ Py_DECREF(sub_message);
+ return NULL;
+ }
+ return sub_message;
+ }
+
+ return InternalGetScalar(self->message, field_descriptor);
+}
+
+int SetAttr(CMessage* self, PyObject* name, PyObject* value) {
+ if (self->composite_fields && PyDict_Contains(self->composite_fields, name)) {
+ PyErr_SetString(PyExc_TypeError, "Can't set composite field");
+ return -1;
+ }
+
+ const FieldDescriptor* field_descriptor = GetFieldDescriptor(self, name);
+ if (field_descriptor != NULL) {
+ AssureWritable(self);
+ if (field_descriptor->label() == FieldDescriptor::LABEL_REPEATED) {
+ PyErr_Format(PyExc_AttributeError, "Assignment not allowed to repeated "
+ "field \"%s\" in protocol message object.",
+ field_descriptor->name().c_str());
+ return -1;
+ } else {
+ if (field_descriptor->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
+ PyErr_Format(PyExc_AttributeError, "Assignment not allowed to "
+ "field \"%s\" in protocol message object.",
+ field_descriptor->name().c_str());
+ return -1;
+ } else {
+ return InternalSetScalar(self, field_descriptor, value);
+ }
+ }
+ }
+
+ PyErr_Format(PyExc_AttributeError,
+ "Assignment not allowed "
+ "(no field \"%s\" in protocol message object).",
+ PyString_AsString(name));
+ return -1;
+}
+
+} // namespace cmessage
+
+PyTypeObject CMessage_Type = {
+ PyVarObject_HEAD_INIT(&CMessageClass_Type, 0)
+ FULL_MODULE_NAME ".CMessage", // tp_name
+ sizeof(CMessage), // tp_basicsize
+ 0, // tp_itemsize
+ (destructor)cmessage::Dealloc, // tp_dealloc
+ 0, // tp_print
+ 0, // tp_getattr
+ 0, // tp_setattr
+ 0, // tp_compare
+ (reprfunc)cmessage::ToStr, // tp_repr
+ 0, // tp_as_number
+ 0, // tp_as_sequence
+ 0, // tp_as_mapping
+ PyObject_HashNotImplemented, // tp_hash
+ 0, // tp_call
+ (reprfunc)cmessage::ToStr, // tp_str
+ (getattrofunc)cmessage::GetAttr, // tp_getattro
+ (setattrofunc)cmessage::SetAttr, // tp_setattro
+ 0, // tp_as_buffer
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, // tp_flags
+ "A ProtocolMessage", // tp_doc
+ 0, // tp_traverse
+ 0, // tp_clear
+ (richcmpfunc)cmessage::RichCompare, // tp_richcompare
+ 0, // tp_weaklistoffset
+ 0, // tp_iter
+ 0, // tp_iternext
+ cmessage::Methods, // tp_methods
+ 0, // tp_members
+ cmessage::Getters, // tp_getset
+ 0, // tp_base
+ 0, // tp_dict
+ 0, // tp_descr_get
+ 0, // tp_descr_set
+ 0, // tp_dictoffset
+ (initproc)cmessage::Init, // tp_init
+ 0, // tp_alloc
+ cmessage::New, // tp_new
+};
+
+// --- Exposing the C proto living inside Python proto to C code:
+
+const Message* (*GetCProtoInsidePyProtoPtr)(PyObject* msg);
+Message* (*MutableCProtoInsidePyProtoPtr)(PyObject* msg);
+
+static const Message* GetCProtoInsidePyProtoImpl(PyObject* msg) {
+ if (!PyObject_TypeCheck(msg, &CMessage_Type)) {
+ return NULL;
+ }
+ CMessage* cmsg = reinterpret_cast<CMessage*>(msg);
+ return cmsg->message;
+}
+
+static Message* MutableCProtoInsidePyProtoImpl(PyObject* msg) {
+ if (!PyObject_TypeCheck(msg, &CMessage_Type)) {
+ return NULL;
+ }
+ CMessage* cmsg = reinterpret_cast<CMessage*>(msg);
+ if ((cmsg->composite_fields && PyDict_Size(cmsg->composite_fields) != 0) ||
+ (cmsg->extensions != NULL &&
+ PyDict_Size(cmsg->extensions->values) != 0)) {
+ // There is currently no way of accurately syncing arbitrary changes to
+ // the underlying C++ message back to the CMessage (e.g. removed repeated
+ // composite containers). We only allow direct mutation of the underlying
+ // C++ message if there is no child data in the CMessage.
+ return NULL;
+ }
+ cmessage::AssureWritable(cmsg);
+ return cmsg->message;
+}
+
+static const char module_docstring[] =
+"python-proto2 is a module that can be used to enhance proto2 Python API\n"
+"performance.\n"
+"\n"
+"It provides access to the protocol buffers C++ reflection API that\n"
+"implements the basic protocol buffer functions.";
+
+void InitGlobals() {
+ // TODO(gps): Check all return values in this function for NULL and propagate
+ // the error (MemoryError) on up to result in an import failure. These should
+ // also be freed and reset to NULL during finalization.
+ kPythonZero = PyInt_FromLong(0);
+ kint32min_py = PyInt_FromLong(kint32min);
+ kint32max_py = PyInt_FromLong(kint32max);
+ kuint32max_py = PyLong_FromLongLong(kuint32max);
+ kint64min_py = PyLong_FromLongLong(kint64min);
+ kint64max_py = PyLong_FromLongLong(kint64max);
+ kuint64max_py = PyLong_FromUnsignedLongLong(kuint64max);
+
+ kDESCRIPTOR = PyString_FromString("DESCRIPTOR");
+ k_cdescriptor = PyString_FromString("_cdescriptor");
+ kfull_name = PyString_FromString("full_name");
+ k_extensions_by_name = PyString_FromString("_extensions_by_name");
+ k_extensions_by_number = PyString_FromString("_extensions_by_number");
+
+ PyObject *dummy_obj = PySet_New(NULL);
+ kEmptyWeakref = PyWeakref_NewRef(dummy_obj, NULL);
+ Py_DECREF(dummy_obj);
+}
+
+bool InitProto2MessageModule(PyObject *m) {
+ // Initialize types and globals in descriptor.cc
+ if (!InitDescriptor()) {
+ return false;
+ }
+
+ // Initialize types and globals in descriptor_pool.cc
+ if (!InitDescriptorPool()) {
+ return false;
+ }
+
+ // Initialize constants defined in this file.
+ InitGlobals();
+
+ CMessageClass_Type.tp_base = &PyType_Type;
+ if (PyType_Ready(&CMessageClass_Type) < 0) {
+ return false;
+ }
+ PyModule_AddObject(m, "MessageMeta",
+ reinterpret_cast<PyObject*>(&CMessageClass_Type));
+
+ if (PyType_Ready(&CMessage_Type) < 0) {
+ return false;
+ }
+
+ // DESCRIPTOR is set on each protocol buffer message class elsewhere, but set
+ // it here as well to document that subclasses need to set it.
+ PyDict_SetItem(CMessage_Type.tp_dict, kDESCRIPTOR, Py_None);
+ // Subclasses with message extensions will override _extensions_by_name and
+ // _extensions_by_number with fresh mutable dictionaries in AddDescriptors.
+ // All other classes can share this same immutable mapping.
+ ScopedPyObjectPtr empty_dict(PyDict_New());
+ if (empty_dict == NULL) {
+ return false;
+ }
+ ScopedPyObjectPtr immutable_dict(PyDictProxy_New(empty_dict.get()));
+ if (immutable_dict == NULL) {
+ return false;
+ }
+ if (PyDict_SetItem(CMessage_Type.tp_dict,
+ k_extensions_by_name, immutable_dict.get()) < 0) {
+ return false;
+ }
+ if (PyDict_SetItem(CMessage_Type.tp_dict,
+ k_extensions_by_number, immutable_dict.get()) < 0) {
+ return false;
+ }
+
+ PyModule_AddObject(m, "Message", reinterpret_cast<PyObject*>(&CMessage_Type));
+
+ // Initialize Repeated container types.
+ {
+ if (PyType_Ready(&RepeatedScalarContainer_Type) < 0) {
+ return false;
+ }
+
+ PyModule_AddObject(m, "RepeatedScalarContainer",
+ reinterpret_cast<PyObject*>(
+ &RepeatedScalarContainer_Type));
+
+ if (PyType_Ready(&RepeatedCompositeContainer_Type) < 0) {
+ return false;
+ }
+
+ PyModule_AddObject(
+ m, "RepeatedCompositeContainer",
+ reinterpret_cast<PyObject*>(
+ &RepeatedCompositeContainer_Type));
+
+ // Register them as collections.Sequence
+ ScopedPyObjectPtr collections(PyImport_ImportModule("collections"));
+ if (collections == NULL) {
+ return false;
+ }
+ ScopedPyObjectPtr mutable_sequence(
+ PyObject_GetAttrString(collections.get(), "MutableSequence"));
+ if (mutable_sequence == NULL) {
+ return false;
+ }
+ if (ScopedPyObjectPtr(
+ PyObject_CallMethod(mutable_sequence.get(), "register", "O",
+ &RepeatedScalarContainer_Type)) == NULL) {
+ return false;
+ }
+ if (ScopedPyObjectPtr(
+ PyObject_CallMethod(mutable_sequence.get(), "register", "O",
+ &RepeatedCompositeContainer_Type)) == NULL) {
+ return false;
+ }
+ }
+
+ // Initialize Map container types.
+ {
+ // ScalarMapContainer_Type derives from our MutableMapping type.
+ ScopedPyObjectPtr containers(PyImport_ImportModule(
+ "google.protobuf.internal.containers"));
+ if (containers == NULL) {
+ return false;
+ }
+
+ ScopedPyObjectPtr mutable_mapping(
+ PyObject_GetAttrString(containers.get(), "MutableMapping"));
+ if (mutable_mapping == NULL) {
+ return false;
+ }
+
+ if (!PyObject_TypeCheck(mutable_mapping.get(), &PyType_Type)) {
+ return false;
+ }
+
+ Py_INCREF(mutable_mapping.get());
+#if PY_MAJOR_VERSION >= 3
+ PyObject* bases = PyTuple_New(1);
+ PyTuple_SET_ITEM(bases, 0, mutable_mapping.get());
+
+ ScalarMapContainer_Type =
+ PyType_FromSpecWithBases(&ScalarMapContainer_Type_spec, bases);
+ PyModule_AddObject(m, "ScalarMapContainer", ScalarMapContainer_Type);
+#else
+ ScalarMapContainer_Type.tp_base =
+ reinterpret_cast<PyTypeObject*>(mutable_mapping.get());
+
+ if (PyType_Ready(&ScalarMapContainer_Type) < 0) {
+ return false;
+ }
+
+ PyModule_AddObject(m, "ScalarMapContainer",
+ reinterpret_cast<PyObject*>(&ScalarMapContainer_Type));
+#endif
+
+ if (PyType_Ready(&MapIterator_Type) < 0) {
+ return false;
+ }
+
+ PyModule_AddObject(m, "MapIterator",
+ reinterpret_cast<PyObject*>(&MapIterator_Type));
+
+
+#if PY_MAJOR_VERSION >= 3
+ MessageMapContainer_Type =
+ PyType_FromSpecWithBases(&MessageMapContainer_Type_spec, bases);
+ PyModule_AddObject(m, "MessageMapContainer", MessageMapContainer_Type);
+#else
+ Py_INCREF(mutable_mapping.get());
+ MessageMapContainer_Type.tp_base =
+ reinterpret_cast<PyTypeObject*>(mutable_mapping.get());
+
+ if (PyType_Ready(&MessageMapContainer_Type) < 0) {
+ return false;
+ }
+
+ PyModule_AddObject(m, "MessageMapContainer",
+ reinterpret_cast<PyObject*>(&MessageMapContainer_Type));
+#endif
+ }
+
+ if (PyType_Ready(&ExtensionDict_Type) < 0) {
+ return false;
+ }
+ PyModule_AddObject(
+ m, "ExtensionDict",
+ reinterpret_cast<PyObject*>(&ExtensionDict_Type));
+
+ // Expose the DescriptorPool used to hold all descriptors added from generated
+ // pb2.py files.
+ // PyModule_AddObject steals a reference.
+ Py_INCREF(GetDefaultDescriptorPool());
+ PyModule_AddObject(m, "default_pool",
+ reinterpret_cast<PyObject*>(GetDefaultDescriptorPool()));
+
+ PyModule_AddObject(m, "DescriptorPool", reinterpret_cast<PyObject*>(
+ &PyDescriptorPool_Type));
+
+ // This implementation provides full Descriptor types, we advertise it so that
+ // descriptor.py can use them in replacement of the Python classes.
+ PyModule_AddIntConstant(m, "_USE_C_DESCRIPTORS", 1);
+
+ PyModule_AddObject(m, "Descriptor", reinterpret_cast<PyObject*>(
+ &PyMessageDescriptor_Type));
+ PyModule_AddObject(m, "FieldDescriptor", reinterpret_cast<PyObject*>(
+ &PyFieldDescriptor_Type));
+ PyModule_AddObject(m, "EnumDescriptor", reinterpret_cast<PyObject*>(
+ &PyEnumDescriptor_Type));
+ PyModule_AddObject(m, "EnumValueDescriptor", reinterpret_cast<PyObject*>(
+ &PyEnumValueDescriptor_Type));
+ PyModule_AddObject(m, "FileDescriptor", reinterpret_cast<PyObject*>(
+ &PyFileDescriptor_Type));
+ PyModule_AddObject(m, "OneofDescriptor", reinterpret_cast<PyObject*>(
+ &PyOneofDescriptor_Type));
+ PyModule_AddObject(m, "ServiceDescriptor", reinterpret_cast<PyObject*>(
+ &PyServiceDescriptor_Type));
+ PyModule_AddObject(m, "MethodDescriptor", reinterpret_cast<PyObject*>(
+ &PyMethodDescriptor_Type));
+
+ PyObject* enum_type_wrapper = PyImport_ImportModule(
+ "google.protobuf.internal.enum_type_wrapper");
+ if (enum_type_wrapper == NULL) {
+ return false;
+ }
+ EnumTypeWrapper_class =
+ PyObject_GetAttrString(enum_type_wrapper, "EnumTypeWrapper");
+ Py_DECREF(enum_type_wrapper);
+
+ PyObject* message_module = PyImport_ImportModule(
+ "google.protobuf.message");
+ if (message_module == NULL) {
+ return false;
+ }
+ EncodeError_class = PyObject_GetAttrString(message_module, "EncodeError");
+ DecodeError_class = PyObject_GetAttrString(message_module, "DecodeError");
+ PythonMessage_class = PyObject_GetAttrString(message_module, "Message");
+ Py_DECREF(message_module);
+
+ PyObject* pickle_module = PyImport_ImportModule("pickle");
+ if (pickle_module == NULL) {
+ return false;
+ }
+ PickleError_class = PyObject_GetAttrString(pickle_module, "PickleError");
+ Py_DECREF(pickle_module);
+
+ // Override {Get,Mutable}CProtoInsidePyProto.
+ GetCProtoInsidePyProtoPtr = GetCProtoInsidePyProtoImpl;
+ MutableCProtoInsidePyProtoPtr = MutableCProtoInsidePyProtoImpl;
+
+ return true;
+}
+
+} // namespace python
+} // namespace protobuf
+
+} // namespace google
diff --git a/python/google/protobuf/pyext/message.h b/python/google/protobuf/pyext/message.h
new file mode 100644
index 00000000..8b399e05
--- /dev/null
+++ b/python/google/protobuf/pyext/message.h
@@ -0,0 +1,365 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Author: anuraag@google.com (Anuraag Agrawal)
+// Author: tibell@google.com (Johan Tibell)
+
+#ifndef GOOGLE_PROTOBUF_PYTHON_CPP_MESSAGE_H__
+#define GOOGLE_PROTOBUF_PYTHON_CPP_MESSAGE_H__
+
+#include <Python.h>
+
+#include <memory>
+#ifndef _SHARED_PTR_H
+#include <google/protobuf/stubs/shared_ptr.h>
+#endif
+#include <string>
+
+namespace google {
+namespace protobuf {
+
+class Message;
+class Reflection;
+class FieldDescriptor;
+class Descriptor;
+class DescriptorPool;
+class MessageFactory;
+
+#ifdef _SHARED_PTR_H
+using std::shared_ptr;
+using std::string;
+#else
+using internal::shared_ptr;
+#endif
+
+namespace python {
+
+struct ExtensionDict;
+struct PyDescriptorPool;
+
+typedef struct CMessage {
+ PyObject_HEAD;
+
+ // This is the top-level C++ Message object that owns the whole
+ // proto tree. Every Python CMessage holds a reference to it in
+ // order to keep it alive as long as there's a Python object that
+ // references any part of the tree.
+ shared_ptr<Message> owner;
+
+ // Weak reference to a parent CMessage object. This is NULL for any top-level
+ // message and is set for any child message (i.e. a child submessage or a
+ // part of a repeated composite field).
+ //
+ // Used to make sure all ancestors are also mutable when first modifying
+ // a child submessage (in other words, turning a default message instance
+ // into a mutable one).
+ //
+ // If a submessage is released (becomes a new top-level message), this field
+ // MUST be set to NULL. The parent may get deallocated and further attempts
+ // to use this pointer will result in a crash.
+ struct CMessage* parent;
+
+ // Pointer to the parent's descriptor that describes this submessage.
+ // Used together with the parent's message when making a default message
+ // instance mutable.
+ // The pointer is owned by the global DescriptorPool.
+ const FieldDescriptor* parent_field_descriptor;
+
+ // Pointer to the C++ Message object for this CMessage. The
+ // CMessage does not own this pointer.
+ Message* message;
+
+ // Indicates this submessage is pointing to a default instance of a message.
+ // Submessages are always first created as read only messages and are then
+ // made writable, at which point this field is set to false.
+ bool read_only;
+
+ // A reference to a Python dictionary containing CMessage,
+ // RepeatedCompositeContainer, and RepeatedScalarContainer
+ // objects. Used as a cache to make sure we don't have to make a
+ // Python wrapper for the C++ Message objects on every access, or
+ // deal with the synchronization nightmare that could create.
+ PyObject* composite_fields;
+
+ // A reference to the dictionary containing the message's extensions.
+ // Similar to composite_fields, acting as a cache, but also contains the
+ // required extension dict logic.
+ ExtensionDict* extensions;
+} CMessage;
+
+extern PyTypeObject CMessage_Type;
+
+
+// The (meta) type of all Messages classes.
+// It allows us to cache some C++ pointers in the class object itself, they are
+// faster to extract than from the type's dictionary.
+
+struct CMessageClass {
+ // This is how CPython subclasses C structures: the base structure must be
+ // the first member of the object.
+ PyHeapTypeObject super;
+
+ // C++ descriptor of this message.
+ const Descriptor* message_descriptor;
+
+ // Owned reference, used to keep the pointer above alive.
+ PyObject* py_message_descriptor;
+
+ // The Python DescriptorPool used to create the class. It is needed to resolve
+ // fields descriptors, including extensions fields; its C++ MessageFactory is
+ // used to instantiate submessages.
+ // This can be different from DESCRIPTOR.file.pool, in the case of a custom
+ // DescriptorPool which defines new extensions.
+ // We own the reference, because it's important to keep the descriptors and
+ // factory alive.
+ PyDescriptorPool* py_descriptor_pool;
+
+ PyObject* AsPyObject() {
+ return reinterpret_cast<PyObject*>(this);
+ }
+};
+
+
+namespace cmessage {
+
+// Internal function to create a new empty Message Python object, but with empty
+// pointers to the C++ objects.
+// The caller must fill self->message, self->owner and eventually self->parent.
+CMessage* NewEmptyMessage(CMessageClass* type);
+
+// Release a submessage from its proto tree, making it a new top-level messgae.
+// A new message will be created if this is a read-only default instance.
+//
+// Corresponds to reflection api method ReleaseMessage.
+int ReleaseSubMessage(CMessage* self,
+ const FieldDescriptor* field_descriptor,
+ CMessage* child_cmessage);
+
+// Retrieves the C++ descriptor of a Python Extension descriptor.
+// On error, return NULL with an exception set.
+const FieldDescriptor* GetExtensionDescriptor(PyObject* extension);
+
+// Initializes a new CMessage instance for a submessage. Only called once per
+// submessage as the result is cached in composite_fields.
+//
+// Corresponds to reflection api method GetMessage.
+PyObject* InternalGetSubMessage(
+ CMessage* self, const FieldDescriptor* field_descriptor);
+
+// Deletes a range of C++ submessages in a repeated field (following a
+// removal in a RepeatedCompositeContainer).
+//
+// Releases messages to the provided cmessage_list if it is not NULL rather
+// than just removing them from the underlying proto. This cmessage_list must
+// have a CMessage for each underlying submessage. The CMessages referred to
+// by slice will be removed from cmessage_list by this function.
+//
+// Corresponds to reflection api method RemoveLast.
+int InternalDeleteRepeatedField(CMessage* self,
+ const FieldDescriptor* field_descriptor,
+ PyObject* slice, PyObject* cmessage_list);
+
+// Sets the specified scalar value to the message.
+int InternalSetScalar(CMessage* self,
+ const FieldDescriptor* field_descriptor,
+ PyObject* value);
+
+// Sets the specified scalar value to the message. Requires it is not a Oneof.
+int InternalSetNonOneofScalar(Message* message,
+ const FieldDescriptor* field_descriptor,
+ PyObject* arg);
+
+// Retrieves the specified scalar value from the message.
+//
+// Returns a new python reference.
+PyObject* InternalGetScalar(const Message* message,
+ const FieldDescriptor* field_descriptor);
+
+// Clears the message, removing all contained data. Extension dictionary and
+// submessages are released first if there are remaining external references.
+//
+// Corresponds to message api method Clear.
+PyObject* Clear(CMessage* self);
+
+// Clears the data described by the given descriptor. Used to clear extensions
+// (which don't have names). Extension release is handled by ExtensionDict
+// class, not this function.
+// TODO(anuraag): Try to make this discrepancy in release semantics with
+// ClearField less confusing.
+//
+// Corresponds to reflection api method ClearField.
+PyObject* ClearFieldByDescriptor(
+ CMessage* self, const FieldDescriptor* descriptor);
+
+// Clears the data for the given field name. The message is released if there
+// are any external references.
+//
+// Corresponds to reflection api method ClearField.
+PyObject* ClearField(CMessage* self, PyObject* arg);
+
+// Checks if the message has the field described by the descriptor. Used for
+// extensions (which have no name).
+//
+// Corresponds to reflection api method HasField
+PyObject* HasFieldByDescriptor(
+ CMessage* self, const FieldDescriptor* field_descriptor);
+
+// Checks if the message has the named field.
+//
+// Corresponds to reflection api method HasField.
+PyObject* HasField(CMessage* self, PyObject* arg);
+
+// Initializes values of fields on a newly constructed message.
+int InitAttributes(CMessage* self, PyObject* kwargs);
+
+PyObject* MergeFrom(CMessage* self, PyObject* arg);
+
+// Retrieves an attribute named 'name' from CMessage 'self'. Returns
+// the attribute value on success, or NULL on failure.
+//
+// Returns a new reference.
+PyObject* GetAttr(CMessage* self, PyObject* name);
+
+// Set the value of the attribute named 'name', for CMessage 'self',
+// to the value 'value'. Returns -1 on failure.
+int SetAttr(CMessage* self, PyObject* name, PyObject* value);
+
+PyObject* FindInitializationErrors(CMessage* self);
+
+// Set the owner field of self and any children of self, recursively.
+// Used when self is being released and thus has a new owner (the
+// released Message.)
+int SetOwner(CMessage* self, const shared_ptr<Message>& new_owner);
+
+int AssureWritable(CMessage* self);
+
+// Returns the "best" DescriptorPool for the given message.
+// This is often equivalent to message.DESCRIPTOR.pool, but not always, when
+// the message class was created from a MessageFactory using a custom pool which
+// uses the generated pool as an underlay.
+//
+// The returned pool is suitable for finding fields and building submessages,
+// even in the case of extensions.
+PyDescriptorPool* GetDescriptorPoolForMessage(CMessage* message);
+
+PyObject* SetAllowOversizeProtos(PyObject* m, PyObject* arg);
+
+} // namespace cmessage
+
+
+/* Is 64bit */
+#define IS_64BIT (SIZEOF_LONG == 8)
+
+#define FIELD_IS_REPEATED(field_descriptor) \
+ ((field_descriptor)->label() == FieldDescriptor::LABEL_REPEATED)
+
+#define GOOGLE_CHECK_GET_INT32(arg, value, err) \
+ int32 value; \
+ if (!CheckAndGetInteger(arg, &value, kint32min_py, kint32max_py)) { \
+ return err; \
+ }
+
+#define GOOGLE_CHECK_GET_INT64(arg, value, err) \
+ int64 value; \
+ if (!CheckAndGetInteger(arg, &value, kint64min_py, kint64max_py)) { \
+ return err; \
+ }
+
+#define GOOGLE_CHECK_GET_UINT32(arg, value, err) \
+ uint32 value; \
+ if (!CheckAndGetInteger(arg, &value, kPythonZero, kuint32max_py)) { \
+ return err; \
+ }
+
+#define GOOGLE_CHECK_GET_UINT64(arg, value, err) \
+ uint64 value; \
+ if (!CheckAndGetInteger(arg, &value, kPythonZero, kuint64max_py)) { \
+ return err; \
+ }
+
+#define GOOGLE_CHECK_GET_FLOAT(arg, value, err) \
+ float value; \
+ if (!CheckAndGetFloat(arg, &value)) { \
+ return err; \
+ } \
+
+#define GOOGLE_CHECK_GET_DOUBLE(arg, value, err) \
+ double value; \
+ if (!CheckAndGetDouble(arg, &value)) { \
+ return err; \
+ }
+
+#define GOOGLE_CHECK_GET_BOOL(arg, value, err) \
+ bool value; \
+ if (!CheckAndGetBool(arg, &value)) { \
+ return err; \
+ }
+
+
+extern PyObject* kPythonZero;
+extern PyObject* kint32min_py;
+extern PyObject* kint32max_py;
+extern PyObject* kuint32max_py;
+extern PyObject* kint64min_py;
+extern PyObject* kint64max_py;
+extern PyObject* kuint64max_py;
+
+#define FULL_MODULE_NAME "google.protobuf.pyext._message"
+
+void FormatTypeError(PyObject* arg, char* expected_types);
+template<class T>
+bool CheckAndGetInteger(
+ PyObject* arg, T* value, PyObject* min, PyObject* max);
+bool CheckAndGetDouble(PyObject* arg, double* value);
+bool CheckAndGetFloat(PyObject* arg, float* value);
+bool CheckAndGetBool(PyObject* arg, bool* value);
+PyObject* CheckString(PyObject* arg, const FieldDescriptor* descriptor);
+bool CheckAndSetString(
+ PyObject* arg, Message* message,
+ const FieldDescriptor* descriptor,
+ const Reflection* reflection,
+ bool append,
+ int index);
+PyObject* ToStringObject(const FieldDescriptor* descriptor, string value);
+
+// Check if the passed field descriptor belongs to the given message.
+// If not, return false and set a Python exception (a KeyError)
+bool CheckFieldBelongsToMessage(const FieldDescriptor* field_descriptor,
+ const Message* message);
+
+extern PyObject* PickleError_class;
+
+bool InitProto2MessageModule(PyObject *m);
+
+} // namespace python
+} // namespace protobuf
+
+} // namespace google
+#endif // GOOGLE_PROTOBUF_PYTHON_CPP_MESSAGE_H__
diff --git a/python/google/protobuf/pyext/message_module.cc b/python/google/protobuf/pyext/message_module.cc
new file mode 100644
index 00000000..d90d9de3
--- /dev/null
+++ b/python/google/protobuf/pyext/message_module.cc
@@ -0,0 +1,88 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <google/protobuf/pyext/message.h>
+
+static const char module_docstring[] =
+"python-proto2 is a module that can be used to enhance proto2 Python API\n"
+"performance.\n"
+"\n"
+"It provides access to the protocol buffers C++ reflection API that\n"
+"implements the basic protocol buffer functions.";
+
+static PyMethodDef ModuleMethods[] = {
+ {"SetAllowOversizeProtos",
+ (PyCFunction)google::protobuf::python::cmessage::SetAllowOversizeProtos,
+ METH_O, "Enable/disable oversize proto parsing."},
+ { NULL, NULL}
+};
+
+#if PY_MAJOR_VERSION >= 3
+static struct PyModuleDef _module = {
+ PyModuleDef_HEAD_INIT,
+ "_message",
+ module_docstring,
+ -1,
+ ModuleMethods, /* m_methods */
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+#define INITFUNC PyInit__message
+#define INITFUNC_ERRORVAL NULL
+#else // Python 2
+#define INITFUNC init_message
+#define INITFUNC_ERRORVAL
+#endif
+
+extern "C" {
+ PyMODINIT_FUNC INITFUNC(void) {
+ PyObject* m;
+#if PY_MAJOR_VERSION >= 3
+ m = PyModule_Create(&_module);
+#else
+ m = Py_InitModule3("_message", ModuleMethods,
+ module_docstring);
+#endif
+ if (m == NULL) {
+ return INITFUNC_ERRORVAL;
+ }
+
+ if (!google::protobuf::python::InitProto2MessageModule(m)) {
+ Py_DECREF(m);
+ return INITFUNC_ERRORVAL;
+ }
+
+#if PY_MAJOR_VERSION >= 3
+ return m;
+#endif
+ }
+}
diff --git a/python/google/protobuf/pyext/proto2_api_test.proto b/python/google/protobuf/pyext/proto2_api_test.proto
new file mode 100644
index 00000000..18aecfb7
--- /dev/null
+++ b/python/google/protobuf/pyext/proto2_api_test.proto
@@ -0,0 +1,40 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+syntax = "proto2";
+
+import "google/protobuf/internal/cpp/proto1_api_test.proto";
+
+package google.protobuf.python.internal;
+
+message TestNestedProto1APIMessage {
+ optional int32 a = 1;
+ optional TestMessage.NestedMessage b = 2;
+}
diff --git a/python/google/protobuf/pyext/python.proto b/python/google/protobuf/pyext/python.proto
new file mode 100644
index 00000000..cce645d7
--- /dev/null
+++ b/python/google/protobuf/pyext/python.proto
@@ -0,0 +1,68 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Author: tibell@google.com (Johan Tibell)
+//
+// These message definitions are used to exercises known corner cases
+// in the C++ implementation of the Python API.
+
+syntax = "proto2";
+
+package google.protobuf.python.internal;
+
+// Protos optimized for SPEED use a strict superset of the generated code
+// of equivalent ones optimized for CODE_SIZE, so we should optimize all our
+// tests for speed unless explicitly testing code size optimization.
+option optimize_for = SPEED;
+
+message TestAllTypes {
+ message NestedMessage {
+ optional int32 bb = 1;
+ optional ForeignMessage cc = 2;
+ }
+
+ repeated NestedMessage repeated_nested_message = 1;
+ optional NestedMessage optional_nested_message = 2;
+ optional int32 optional_int32 = 3;
+}
+
+message ForeignMessage {
+ optional int32 c = 1;
+ repeated int32 d = 2;
+}
+
+message TestAllExtensions {
+ extensions 1 to max;
+}
+
+extend TestAllExtensions {
+ optional TestAllTypes.NestedMessage optional_nested_message_extension = 1;
+ repeated TestAllTypes.NestedMessage repeated_nested_message_extension = 2;
+}
diff --git a/python/google/protobuf/pyext/python_protobuf.h b/python/google/protobuf/pyext/python_protobuf.h
new file mode 100644
index 00000000..beb6e460
--- /dev/null
+++ b/python/google/protobuf/pyext/python_protobuf.h
@@ -0,0 +1,57 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Author: qrczak@google.com (Marcin Kowalczyk)
+//
+// This module exposes the C proto inside the given Python proto, in
+// case the Python proto is implemented with a C proto.
+
+#ifndef GOOGLE_PROTOBUF_PYTHON_PYTHON_PROTOBUF_H__
+#define GOOGLE_PROTOBUF_PYTHON_PYTHON_PROTOBUF_H__
+
+#include <Python.h>
+
+namespace google {
+namespace protobuf {
+
+class Message;
+
+namespace python {
+
+// Return the pointer to the C proto inside the given Python proto,
+// or NULL when this is not a Python proto implemented with a C proto.
+const Message* GetCProtoInsidePyProto(PyObject* msg);
+Message* MutableCProtoInsidePyProto(PyObject* msg);
+
+} // namespace python
+} // namespace protobuf
+
+} // namespace google
+#endif // GOOGLE_PROTOBUF_PYTHON_PYTHON_PROTOBUF_H__
diff --git a/python/google/protobuf/pyext/repeated_composite_container.cc b/python/google/protobuf/pyext/repeated_composite_container.cc
new file mode 100644
index 00000000..4f339e77
--- /dev/null
+++ b/python/google/protobuf/pyext/repeated_composite_container.cc
@@ -0,0 +1,612 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Author: anuraag@google.com (Anuraag Agrawal)
+// Author: tibell@google.com (Johan Tibell)
+
+#include <google/protobuf/pyext/repeated_composite_container.h>
+
+#include <memory>
+#ifndef _SHARED_PTR_H
+#include <google/protobuf/stubs/shared_ptr.h>
+#endif
+
+#include <google/protobuf/stubs/logging.h>
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/dynamic_message.h>
+#include <google/protobuf/message.h>
+#include <google/protobuf/pyext/descriptor.h>
+#include <google/protobuf/pyext/descriptor_pool.h>
+#include <google/protobuf/pyext/message.h>
+#include <google/protobuf/pyext/scoped_pyobject_ptr.h>
+
+#if PY_MAJOR_VERSION >= 3
+ #define PyInt_Check PyLong_Check
+ #define PyInt_AsLong PyLong_AsLong
+ #define PyInt_FromLong PyLong_FromLong
+#endif
+
+namespace google {
+namespace protobuf {
+namespace python {
+
+namespace repeated_composite_container {
+
+// TODO(tibell): We might also want to check:
+// GOOGLE_CHECK_NOTNULL((self)->owner.get());
+#define GOOGLE_CHECK_ATTACHED(self) \
+ do { \
+ GOOGLE_CHECK_NOTNULL((self)->message); \
+ GOOGLE_CHECK_NOTNULL((self)->parent_field_descriptor); \
+ } while (0);
+
+#define GOOGLE_CHECK_RELEASED(self) \
+ do { \
+ GOOGLE_CHECK((self)->owner.get() == NULL); \
+ GOOGLE_CHECK((self)->message == NULL); \
+ GOOGLE_CHECK((self)->parent_field_descriptor == NULL); \
+ GOOGLE_CHECK((self)->parent == NULL); \
+ } while (0);
+
+// ---------------------------------------------------------------------
+// len()
+
+static Py_ssize_t Length(RepeatedCompositeContainer* self) {
+ Message* message = self->message;
+ if (message != NULL) {
+ return message->GetReflection()->FieldSize(*message,
+ self->parent_field_descriptor);
+ } else {
+ // The container has been released (i.e. by a call to Clear() or
+ // ClearField() on the parent) and thus there's no message.
+ return PyList_GET_SIZE(self->child_messages);
+ }
+}
+
+// Returns 0 if successful; returns -1 and sets an exception if
+// unsuccessful.
+static int UpdateChildMessages(RepeatedCompositeContainer* self) {
+ if (self->message == NULL)
+ return 0;
+
+ // A MergeFrom on a parent message could have caused extra messages to be
+ // added in the underlying protobuf so add them to our list. They can never
+ // be removed in such a way so there's no need to worry about that.
+ Py_ssize_t message_length = Length(self);
+ Py_ssize_t child_length = PyList_GET_SIZE(self->child_messages);
+ Message* message = self->message;
+ const Reflection* reflection = message->GetReflection();
+ for (Py_ssize_t i = child_length; i < message_length; ++i) {
+ const Message& sub_message = reflection->GetRepeatedMessage(
+ *(self->message), self->parent_field_descriptor, i);
+ CMessage* cmsg = cmessage::NewEmptyMessage(self->child_message_class);
+ ScopedPyObjectPtr py_cmsg(reinterpret_cast<PyObject*>(cmsg));
+ if (cmsg == NULL) {
+ return -1;
+ }
+ cmsg->owner = self->owner;
+ cmsg->message = const_cast<Message*>(&sub_message);
+ cmsg->parent = self->parent;
+ if (PyList_Append(self->child_messages, py_cmsg.get()) < 0) {
+ return -1;
+ }
+ }
+ return 0;
+}
+
+// ---------------------------------------------------------------------
+// add()
+
+static PyObject* AddToAttached(RepeatedCompositeContainer* self,
+ PyObject* args,
+ PyObject* kwargs) {
+ GOOGLE_CHECK_ATTACHED(self);
+
+ if (UpdateChildMessages(self) < 0) {
+ return NULL;
+ }
+ if (cmessage::AssureWritable(self->parent) == -1)
+ return NULL;
+ Message* message = self->message;
+ Message* sub_message =
+ message->GetReflection()->AddMessage(message,
+ self->parent_field_descriptor);
+ CMessage* cmsg = cmessage::NewEmptyMessage(self->child_message_class);
+ if (cmsg == NULL)
+ return NULL;
+
+ cmsg->owner = self->owner;
+ cmsg->message = sub_message;
+ cmsg->parent = self->parent;
+ if (cmessage::InitAttributes(cmsg, kwargs) < 0) {
+ Py_DECREF(cmsg);
+ return NULL;
+ }
+
+ PyObject* py_cmsg = reinterpret_cast<PyObject*>(cmsg);
+ if (PyList_Append(self->child_messages, py_cmsg) < 0) {
+ Py_DECREF(py_cmsg);
+ return NULL;
+ }
+ return py_cmsg;
+}
+
+static PyObject* AddToReleased(RepeatedCompositeContainer* self,
+ PyObject* args,
+ PyObject* kwargs) {
+ GOOGLE_CHECK_RELEASED(self);
+
+ // Create a new Message detached from the rest.
+ PyObject* py_cmsg = PyEval_CallObjectWithKeywords(
+ self->child_message_class->AsPyObject(), NULL, kwargs);
+ if (py_cmsg == NULL)
+ return NULL;
+
+ if (PyList_Append(self->child_messages, py_cmsg) < 0) {
+ Py_DECREF(py_cmsg);
+ return NULL;
+ }
+ return py_cmsg;
+}
+
+PyObject* Add(RepeatedCompositeContainer* self,
+ PyObject* args,
+ PyObject* kwargs) {
+ if (self->message == NULL)
+ return AddToReleased(self, args, kwargs);
+ else
+ return AddToAttached(self, args, kwargs);
+}
+
+// ---------------------------------------------------------------------
+// extend()
+
+PyObject* Extend(RepeatedCompositeContainer* self, PyObject* value) {
+ cmessage::AssureWritable(self->parent);
+ if (UpdateChildMessages(self) < 0) {
+ return NULL;
+ }
+ ScopedPyObjectPtr iter(PyObject_GetIter(value));
+ if (iter == NULL) {
+ PyErr_SetString(PyExc_TypeError, "Value must be iterable");
+ return NULL;
+ }
+ ScopedPyObjectPtr next;
+ while ((next.reset(PyIter_Next(iter.get()))) != NULL) {
+ if (!PyObject_TypeCheck(next.get(), &CMessage_Type)) {
+ PyErr_SetString(PyExc_TypeError, "Not a cmessage");
+ return NULL;
+ }
+ ScopedPyObjectPtr new_message(Add(self, NULL, NULL));
+ if (new_message == NULL) {
+ return NULL;
+ }
+ CMessage* new_cmessage = reinterpret_cast<CMessage*>(new_message.get());
+ if (ScopedPyObjectPtr(cmessage::MergeFrom(new_cmessage, next.get())) ==
+ NULL) {
+ return NULL;
+ }
+ }
+ if (PyErr_Occurred()) {
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
+PyObject* MergeFrom(RepeatedCompositeContainer* self, PyObject* other) {
+ if (UpdateChildMessages(self) < 0) {
+ return NULL;
+ }
+ return Extend(self, other);
+}
+
+PyObject* Subscript(RepeatedCompositeContainer* self, PyObject* slice) {
+ if (UpdateChildMessages(self) < 0) {
+ return NULL;
+ }
+ // Just forward the call to the subscript-handling function of the
+ // list containing the child messages.
+ return PyObject_GetItem(self->child_messages, slice);
+}
+
+int AssignSubscript(RepeatedCompositeContainer* self,
+ PyObject* slice,
+ PyObject* value) {
+ if (UpdateChildMessages(self) < 0) {
+ return -1;
+ }
+ if (value != NULL) {
+ PyErr_SetString(PyExc_TypeError, "does not support assignment");
+ return -1;
+ }
+
+ // Delete from the underlying Message, if any.
+ if (self->parent != NULL) {
+ if (cmessage::InternalDeleteRepeatedField(self->parent,
+ self->parent_field_descriptor,
+ slice,
+ self->child_messages) < 0) {
+ return -1;
+ }
+ } else {
+ Py_ssize_t from;
+ Py_ssize_t to;
+ Py_ssize_t step;
+ Py_ssize_t length = Length(self);
+ Py_ssize_t slicelength;
+ if (PySlice_Check(slice)) {
+#if PY_MAJOR_VERSION >= 3
+ if (PySlice_GetIndicesEx(slice,
+#else
+ if (PySlice_GetIndicesEx(reinterpret_cast<PySliceObject*>(slice),
+#endif
+ length, &from, &to, &step, &slicelength) == -1) {
+ return -1;
+ }
+ return PySequence_DelSlice(self->child_messages, from, to);
+ } else if (PyInt_Check(slice) || PyLong_Check(slice)) {
+ from = to = PyLong_AsLong(slice);
+ if (from < 0) {
+ from = to = length + from;
+ }
+ return PySequence_DelItem(self->child_messages, from);
+ }
+ }
+
+ return 0;
+}
+
+static PyObject* Remove(RepeatedCompositeContainer* self, PyObject* value) {
+ if (UpdateChildMessages(self) < 0) {
+ return NULL;
+ }
+ Py_ssize_t index = PySequence_Index(self->child_messages, value);
+ if (index == -1) {
+ return NULL;
+ }
+ ScopedPyObjectPtr py_index(PyLong_FromLong(index));
+ if (AssignSubscript(self, py_index.get(), NULL) < 0) {
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
+static PyObject* RichCompare(RepeatedCompositeContainer* self,
+ PyObject* other,
+ int opid) {
+ if (UpdateChildMessages(self) < 0) {
+ return NULL;
+ }
+ if (!PyObject_TypeCheck(other, &RepeatedCompositeContainer_Type)) {
+ PyErr_SetString(PyExc_TypeError,
+ "Can only compare repeated composite fields "
+ "against other repeated composite fields.");
+ return NULL;
+ }
+ if (opid == Py_EQ || opid == Py_NE) {
+ // TODO(anuraag): Don't make new lists just for this...
+ ScopedPyObjectPtr full_slice(PySlice_New(NULL, NULL, NULL));
+ if (full_slice == NULL) {
+ return NULL;
+ }
+ ScopedPyObjectPtr list(Subscript(self, full_slice.get()));
+ if (list == NULL) {
+ return NULL;
+ }
+ ScopedPyObjectPtr other_list(
+ Subscript(reinterpret_cast<RepeatedCompositeContainer*>(other),
+ full_slice.get()));
+ if (other_list == NULL) {
+ return NULL;
+ }
+ return PyObject_RichCompare(list.get(), other_list.get(), opid);
+ } else {
+ Py_INCREF(Py_NotImplemented);
+ return Py_NotImplemented;
+ }
+}
+
+// ---------------------------------------------------------------------
+// sort()
+
+static void ReorderAttached(RepeatedCompositeContainer* self) {
+ Message* message = self->message;
+ const Reflection* reflection = message->GetReflection();
+ const FieldDescriptor* descriptor = self->parent_field_descriptor;
+ const Py_ssize_t length = Length(self);
+
+ // Since Python protobuf objects are never arena-allocated, adding and
+ // removing message pointers to the underlying array is just updating
+ // pointers.
+ for (Py_ssize_t i = 0; i < length; ++i)
+ reflection->ReleaseLast(message, descriptor);
+
+ for (Py_ssize_t i = 0; i < length; ++i) {
+ CMessage* py_cmsg = reinterpret_cast<CMessage*>(
+ PyList_GET_ITEM(self->child_messages, i));
+ reflection->AddAllocatedMessage(message, descriptor, py_cmsg->message);
+ }
+}
+
+// Returns 0 if successful; returns -1 and sets an exception if
+// unsuccessful.
+static int SortPythonMessages(RepeatedCompositeContainer* self,
+ PyObject* args,
+ PyObject* kwds) {
+ ScopedPyObjectPtr m(PyObject_GetAttrString(self->child_messages, "sort"));
+ if (m == NULL)
+ return -1;
+ if (PyObject_Call(m.get(), args, kwds) == NULL)
+ return -1;
+ if (self->message != NULL) {
+ ReorderAttached(self);
+ }
+ return 0;
+}
+
+static PyObject* Sort(RepeatedCompositeContainer* self,
+ PyObject* args,
+ PyObject* kwds) {
+ // Support the old sort_function argument for backwards
+ // compatibility.
+ if (kwds != NULL) {
+ PyObject* sort_func = PyDict_GetItemString(kwds, "sort_function");
+ if (sort_func != NULL) {
+ // Must set before deleting as sort_func is a borrowed reference
+ // and kwds might be the only thing keeping it alive.
+ PyDict_SetItemString(kwds, "cmp", sort_func);
+ PyDict_DelItemString(kwds, "sort_function");
+ }
+ }
+
+ if (UpdateChildMessages(self) < 0) {
+ return NULL;
+ }
+ if (SortPythonMessages(self, args, kwds) < 0) {
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
+// ---------------------------------------------------------------------
+
+static PyObject* Item(RepeatedCompositeContainer* self, Py_ssize_t index) {
+ if (UpdateChildMessages(self) < 0) {
+ return NULL;
+ }
+ Py_ssize_t length = Length(self);
+ if (index < 0) {
+ index = length + index;
+ }
+ PyObject* item = PyList_GetItem(self->child_messages, index);
+ if (item == NULL) {
+ return NULL;
+ }
+ Py_INCREF(item);
+ return item;
+}
+
+static PyObject* Pop(RepeatedCompositeContainer* self,
+ PyObject* args) {
+ Py_ssize_t index = -1;
+ if (!PyArg_ParseTuple(args, "|n", &index)) {
+ return NULL;
+ }
+ PyObject* item = Item(self, index);
+ if (item == NULL) {
+ PyErr_Format(PyExc_IndexError,
+ "list index (%zd) out of range",
+ index);
+ return NULL;
+ }
+ ScopedPyObjectPtr py_index(PyLong_FromSsize_t(index));
+ if (AssignSubscript(self, py_index.get(), NULL) < 0) {
+ return NULL;
+ }
+ return item;
+}
+
+// Release field of parent message and transfer the ownership to target.
+void ReleaseLastTo(CMessage* parent,
+ const FieldDescriptor* field,
+ CMessage* target) {
+ GOOGLE_CHECK_NOTNULL(parent);
+ GOOGLE_CHECK_NOTNULL(field);
+ GOOGLE_CHECK_NOTNULL(target);
+
+ shared_ptr<Message> released_message(
+ parent->message->GetReflection()->ReleaseLast(parent->message, field));
+ // TODO(tibell): Deal with proto1.
+
+ target->parent = NULL;
+ target->parent_field_descriptor = NULL;
+ target->message = released_message.get();
+ target->read_only = false;
+ cmessage::SetOwner(target, released_message);
+}
+
+// Called to release a container using
+// ClearField('container_field_name') on the parent.
+int Release(RepeatedCompositeContainer* self) {
+ if (UpdateChildMessages(self) < 0) {
+ PyErr_WriteUnraisable(PyBytes_FromString("Failed to update released "
+ "messages"));
+ return -1;
+ }
+
+ Message* message = self->message;
+ const FieldDescriptor* field = self->parent_field_descriptor;
+
+ // The reflection API only lets us release the last message in a
+ // repeated field. Therefore we iterate through the children
+ // starting with the last one.
+ const Py_ssize_t size = PyList_GET_SIZE(self->child_messages);
+ GOOGLE_DCHECK_EQ(size, message->GetReflection()->FieldSize(*message, field));
+ for (Py_ssize_t i = size - 1; i >= 0; --i) {
+ CMessage* child_cmessage = reinterpret_cast<CMessage*>(
+ PyList_GET_ITEM(self->child_messages, i));
+ ReleaseLastTo(self->parent, field, child_cmessage);
+ }
+
+ // Detach from containing message.
+ self->parent = NULL;
+ self->parent_field_descriptor = NULL;
+ self->message = NULL;
+ self->owner.reset();
+
+ return 0;
+}
+
+int SetOwner(RepeatedCompositeContainer* self,
+ const shared_ptr<Message>& new_owner) {
+ GOOGLE_CHECK_ATTACHED(self);
+
+ self->owner = new_owner;
+ const Py_ssize_t n = PyList_GET_SIZE(self->child_messages);
+ for (Py_ssize_t i = 0; i < n; ++i) {
+ PyObject* msg = PyList_GET_ITEM(self->child_messages, i);
+ if (cmessage::SetOwner(reinterpret_cast<CMessage*>(msg), new_owner) == -1) {
+ return -1;
+ }
+ }
+ return 0;
+}
+
+// The private constructor of RepeatedCompositeContainer objects.
+PyObject *NewContainer(
+ CMessage* parent,
+ const FieldDescriptor* parent_field_descriptor,
+ CMessageClass* concrete_class) {
+ if (!CheckFieldBelongsToMessage(parent_field_descriptor, parent->message)) {
+ return NULL;
+ }
+
+ RepeatedCompositeContainer* self =
+ reinterpret_cast<RepeatedCompositeContainer*>(
+ PyType_GenericAlloc(&RepeatedCompositeContainer_Type, 0));
+ if (self == NULL) {
+ return NULL;
+ }
+
+ self->message = parent->message;
+ self->parent = parent;
+ self->parent_field_descriptor = parent_field_descriptor;
+ self->owner = parent->owner;
+ Py_INCREF(concrete_class);
+ self->child_message_class = concrete_class;
+ self->child_messages = PyList_New(0);
+
+ return reinterpret_cast<PyObject*>(self);
+}
+
+static void Dealloc(RepeatedCompositeContainer* self) {
+ Py_CLEAR(self->child_messages);
+ Py_CLEAR(self->child_message_class);
+ // TODO(tibell): Do we need to call delete on these objects to make
+ // sure their destructors are called?
+ self->owner.reset();
+
+ Py_TYPE(self)->tp_free(reinterpret_cast<PyObject*>(self));
+}
+
+static PySequenceMethods SqMethods = {
+ (lenfunc)Length, /* sq_length */
+ 0, /* sq_concat */
+ 0, /* sq_repeat */
+ (ssizeargfunc)Item /* sq_item */
+};
+
+static PyMappingMethods MpMethods = {
+ (lenfunc)Length, /* mp_length */
+ (binaryfunc)Subscript, /* mp_subscript */
+ (objobjargproc)AssignSubscript,/* mp_ass_subscript */
+};
+
+static PyMethodDef Methods[] = {
+ { "add", (PyCFunction) Add, METH_VARARGS | METH_KEYWORDS,
+ "Adds an object to the repeated container." },
+ { "extend", (PyCFunction) Extend, METH_O,
+ "Adds objects to the repeated container." },
+ { "pop", (PyCFunction)Pop, METH_VARARGS,
+ "Removes an object from the repeated container and returns it." },
+ { "remove", (PyCFunction) Remove, METH_O,
+ "Removes an object from the repeated container." },
+ { "sort", (PyCFunction) Sort, METH_VARARGS | METH_KEYWORDS,
+ "Sorts the repeated container." },
+ { "MergeFrom", (PyCFunction) MergeFrom, METH_O,
+ "Adds objects to the repeated container." },
+ { NULL, NULL }
+};
+
+} // namespace repeated_composite_container
+
+PyTypeObject RepeatedCompositeContainer_Type = {
+ PyVarObject_HEAD_INIT(&PyType_Type, 0)
+ FULL_MODULE_NAME ".RepeatedCompositeContainer", // tp_name
+ sizeof(RepeatedCompositeContainer), // tp_basicsize
+ 0, // tp_itemsize
+ (destructor)repeated_composite_container::Dealloc, // tp_dealloc
+ 0, // tp_print
+ 0, // tp_getattr
+ 0, // tp_setattr
+ 0, // tp_compare
+ 0, // tp_repr
+ 0, // tp_as_number
+ &repeated_composite_container::SqMethods, // tp_as_sequence
+ &repeated_composite_container::MpMethods, // tp_as_mapping
+ PyObject_HashNotImplemented, // tp_hash
+ 0, // tp_call
+ 0, // tp_str
+ 0, // tp_getattro
+ 0, // tp_setattro
+ 0, // tp_as_buffer
+ Py_TPFLAGS_DEFAULT, // tp_flags
+ "A Repeated scalar container", // tp_doc
+ 0, // tp_traverse
+ 0, // tp_clear
+ (richcmpfunc)repeated_composite_container::RichCompare, // tp_richcompare
+ 0, // tp_weaklistoffset
+ 0, // tp_iter
+ 0, // tp_iternext
+ repeated_composite_container::Methods, // tp_methods
+ 0, // tp_members
+ 0, // tp_getset
+ 0, // tp_base
+ 0, // tp_dict
+ 0, // tp_descr_get
+ 0, // tp_descr_set
+ 0, // tp_dictoffset
+ 0, // tp_init
+};
+
+} // namespace python
+} // namespace protobuf
+} // namespace google
diff --git a/python/google/protobuf/pyext/repeated_composite_container.h b/python/google/protobuf/pyext/repeated_composite_container.h
new file mode 100644
index 00000000..a7b56b61
--- /dev/null
+++ b/python/google/protobuf/pyext/repeated_composite_container.h
@@ -0,0 +1,179 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Author: anuraag@google.com (Anuraag Agrawal)
+// Author: tibell@google.com (Johan Tibell)
+
+#ifndef GOOGLE_PROTOBUF_PYTHON_CPP_REPEATED_COMPOSITE_CONTAINER_H__
+#define GOOGLE_PROTOBUF_PYTHON_CPP_REPEATED_COMPOSITE_CONTAINER_H__
+
+#include <Python.h>
+
+#include <memory>
+#ifndef _SHARED_PTR_H
+#include <google/protobuf/stubs/shared_ptr.h>
+#endif
+#include <string>
+#include <vector>
+
+namespace google {
+namespace protobuf {
+
+class FieldDescriptor;
+class Message;
+
+#ifdef _SHARED_PTR_H
+using std::shared_ptr;
+#else
+using internal::shared_ptr;
+#endif
+
+namespace python {
+
+struct CMessage;
+struct CMessageClass;
+
+// A RepeatedCompositeContainer can be in one of two states: attached
+// or released.
+//
+// When in the attached state all modifications to the container are
+// done both on the 'message' and on the 'child_messages'
+// list. In this state all Messages referred to by the children in
+// 'child_messages' are owner by the 'owner'.
+//
+// When in the released state 'message', 'owner', 'parent', and
+// 'parent_field_descriptor' are NULL.
+typedef struct RepeatedCompositeContainer {
+ PyObject_HEAD;
+
+ // This is the top-level C++ Message object that owns the whole
+ // proto tree. Every Python RepeatedCompositeContainer holds a
+ // reference to it in order to keep it alive as long as there's a
+ // Python object that references any part of the tree.
+ shared_ptr<Message> owner;
+
+ // Weak reference to parent object. May be NULL. Used to make sure
+ // the parent is writable before modifying the
+ // RepeatedCompositeContainer.
+ CMessage* parent;
+
+ // A descriptor used to modify the underlying 'message'.
+ // The pointer is owned by the global DescriptorPool.
+ const FieldDescriptor* parent_field_descriptor;
+
+ // Pointer to the C++ Message that contains this container. The
+ // RepeatedCompositeContainer does not own this pointer.
+ //
+ // If NULL, this message has been released from its parent (by
+ // calling Clear() or ClearField() on the parent.
+ Message* message;
+
+ // The type used to create new child messages.
+ CMessageClass* child_message_class;
+
+ // A list of child messages.
+ PyObject* child_messages;
+} RepeatedCompositeContainer;
+
+extern PyTypeObject RepeatedCompositeContainer_Type;
+
+namespace repeated_composite_container {
+
+// Builds a RepeatedCompositeContainer object, from a parent message and a
+// field descriptor.
+PyObject *NewContainer(
+ CMessage* parent,
+ const FieldDescriptor* parent_field_descriptor,
+ CMessageClass *child_message_class);
+
+// Appends a new CMessage to the container and returns it. The
+// CMessage is initialized using the content of kwargs.
+//
+// Returns a new reference if successful; returns NULL and sets an
+// exception if unsuccessful.
+PyObject* Add(RepeatedCompositeContainer* self,
+ PyObject* args,
+ PyObject* kwargs);
+
+// Appends all the CMessages in the input iterator to the container.
+//
+// Returns None if successful; returns NULL and sets an exception if
+// unsuccessful.
+PyObject* Extend(RepeatedCompositeContainer* self, PyObject* value);
+
+// Appends a new message to the container for each message in the
+// input iterator, merging each data element in. Equivalent to extend.
+//
+// Returns None if successful; returns NULL and sets an exception if
+// unsuccessful.
+PyObject* MergeFrom(RepeatedCompositeContainer* self, PyObject* other);
+
+// Accesses messages in the container.
+//
+// Returns a new reference to the message for an integer parameter.
+// Returns a new reference to a list of messages for a slice.
+PyObject* Subscript(RepeatedCompositeContainer* self, PyObject* slice);
+
+// Deletes items from the container (cannot be used for assignment).
+//
+// Returns 0 on success, -1 on failure.
+int AssignSubscript(RepeatedCompositeContainer* self,
+ PyObject* slice,
+ PyObject* value);
+
+// Releases the messages in the container to the given message.
+//
+// Returns 0 on success, -1 on failure.
+int ReleaseToMessage(RepeatedCompositeContainer* self, Message* new_message);
+
+// Releases the messages in the container to a new message.
+//
+// Returns 0 on success, -1 on failure.
+int Release(RepeatedCompositeContainer* self);
+
+// Returns 0 on success, -1 on failure.
+int SetOwner(RepeatedCompositeContainer* self,
+ const shared_ptr<Message>& new_owner);
+
+// Removes the last element of the repeated message field 'field' on
+// the Message 'parent', and transfers the ownership of the released
+// Message to 'target'.
+//
+// Corresponds to reflection api method ReleaseMessage.
+void ReleaseLastTo(CMessage* parent,
+ const FieldDescriptor* field,
+ CMessage* target);
+
+} // namespace repeated_composite_container
+} // namespace python
+} // namespace protobuf
+
+} // namespace google
+#endif // GOOGLE_PROTOBUF_PYTHON_CPP_REPEATED_COMPOSITE_CONTAINER_H__
diff --git a/python/google/protobuf/pyext/repeated_scalar_container.cc b/python/google/protobuf/pyext/repeated_scalar_container.cc
new file mode 100644
index 00000000..95da85f8
--- /dev/null
+++ b/python/google/protobuf/pyext/repeated_scalar_container.cc
@@ -0,0 +1,812 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Author: anuraag@google.com (Anuraag Agrawal)
+// Author: tibell@google.com (Johan Tibell)
+
+#include <google/protobuf/pyext/repeated_scalar_container.h>
+
+#include <memory>
+#ifndef _SHARED_PTR_H
+#include <google/protobuf/stubs/shared_ptr.h>
+#endif
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/stubs/logging.h>
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/dynamic_message.h>
+#include <google/protobuf/message.h>
+#include <google/protobuf/pyext/descriptor.h>
+#include <google/protobuf/pyext/descriptor_pool.h>
+#include <google/protobuf/pyext/message.h>
+#include <google/protobuf/pyext/scoped_pyobject_ptr.h>
+
+#if PY_MAJOR_VERSION >= 3
+ #define PyInt_FromLong PyLong_FromLong
+ #if PY_VERSION_HEX < 0x03030000
+ #error "Python 3.0 - 3.2 are not supported."
+ #else
+ #define PyString_AsString(ob) \
+ (PyUnicode_Check(ob)? PyUnicode_AsUTF8(ob): PyBytes_AsString(ob))
+ #endif
+#endif
+
+namespace google {
+namespace protobuf {
+namespace python {
+
+namespace repeated_scalar_container {
+
+static int InternalAssignRepeatedField(
+ RepeatedScalarContainer* self, PyObject* list) {
+ self->message->GetReflection()->ClearField(self->message,
+ self->parent_field_descriptor);
+ for (Py_ssize_t i = 0; i < PyList_GET_SIZE(list); ++i) {
+ PyObject* value = PyList_GET_ITEM(list, i);
+ if (ScopedPyObjectPtr(Append(self, value)) == NULL) {
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static Py_ssize_t Len(RepeatedScalarContainer* self) {
+ Message* message = self->message;
+ return message->GetReflection()->FieldSize(*message,
+ self->parent_field_descriptor);
+}
+
+static int AssignItem(RepeatedScalarContainer* self,
+ Py_ssize_t index,
+ PyObject* arg) {
+ cmessage::AssureWritable(self->parent);
+ Message* message = self->message;
+ const FieldDescriptor* field_descriptor = self->parent_field_descriptor;
+
+ const Reflection* reflection = message->GetReflection();
+ int field_size = reflection->FieldSize(*message, field_descriptor);
+ if (index < 0) {
+ index = field_size + index;
+ }
+ if (index < 0 || index >= field_size) {
+ PyErr_Format(PyExc_IndexError,
+ "list assignment index (%d) out of range",
+ static_cast<int>(index));
+ return -1;
+ }
+
+ if (arg == NULL) {
+ ScopedPyObjectPtr py_index(PyLong_FromLong(index));
+ return cmessage::InternalDeleteRepeatedField(self->parent, field_descriptor,
+ py_index.get(), NULL);
+ }
+
+ if (PySequence_Check(arg) && !(PyBytes_Check(arg) || PyUnicode_Check(arg))) {
+ PyErr_SetString(PyExc_TypeError, "Value must be scalar");
+ return -1;
+ }
+
+ switch (field_descriptor->cpp_type()) {
+ case FieldDescriptor::CPPTYPE_INT32: {
+ GOOGLE_CHECK_GET_INT32(arg, value, -1);
+ reflection->SetRepeatedInt32(message, field_descriptor, index, value);
+ break;
+ }
+ case FieldDescriptor::CPPTYPE_INT64: {
+ GOOGLE_CHECK_GET_INT64(arg, value, -1);
+ reflection->SetRepeatedInt64(message, field_descriptor, index, value);
+ break;
+ }
+ case FieldDescriptor::CPPTYPE_UINT32: {
+ GOOGLE_CHECK_GET_UINT32(arg, value, -1);
+ reflection->SetRepeatedUInt32(message, field_descriptor, index, value);
+ break;
+ }
+ case FieldDescriptor::CPPTYPE_UINT64: {
+ GOOGLE_CHECK_GET_UINT64(arg, value, -1);
+ reflection->SetRepeatedUInt64(message, field_descriptor, index, value);
+ break;
+ }
+ case FieldDescriptor::CPPTYPE_FLOAT: {
+ GOOGLE_CHECK_GET_FLOAT(arg, value, -1);
+ reflection->SetRepeatedFloat(message, field_descriptor, index, value);
+ break;
+ }
+ case FieldDescriptor::CPPTYPE_DOUBLE: {
+ GOOGLE_CHECK_GET_DOUBLE(arg, value, -1);
+ reflection->SetRepeatedDouble(message, field_descriptor, index, value);
+ break;
+ }
+ case FieldDescriptor::CPPTYPE_BOOL: {
+ GOOGLE_CHECK_GET_BOOL(arg, value, -1);
+ reflection->SetRepeatedBool(message, field_descriptor, index, value);
+ break;
+ }
+ case FieldDescriptor::CPPTYPE_STRING: {
+ if (!CheckAndSetString(
+ arg, message, field_descriptor, reflection, false, index)) {
+ return -1;
+ }
+ break;
+ }
+ case FieldDescriptor::CPPTYPE_ENUM: {
+ GOOGLE_CHECK_GET_INT32(arg, value, -1);
+ if (reflection->SupportsUnknownEnumValues()) {
+ reflection->SetRepeatedEnumValue(message, field_descriptor, index,
+ value);
+ } else {
+ const EnumDescriptor* enum_descriptor = field_descriptor->enum_type();
+ const EnumValueDescriptor* enum_value =
+ enum_descriptor->FindValueByNumber(value);
+ if (enum_value != NULL) {
+ reflection->SetRepeatedEnum(message, field_descriptor, index,
+ enum_value);
+ } else {
+ ScopedPyObjectPtr s(PyObject_Str(arg));
+ if (s != NULL) {
+ PyErr_Format(PyExc_ValueError, "Unknown enum value: %s",
+ PyString_AsString(s.get()));
+ }
+ return -1;
+ }
+ }
+ break;
+ }
+ default:
+ PyErr_Format(
+ PyExc_SystemError, "Adding value to a field of unknown type %d",
+ field_descriptor->cpp_type());
+ return -1;
+ }
+ return 0;
+}
+
+static PyObject* Item(RepeatedScalarContainer* self, Py_ssize_t index) {
+ Message* message = self->message;
+ const FieldDescriptor* field_descriptor = self->parent_field_descriptor;
+ const Reflection* reflection = message->GetReflection();
+
+ int field_size = reflection->FieldSize(*message, field_descriptor);
+ if (index < 0) {
+ index = field_size + index;
+ }
+ if (index < 0 || index >= field_size) {
+ PyErr_Format(PyExc_IndexError,
+ "list index (%zd) out of range",
+ index);
+ return NULL;
+ }
+
+ PyObject* result = NULL;
+ switch (field_descriptor->cpp_type()) {
+ case FieldDescriptor::CPPTYPE_INT32: {
+ int32 value = reflection->GetRepeatedInt32(
+ *message, field_descriptor, index);
+ result = PyInt_FromLong(value);
+ break;
+ }
+ case FieldDescriptor::CPPTYPE_INT64: {
+ int64 value = reflection->GetRepeatedInt64(
+ *message, field_descriptor, index);
+ result = PyLong_FromLongLong(value);
+ break;
+ }
+ case FieldDescriptor::CPPTYPE_UINT32: {
+ uint32 value = reflection->GetRepeatedUInt32(
+ *message, field_descriptor, index);
+ result = PyLong_FromLongLong(value);
+ break;
+ }
+ case FieldDescriptor::CPPTYPE_UINT64: {
+ uint64 value = reflection->GetRepeatedUInt64(
+ *message, field_descriptor, index);
+ result = PyLong_FromUnsignedLongLong(value);
+ break;
+ }
+ case FieldDescriptor::CPPTYPE_FLOAT: {
+ float value = reflection->GetRepeatedFloat(
+ *message, field_descriptor, index);
+ result = PyFloat_FromDouble(value);
+ break;
+ }
+ case FieldDescriptor::CPPTYPE_DOUBLE: {
+ double value = reflection->GetRepeatedDouble(
+ *message, field_descriptor, index);
+ result = PyFloat_FromDouble(value);
+ break;
+ }
+ case FieldDescriptor::CPPTYPE_BOOL: {
+ bool value = reflection->GetRepeatedBool(
+ *message, field_descriptor, index);
+ result = PyBool_FromLong(value ? 1 : 0);
+ break;
+ }
+ case FieldDescriptor::CPPTYPE_ENUM: {
+ const EnumValueDescriptor* enum_value =
+ message->GetReflection()->GetRepeatedEnum(
+ *message, field_descriptor, index);
+ result = PyInt_FromLong(enum_value->number());
+ break;
+ }
+ case FieldDescriptor::CPPTYPE_STRING: {
+ string value = reflection->GetRepeatedString(
+ *message, field_descriptor, index);
+ result = ToStringObject(field_descriptor, value);
+ break;
+ }
+ case FieldDescriptor::CPPTYPE_MESSAGE: {
+ PyObject* py_cmsg = PyObject_CallObject(reinterpret_cast<PyObject*>(
+ &CMessage_Type), NULL);
+ if (py_cmsg == NULL) {
+ return NULL;
+ }
+ CMessage* cmsg = reinterpret_cast<CMessage*>(py_cmsg);
+ const Message& msg = reflection->GetRepeatedMessage(
+ *message, field_descriptor, index);
+ cmsg->owner = self->owner;
+ cmsg->parent = self->parent;
+ cmsg->message = const_cast<Message*>(&msg);
+ cmsg->read_only = false;
+ result = reinterpret_cast<PyObject*>(py_cmsg);
+ break;
+ }
+ default:
+ PyErr_Format(
+ PyExc_SystemError,
+ "Getting value from a repeated field of unknown type %d",
+ field_descriptor->cpp_type());
+ }
+
+ return result;
+}
+
+static PyObject* Subscript(RepeatedScalarContainer* self, PyObject* slice) {
+ Py_ssize_t from;
+ Py_ssize_t to;
+ Py_ssize_t step;
+ Py_ssize_t length;
+ Py_ssize_t slicelength;
+ bool return_list = false;
+#if PY_MAJOR_VERSION < 3
+ if (PyInt_Check(slice)) {
+ from = to = PyInt_AsLong(slice);
+ } else // NOLINT
+#endif
+ if (PyLong_Check(slice)) {
+ from = to = PyLong_AsLong(slice);
+ } else if (PySlice_Check(slice)) {
+ length = Len(self);
+#if PY_MAJOR_VERSION >= 3
+ if (PySlice_GetIndicesEx(slice,
+#else
+ if (PySlice_GetIndicesEx(reinterpret_cast<PySliceObject*>(slice),
+#endif
+ length, &from, &to, &step, &slicelength) == -1) {
+ return NULL;
+ }
+ return_list = true;
+ } else {
+ PyErr_SetString(PyExc_TypeError, "list indices must be integers");
+ return NULL;
+ }
+
+ if (!return_list) {
+ return Item(self, from);
+ }
+
+ PyObject* list = PyList_New(0);
+ if (list == NULL) {
+ return NULL;
+ }
+ if (from <= to) {
+ if (step < 0) {
+ return list;
+ }
+ for (Py_ssize_t index = from; index < to; index += step) {
+ if (index < 0 || index >= length) {
+ break;
+ }
+ ScopedPyObjectPtr s(Item(self, index));
+ PyList_Append(list, s.get());
+ }
+ } else {
+ if (step > 0) {
+ return list;
+ }
+ for (Py_ssize_t index = from; index > to; index += step) {
+ if (index < 0 || index >= length) {
+ break;
+ }
+ ScopedPyObjectPtr s(Item(self, index));
+ PyList_Append(list, s.get());
+ }
+ }
+ return list;
+}
+
+PyObject* Append(RepeatedScalarContainer* self, PyObject* item) {
+ cmessage::AssureWritable(self->parent);
+ Message* message = self->message;
+ const FieldDescriptor* field_descriptor = self->parent_field_descriptor;
+
+ const Reflection* reflection = message->GetReflection();
+ switch (field_descriptor->cpp_type()) {
+ case FieldDescriptor::CPPTYPE_INT32: {
+ GOOGLE_CHECK_GET_INT32(item, value, NULL);
+ reflection->AddInt32(message, field_descriptor, value);
+ break;
+ }
+ case FieldDescriptor::CPPTYPE_INT64: {
+ GOOGLE_CHECK_GET_INT64(item, value, NULL);
+ reflection->AddInt64(message, field_descriptor, value);
+ break;
+ }
+ case FieldDescriptor::CPPTYPE_UINT32: {
+ GOOGLE_CHECK_GET_UINT32(item, value, NULL);
+ reflection->AddUInt32(message, field_descriptor, value);
+ break;
+ }
+ case FieldDescriptor::CPPTYPE_UINT64: {
+ GOOGLE_CHECK_GET_UINT64(item, value, NULL);
+ reflection->AddUInt64(message, field_descriptor, value);
+ break;
+ }
+ case FieldDescriptor::CPPTYPE_FLOAT: {
+ GOOGLE_CHECK_GET_FLOAT(item, value, NULL);
+ reflection->AddFloat(message, field_descriptor, value);
+ break;
+ }
+ case FieldDescriptor::CPPTYPE_DOUBLE: {
+ GOOGLE_CHECK_GET_DOUBLE(item, value, NULL);
+ reflection->AddDouble(message, field_descriptor, value);
+ break;
+ }
+ case FieldDescriptor::CPPTYPE_BOOL: {
+ GOOGLE_CHECK_GET_BOOL(item, value, NULL);
+ reflection->AddBool(message, field_descriptor, value);
+ break;
+ }
+ case FieldDescriptor::CPPTYPE_STRING: {
+ if (!CheckAndSetString(
+ item, message, field_descriptor, reflection, true, -1)) {
+ return NULL;
+ }
+ break;
+ }
+ case FieldDescriptor::CPPTYPE_ENUM: {
+ GOOGLE_CHECK_GET_INT32(item, value, NULL);
+ if (reflection->SupportsUnknownEnumValues()) {
+ reflection->AddEnumValue(message, field_descriptor, value);
+ } else {
+ const EnumDescriptor* enum_descriptor = field_descriptor->enum_type();
+ const EnumValueDescriptor* enum_value =
+ enum_descriptor->FindValueByNumber(value);
+ if (enum_value != NULL) {
+ reflection->AddEnum(message, field_descriptor, enum_value);
+ } else {
+ ScopedPyObjectPtr s(PyObject_Str(item));
+ if (s != NULL) {
+ PyErr_Format(PyExc_ValueError, "Unknown enum value: %s",
+ PyString_AsString(s.get()));
+ }
+ return NULL;
+ }
+ }
+ break;
+ }
+ default:
+ PyErr_Format(
+ PyExc_SystemError, "Adding value to a field of unknown type %d",
+ field_descriptor->cpp_type());
+ return NULL;
+ }
+
+ Py_RETURN_NONE;
+}
+
+static int AssSubscript(RepeatedScalarContainer* self,
+ PyObject* slice,
+ PyObject* value) {
+ Py_ssize_t from;
+ Py_ssize_t to;
+ Py_ssize_t step;
+ Py_ssize_t length;
+ Py_ssize_t slicelength;
+ bool create_list = false;
+
+ cmessage::AssureWritable(self->parent);
+ Message* message = self->message;
+ const FieldDescriptor* field_descriptor =
+ self->parent_field_descriptor;
+
+#if PY_MAJOR_VERSION < 3
+ if (PyInt_Check(slice)) {
+ from = to = PyInt_AsLong(slice);
+ } else
+#endif
+ if (PyLong_Check(slice)) {
+ from = to = PyLong_AsLong(slice);
+ } else if (PySlice_Check(slice)) {
+ const Reflection* reflection = message->GetReflection();
+ length = reflection->FieldSize(*message, field_descriptor);
+#if PY_MAJOR_VERSION >= 3
+ if (PySlice_GetIndicesEx(slice,
+#else
+ if (PySlice_GetIndicesEx(reinterpret_cast<PySliceObject*>(slice),
+#endif
+ length, &from, &to, &step, &slicelength) == -1) {
+ return -1;
+ }
+ create_list = true;
+ } else {
+ PyErr_SetString(PyExc_TypeError, "list indices must be integers");
+ return -1;
+ }
+
+ if (value == NULL) {
+ return cmessage::InternalDeleteRepeatedField(
+ self->parent, field_descriptor, slice, NULL);
+ }
+
+ if (!create_list) {
+ return AssignItem(self, from, value);
+ }
+
+ ScopedPyObjectPtr full_slice(PySlice_New(NULL, NULL, NULL));
+ if (full_slice == NULL) {
+ return -1;
+ }
+ ScopedPyObjectPtr new_list(Subscript(self, full_slice.get()));
+ if (new_list == NULL) {
+ return -1;
+ }
+ if (PySequence_SetSlice(new_list.get(), from, to, value) < 0) {
+ return -1;
+ }
+
+ return InternalAssignRepeatedField(self, new_list.get());
+}
+
+PyObject* Extend(RepeatedScalarContainer* self, PyObject* value) {
+ cmessage::AssureWritable(self->parent);
+
+ // TODO(ptucker): Deprecate this behavior. b/18413862
+ if (value == Py_None) {
+ Py_RETURN_NONE;
+ }
+ if ((Py_TYPE(value)->tp_as_sequence == NULL) && PyObject_Not(value)) {
+ Py_RETURN_NONE;
+ }
+
+ ScopedPyObjectPtr iter(PyObject_GetIter(value));
+ if (iter == NULL) {
+ PyErr_SetString(PyExc_TypeError, "Value must be iterable");
+ return NULL;
+ }
+ ScopedPyObjectPtr next;
+ while ((next.reset(PyIter_Next(iter.get()))) != NULL) {
+ if (ScopedPyObjectPtr(Append(self, next.get())) == NULL) {
+ return NULL;
+ }
+ }
+ if (PyErr_Occurred()) {
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
+static PyObject* Insert(RepeatedScalarContainer* self, PyObject* args) {
+ Py_ssize_t index;
+ PyObject* value;
+ if (!PyArg_ParseTuple(args, "lO", &index, &value)) {
+ return NULL;
+ }
+ ScopedPyObjectPtr full_slice(PySlice_New(NULL, NULL, NULL));
+ ScopedPyObjectPtr new_list(Subscript(self, full_slice.get()));
+ if (PyList_Insert(new_list.get(), index, value) < 0) {
+ return NULL;
+ }
+ int ret = InternalAssignRepeatedField(self, new_list.get());
+ if (ret < 0) {
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
+static PyObject* Remove(RepeatedScalarContainer* self, PyObject* value) {
+ Py_ssize_t match_index = -1;
+ for (Py_ssize_t i = 0; i < Len(self); ++i) {
+ ScopedPyObjectPtr elem(Item(self, i));
+ if (PyObject_RichCompareBool(elem.get(), value, Py_EQ)) {
+ match_index = i;
+ break;
+ }
+ }
+ if (match_index == -1) {
+ PyErr_SetString(PyExc_ValueError, "remove(x): x not in container");
+ return NULL;
+ }
+ if (AssignItem(self, match_index, NULL) < 0) {
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
+static PyObject* RichCompare(RepeatedScalarContainer* self,
+ PyObject* other,
+ int opid) {
+ if (opid != Py_EQ && opid != Py_NE) {
+ Py_INCREF(Py_NotImplemented);
+ return Py_NotImplemented;
+ }
+
+ // Copy the contents of this repeated scalar container, and other if it is
+ // also a repeated scalar container, into Python lists so we can delegate
+ // to the list's compare method.
+
+ ScopedPyObjectPtr full_slice(PySlice_New(NULL, NULL, NULL));
+ if (full_slice == NULL) {
+ return NULL;
+ }
+
+ ScopedPyObjectPtr other_list_deleter;
+ if (PyObject_TypeCheck(other, &RepeatedScalarContainer_Type)) {
+ other_list_deleter.reset(Subscript(
+ reinterpret_cast<RepeatedScalarContainer*>(other), full_slice.get()));
+ other = other_list_deleter.get();
+ }
+
+ ScopedPyObjectPtr list(Subscript(self, full_slice.get()));
+ if (list == NULL) {
+ return NULL;
+ }
+ return PyObject_RichCompare(list.get(), other, opid);
+}
+
+PyObject* Reduce(RepeatedScalarContainer* unused_self) {
+ PyErr_Format(
+ PickleError_class,
+ "can't pickle repeated message fields, convert to list first");
+ return NULL;
+}
+
+static PyObject* Sort(RepeatedScalarContainer* self,
+ PyObject* args,
+ PyObject* kwds) {
+ // Support the old sort_function argument for backwards
+ // compatibility.
+ if (kwds != NULL) {
+ PyObject* sort_func = PyDict_GetItemString(kwds, "sort_function");
+ if (sort_func != NULL) {
+ // Must set before deleting as sort_func is a borrowed reference
+ // and kwds might be the only thing keeping it alive.
+ if (PyDict_SetItemString(kwds, "cmp", sort_func) == -1)
+ return NULL;
+ if (PyDict_DelItemString(kwds, "sort_function") == -1)
+ return NULL;
+ }
+ }
+
+ ScopedPyObjectPtr full_slice(PySlice_New(NULL, NULL, NULL));
+ if (full_slice == NULL) {
+ return NULL;
+ }
+ ScopedPyObjectPtr list(Subscript(self, full_slice.get()));
+ if (list == NULL) {
+ return NULL;
+ }
+ ScopedPyObjectPtr m(PyObject_GetAttrString(list.get(), "sort"));
+ if (m == NULL) {
+ return NULL;
+ }
+ ScopedPyObjectPtr res(PyObject_Call(m.get(), args, kwds));
+ if (res == NULL) {
+ return NULL;
+ }
+ int ret = InternalAssignRepeatedField(self, list.get());
+ if (ret < 0) {
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
+static PyObject* Pop(RepeatedScalarContainer* self,
+ PyObject* args) {
+ Py_ssize_t index = -1;
+ if (!PyArg_ParseTuple(args, "|n", &index)) {
+ return NULL;
+ }
+ PyObject* item = Item(self, index);
+ if (item == NULL) {
+ PyErr_Format(PyExc_IndexError,
+ "list index (%zd) out of range",
+ index);
+ return NULL;
+ }
+ if (AssignItem(self, index, NULL) < 0) {
+ return NULL;
+ }
+ return item;
+}
+
+// The private constructor of RepeatedScalarContainer objects.
+PyObject *NewContainer(
+ CMessage* parent, const FieldDescriptor* parent_field_descriptor) {
+ if (!CheckFieldBelongsToMessage(parent_field_descriptor, parent->message)) {
+ return NULL;
+ }
+
+ RepeatedScalarContainer* self = reinterpret_cast<RepeatedScalarContainer*>(
+ PyType_GenericAlloc(&RepeatedScalarContainer_Type, 0));
+ if (self == NULL) {
+ return NULL;
+ }
+
+ self->message = parent->message;
+ self->parent = parent;
+ self->parent_field_descriptor = parent_field_descriptor;
+ self->owner = parent->owner;
+
+ return reinterpret_cast<PyObject*>(self);
+}
+
+// Initializes the underlying Message object of "to" so it becomes a new parent
+// repeated scalar, and copies all the values from "from" to it. A child scalar
+// container can be released by passing it as both from and to (e.g. making it
+// the recipient of the new parent message and copying the values from itself).
+static int InitializeAndCopyToParentContainer(
+ RepeatedScalarContainer* from,
+ RepeatedScalarContainer* to) {
+ ScopedPyObjectPtr full_slice(PySlice_New(NULL, NULL, NULL));
+ if (full_slice == NULL) {
+ return -1;
+ }
+ ScopedPyObjectPtr values(Subscript(from, full_slice.get()));
+ if (values == NULL) {
+ return -1;
+ }
+ Message* new_message = from->message->New();
+ to->parent = NULL;
+ to->parent_field_descriptor = from->parent_field_descriptor;
+ to->message = new_message;
+ to->owner.reset(new_message);
+ if (InternalAssignRepeatedField(to, values.get()) < 0) {
+ return -1;
+ }
+ return 0;
+}
+
+int Release(RepeatedScalarContainer* self) {
+ return InitializeAndCopyToParentContainer(self, self);
+}
+
+PyObject* DeepCopy(RepeatedScalarContainer* self, PyObject* arg) {
+ RepeatedScalarContainer* clone = reinterpret_cast<RepeatedScalarContainer*>(
+ PyType_GenericAlloc(&RepeatedScalarContainer_Type, 0));
+ if (clone == NULL) {
+ return NULL;
+ }
+
+ if (InitializeAndCopyToParentContainer(self, clone) < 0) {
+ Py_DECREF(clone);
+ return NULL;
+ }
+ return reinterpret_cast<PyObject*>(clone);
+}
+
+static void Dealloc(RepeatedScalarContainer* self) {
+ self->owner.reset();
+ Py_TYPE(self)->tp_free(reinterpret_cast<PyObject*>(self));
+}
+
+void SetOwner(RepeatedScalarContainer* self,
+ const shared_ptr<Message>& new_owner) {
+ self->owner = new_owner;
+}
+
+static PySequenceMethods SqMethods = {
+ (lenfunc)Len, /* sq_length */
+ 0, /* sq_concat */
+ 0, /* sq_repeat */
+ (ssizeargfunc)Item, /* sq_item */
+ 0, /* sq_slice */
+ (ssizeobjargproc)AssignItem /* sq_ass_item */
+};
+
+static PyMappingMethods MpMethods = {
+ (lenfunc)Len, /* mp_length */
+ (binaryfunc)Subscript, /* mp_subscript */
+ (objobjargproc)AssSubscript, /* mp_ass_subscript */
+};
+
+static PyMethodDef Methods[] = {
+ { "__deepcopy__", (PyCFunction)DeepCopy, METH_VARARGS,
+ "Makes a deep copy of the class." },
+ { "__reduce__", (PyCFunction)Reduce, METH_NOARGS,
+ "Outputs picklable representation of the repeated field." },
+ { "append", (PyCFunction)Append, METH_O,
+ "Appends an object to the repeated container." },
+ { "extend", (PyCFunction)Extend, METH_O,
+ "Appends objects to the repeated container." },
+ { "insert", (PyCFunction)Insert, METH_VARARGS,
+ "Appends objects to the repeated container." },
+ { "pop", (PyCFunction)Pop, METH_VARARGS,
+ "Removes an object from the repeated container and returns it." },
+ { "remove", (PyCFunction)Remove, METH_O,
+ "Removes an object from the repeated container." },
+ { "sort", (PyCFunction)Sort, METH_VARARGS | METH_KEYWORDS,
+ "Sorts the repeated container."},
+ { NULL, NULL }
+};
+
+} // namespace repeated_scalar_container
+
+PyTypeObject RepeatedScalarContainer_Type = {
+ PyVarObject_HEAD_INIT(&PyType_Type, 0)
+ FULL_MODULE_NAME ".RepeatedScalarContainer", // tp_name
+ sizeof(RepeatedScalarContainer), // tp_basicsize
+ 0, // tp_itemsize
+ (destructor)repeated_scalar_container::Dealloc, // tp_dealloc
+ 0, // tp_print
+ 0, // tp_getattr
+ 0, // tp_setattr
+ 0, // tp_compare
+ 0, // tp_repr
+ 0, // tp_as_number
+ &repeated_scalar_container::SqMethods, // tp_as_sequence
+ &repeated_scalar_container::MpMethods, // tp_as_mapping
+ PyObject_HashNotImplemented, // tp_hash
+ 0, // tp_call
+ 0, // tp_str
+ 0, // tp_getattro
+ 0, // tp_setattro
+ 0, // tp_as_buffer
+ Py_TPFLAGS_DEFAULT, // tp_flags
+ "A Repeated scalar container", // tp_doc
+ 0, // tp_traverse
+ 0, // tp_clear
+ (richcmpfunc)repeated_scalar_container::RichCompare, // tp_richcompare
+ 0, // tp_weaklistoffset
+ 0, // tp_iter
+ 0, // tp_iternext
+ repeated_scalar_container::Methods, // tp_methods
+ 0, // tp_members
+ 0, // tp_getset
+ 0, // tp_base
+ 0, // tp_dict
+ 0, // tp_descr_get
+ 0, // tp_descr_set
+ 0, // tp_dictoffset
+ 0, // tp_init
+};
+
+} // namespace python
+} // namespace protobuf
+} // namespace google
diff --git a/python/google/protobuf/pyext/repeated_scalar_container.h b/python/google/protobuf/pyext/repeated_scalar_container.h
new file mode 100644
index 00000000..555e621c
--- /dev/null
+++ b/python/google/protobuf/pyext/repeated_scalar_container.h
@@ -0,0 +1,122 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Author: anuraag@google.com (Anuraag Agrawal)
+// Author: tibell@google.com (Johan Tibell)
+
+#ifndef GOOGLE_PROTOBUF_PYTHON_CPP_REPEATED_SCALAR_CONTAINER_H__
+#define GOOGLE_PROTOBUF_PYTHON_CPP_REPEATED_SCALAR_CONTAINER_H__
+
+#include <Python.h>
+
+#include <memory>
+#ifndef _SHARED_PTR_H
+#include <google/protobuf/stubs/shared_ptr.h>
+#endif
+
+#include <google/protobuf/descriptor.h>
+
+namespace google {
+namespace protobuf {
+
+class Message;
+
+#ifdef _SHARED_PTR_H
+using std::shared_ptr;
+#else
+using internal::shared_ptr;
+#endif
+
+namespace python {
+
+struct CMessage;
+
+typedef struct RepeatedScalarContainer {
+ PyObject_HEAD;
+
+ // This is the top-level C++ Message object that owns the whole
+ // proto tree. Every Python RepeatedScalarContainer holds a
+ // reference to it in order to keep it alive as long as there's a
+ // Python object that references any part of the tree.
+ shared_ptr<Message> owner;
+
+ // Pointer to the C++ Message that contains this container. The
+ // RepeatedScalarContainer does not own this pointer.
+ Message* message;
+
+ // Weak reference to a parent CMessage object (i.e. may be NULL.)
+ //
+ // Used to make sure all ancestors are also mutable when first
+ // modifying the container.
+ CMessage* parent;
+
+ // Pointer to the parent's descriptor that describes this
+ // field. Used together with the parent's message when making a
+ // default message instance mutable.
+ // The pointer is owned by the global DescriptorPool.
+ const FieldDescriptor* parent_field_descriptor;
+} RepeatedScalarContainer;
+
+extern PyTypeObject RepeatedScalarContainer_Type;
+
+namespace repeated_scalar_container {
+
+// Builds a RepeatedScalarContainer object, from a parent message and a
+// field descriptor.
+extern PyObject *NewContainer(
+ CMessage* parent, const FieldDescriptor* parent_field_descriptor);
+
+// Appends the scalar 'item' to the end of the container 'self'.
+//
+// Returns None if successful; returns NULL and sets an exception if
+// unsuccessful.
+PyObject* Append(RepeatedScalarContainer* self, PyObject* item);
+
+// Releases the messages in the container to a new message.
+//
+// Returns 0 on success, -1 on failure.
+int Release(RepeatedScalarContainer* self);
+
+// Appends all the elements in the input iterator to the container.
+//
+// Returns None if successful; returns NULL and sets an exception if
+// unsuccessful.
+PyObject* Extend(RepeatedScalarContainer* self, PyObject* value);
+
+// Set the owner field of self and any children of self.
+void SetOwner(RepeatedScalarContainer* self,
+ const shared_ptr<Message>& new_owner);
+
+} // namespace repeated_scalar_container
+} // namespace python
+} // namespace protobuf
+
+} // namespace google
+#endif // GOOGLE_PROTOBUF_PYTHON_CPP_REPEATED_SCALAR_CONTAINER_H__
diff --git a/python/google/protobuf/pyext/scoped_pyobject_ptr.h b/python/google/protobuf/pyext/scoped_pyobject_ptr.h
new file mode 100644
index 00000000..a128cd4c
--- /dev/null
+++ b/python/google/protobuf/pyext/scoped_pyobject_ptr.h
@@ -0,0 +1,96 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Author: tibell@google.com (Johan Tibell)
+
+#ifndef GOOGLE_PROTOBUF_PYTHON_CPP_SCOPED_PYOBJECT_PTR_H__
+#define GOOGLE_PROTOBUF_PYTHON_CPP_SCOPED_PYOBJECT_PTR_H__
+
+#include <google/protobuf/stubs/common.h>
+
+#include <Python.h>
+
+namespace google {
+class ScopedPyObjectPtr {
+ public:
+ // Constructor. Defaults to initializing with NULL.
+ // There is no way to create an uninitialized ScopedPyObjectPtr.
+ explicit ScopedPyObjectPtr(PyObject* p = NULL) : ptr_(p) { }
+
+ // Destructor. If there is a PyObject object, delete it.
+ ~ScopedPyObjectPtr() {
+ Py_XDECREF(ptr_);
+ }
+
+ // Reset. Deletes the current owned object, if any.
+ // Then takes ownership of a new object, if given.
+ // This function must be called with a reference that you own.
+ // this->reset(this->get()) is wrong!
+ // this->reset(this->release()) is OK.
+ PyObject* reset(PyObject* p = NULL) {
+ Py_XDECREF(ptr_);
+ ptr_ = p;
+ return ptr_;
+ }
+
+ // Releases ownership of the object.
+ // The caller now owns the returned reference.
+ PyObject* release() {
+ PyObject* p = ptr_;
+ ptr_ = NULL;
+ return p;
+ }
+
+ PyObject* operator->() const {
+ assert(ptr_ != NULL);
+ return ptr_;
+ }
+
+ PyObject* get() const { return ptr_; }
+
+ Py_ssize_t refcnt() const { return Py_REFCNT(ptr_); }
+
+ void inc() const { Py_INCREF(ptr_); }
+
+ // Comparison operators.
+ // These return whether a ScopedPyObjectPtr and a raw pointer
+ // refer to the same object, not just to two different but equal
+ // objects.
+ bool operator==(const PyObject* p) const { return ptr_ == p; }
+ bool operator!=(const PyObject* p) const { return ptr_ != p; }
+
+ private:
+ PyObject* ptr_;
+
+ GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ScopedPyObjectPtr);
+};
+
+} // namespace google
+#endif // GOOGLE_PROTOBUF_PYTHON_CPP_SCOPED_PYOBJECT_PTR_H__