aboutsummaryrefslogtreecommitdiffhomepage
path: root/python/google/protobuf/pyext
diff options
context:
space:
mode:
authorGravatar Jisi Liu <jisi.liu@gmail.com>2016-04-28 14:34:59 -0700
committerGravatar Jisi Liu <jisi.liu@gmail.com>2016-04-28 14:34:59 -0700
commitcf14183bcd5485b4a71541599ddce0b35eb71352 (patch)
tree12f6e5eb731d7a70cdac4cdafc8b3131629413e2 /python/google/protobuf/pyext
parentf00300d7f04f1c38a7d70e271f9232b94dd0e326 (diff)
Down integrate from Google internal.
Diffstat (limited to 'python/google/protobuf/pyext')
-rw-r--r--python/google/protobuf/pyext/descriptor.cc11
-rw-r--r--python/google/protobuf/pyext/descriptor_pool.cc8
-rw-r--r--python/google/protobuf/pyext/descriptor_pool.h11
-rw-r--r--python/google/protobuf/pyext/extension_dict.cc19
-rw-r--r--python/google/protobuf/pyext/extension_dict.h6
-rw-r--r--python/google/protobuf/pyext/map_container.cc17
-rw-r--r--python/google/protobuf/pyext/map_container.h7
-rw-r--r--python/google/protobuf/pyext/message.cc120
-rw-r--r--python/google/protobuf/pyext/message.h33
-rw-r--r--python/google/protobuf/pyext/repeated_composite_container.cc14
-rw-r--r--python/google/protobuf/pyext/repeated_composite_container.h7
11 files changed, 161 insertions, 92 deletions
diff --git a/python/google/protobuf/pyext/descriptor.cc b/python/google/protobuf/pyext/descriptor.cc
index 07550706..23557538 100644
--- a/python/google/protobuf/pyext/descriptor.cc
+++ b/python/google/protobuf/pyext/descriptor.cc
@@ -200,8 +200,8 @@ static PyObject* GetOrBuildOptions(const DescriptorClass *descriptor) {
// read-only instance.
const Message& options(descriptor->options());
const Descriptor *message_type = options.GetDescriptor();
- PyObject* message_class(cdescriptor_pool::GetMessageClass(
- pool, message_type));
+ CMessageClass* message_class(
+ cdescriptor_pool::GetMessageClass(pool, message_type));
if (message_class == NULL) {
// The Options message was not found in the current DescriptorPool.
// In this case, there cannot be extensions to these options, and we can
@@ -215,7 +215,8 @@ static PyObject* GetOrBuildOptions(const DescriptorClass *descriptor) {
message_type->full_name().c_str());
return NULL;
}
- ScopedPyObjectPtr value(PyEval_CallObject(message_class, NULL));
+ ScopedPyObjectPtr value(
+ PyEval_CallObject(message_class->AsPyObject(), NULL));
if (value == NULL) {
return NULL;
}
@@ -433,11 +434,11 @@ static PyObject* GetConcreteClass(PyBaseDescriptor* self, void *closure) {
// 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.
- PyObject* concrete_class(cdescriptor_pool::GetMessageClass(
+ CMessageClass* concrete_class(cdescriptor_pool::GetMessageClass(
GetDescriptorPool_FromPool(_GetDescriptor(self)->file()->pool()),
_GetDescriptor(self)));
Py_XINCREF(concrete_class);
- return concrete_class;
+ return concrete_class->AsPyObject();
}
static PyObject* GetFieldsByName(PyBaseDescriptor* self, void *closure) {
diff --git a/python/google/protobuf/pyext/descriptor_pool.cc b/python/google/protobuf/pyext/descriptor_pool.cc
index 0bc76bc9..1faff96b 100644
--- a/python/google/protobuf/pyext/descriptor_pool.cc
+++ b/python/google/protobuf/pyext/descriptor_pool.cc
@@ -190,8 +190,8 @@ PyObject* FindMessageByName(PyDescriptorPool* self, PyObject* arg) {
// Add a message class to our database.
int RegisterMessageClass(PyDescriptorPool* self,
- const Descriptor *message_descriptor,
- PyObject *message_class) {
+ 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(
@@ -205,8 +205,8 @@ int RegisterMessageClass(PyDescriptorPool* self,
}
// Retrieve the message class added to our database.
-PyObject *GetMessageClass(PyDescriptorPool* self,
- const Descriptor *message_descriptor) {
+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()) {
diff --git a/python/google/protobuf/pyext/descriptor_pool.h b/python/google/protobuf/pyext/descriptor_pool.h
index 16bc910c..2a42c112 100644
--- a/python/google/protobuf/pyext/descriptor_pool.h
+++ b/python/google/protobuf/pyext/descriptor_pool.h
@@ -42,6 +42,9 @@ 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.
//
@@ -78,7 +81,7 @@ typedef struct PyDescriptorPool {
//
// Descriptor pointers stored here are owned by the DescriptorPool above.
// Python references to classes are owned by this PyDescriptorPool.
- typedef hash_map<const Descriptor*, PyObject*> ClassesByMessageMap;
+ typedef hash_map<const Descriptor*, CMessageClass*> ClassesByMessageMap;
ClassesByMessageMap* classes_by_descriptor;
// Cache the options for any kind of descriptor.
@@ -101,14 +104,14 @@ const Descriptor* FindMessageTypeByName(PyDescriptorPool* self,
// On error, returns -1 with a Python exception set.
int RegisterMessageClass(PyDescriptorPool* self,
const Descriptor* message_descriptor,
- PyObject* message_class);
+ 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.
-PyObject* GetMessageClass(PyDescriptorPool* self,
- const Descriptor* message_descriptor);
+CMessageClass* GetMessageClass(PyDescriptorPool* self,
+ const Descriptor* message_descriptor);
// The functions below are also exposed as methods of the DescriptorPool type.
diff --git a/python/google/protobuf/pyext/extension_dict.cc b/python/google/protobuf/pyext/extension_dict.cc
index 555bd293..21bbb8c2 100644
--- a/python/google/protobuf/pyext/extension_dict.cc
+++ b/python/google/protobuf/pyext/extension_dict.cc
@@ -130,7 +130,7 @@ PyObject* subscript(ExtensionDict* self, PyObject* key) {
if (descriptor->label() == FieldDescriptor::LABEL_REPEATED) {
if (descriptor->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
- PyObject *message_class = cdescriptor_pool::GetMessageClass(
+ CMessageClass* message_class = cdescriptor_pool::GetMessageClass(
cmessage::GetDescriptorPoolForMessage(self->parent),
descriptor->message_type());
if (message_class == NULL) {
@@ -239,6 +239,21 @@ PyObject* _FindExtensionByName(ExtensionDict* self, PyObject* name) {
}
}
+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));
@@ -271,6 +286,8 @@ static PyMethodDef Methods[] = {
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 }
};
diff --git a/python/google/protobuf/pyext/extension_dict.h b/python/google/protobuf/pyext/extension_dict.h
index 1e7f6f7b..049d2e45 100644
--- a/python/google/protobuf/pyext/extension_dict.h
+++ b/python/google/protobuf/pyext/extension_dict.h
@@ -123,6 +123,12 @@ PyObject* ClearExtension(ExtensionDict* self,
// 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
diff --git a/python/google/protobuf/pyext/map_container.cc b/python/google/protobuf/pyext/map_container.cc
index df9138a4..e022406d 100644
--- a/python/google/protobuf/pyext/map_container.cc
+++ b/python/google/protobuf/pyext/map_container.cc
@@ -32,6 +32,11 @@
#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/stubs/scoped_ptr.h>
@@ -70,7 +75,7 @@ class MapReflectionFriend {
struct MapIterator {
PyObject_HEAD;
- scoped_ptr< ::google::protobuf::MapIterator> iter;
+ 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.
@@ -610,8 +615,7 @@ static PyObject* GetCMessage(MessageMapContainer* self, Message* message) {
PyObject* ret = PyDict_GetItem(self->message_dict, key.get());
if (ret == NULL) {
- CMessage* cmsg = cmessage::NewEmptyMessage(self->subclass_init,
- message->GetDescriptor());
+ CMessage* cmsg = cmessage::NewEmptyMessage(self->message_class);
ret = reinterpret_cast<PyObject*>(cmsg);
if (cmsg == NULL) {
@@ -634,7 +638,7 @@ static PyObject* GetCMessage(MessageMapContainer* self, Message* message) {
PyObject* NewMessageMapContainer(
CMessage* parent, const google::protobuf::FieldDescriptor* parent_field_descriptor,
- PyObject* concrete_class) {
+ CMessageClass* message_class) {
if (!CheckFieldBelongsToMessage(parent_field_descriptor, parent->message)) {
return NULL;
}
@@ -669,8 +673,8 @@ PyObject* NewMessageMapContainer(
"Could not allocate message dict.");
}
- Py_INCREF(concrete_class);
- self->subclass_init = concrete_class;
+ Py_INCREF(message_class);
+ self->message_class = message_class;
if (self->key_field_descriptor == NULL ||
self->value_field_descriptor == NULL) {
@@ -763,6 +767,7 @@ 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);
}
diff --git a/python/google/protobuf/pyext/map_container.h b/python/google/protobuf/pyext/map_container.h
index 27ee6dbd..b11dfa34 100644
--- a/python/google/protobuf/pyext/map_container.h
+++ b/python/google/protobuf/pyext/map_container.h
@@ -55,6 +55,7 @@ using internal::shared_ptr;
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.
@@ -104,8 +105,8 @@ struct MapContainer {
};
struct MessageMapContainer : public MapContainer {
- // A callable that is used to create new child messages.
- PyObject* subclass_init;
+ // The type used to create new child messages.
+ CMessageClass* message_class;
// A dict mapping Message* -> CMessage.
PyObject* message_dict;
@@ -132,7 +133,7 @@ extern PyObject* NewScalarMapContainer(
// field descriptor.
extern PyObject* NewMessageMapContainer(
CMessage* parent, const FieldDescriptor* parent_field_descriptor,
- PyObject* concrete_class);
+ CMessageClass* message_class);
} // namespace python
} // namespace protobuf
diff --git a/python/google/protobuf/pyext/message.cc b/python/google/protobuf/pyext/message.cc
index 6d7b2b0f..83c151ff 100644
--- a/python/google/protobuf/pyext/message.cc
+++ b/python/google/protobuf/pyext/message.cc
@@ -98,31 +98,6 @@ static PyObject* PythonMessage_class;
static PyObject* kEmptyWeakref;
static PyObject* WKT_classes = NULL;
-// Defines the Metaclass of all Message 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 PyMessageMeta {
- // 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;
-};
-
namespace message_meta {
static int InsertEmptyWeakref(PyTypeObject* base);
@@ -173,10 +148,6 @@ static int AddDescriptors(PyObject* cls, const Descriptor* descriptor) {
}
// For each enum set cls.<enum name> = EnumTypeWrapper(<enum descriptor>).
- //
- // The enum descriptor we get from
- // <messagedescriptor>.enum_types_by_name[name]
- // which was built previously.
for (int i = 0; i < descriptor->enum_type_count(); ++i) {
const EnumDescriptor* enum_descriptor = descriptor->enum_type(i);
ScopedPyObjectPtr enum_type(
@@ -309,7 +280,7 @@ static PyObject* New(PyTypeObject* type,
if (result == NULL) {
return NULL;
}
- PyMessageMeta* newtype = reinterpret_cast<PyMessageMeta*>(result.get());
+ CMessageClass* newtype = reinterpret_cast<CMessageClass*>(result.get());
// Insert the empty weakref into the base classes.
if (InsertEmptyWeakref(
@@ -338,7 +309,7 @@ static PyObject* New(PyTypeObject* type,
// Add the message to the DescriptorPool.
if (cdescriptor_pool::RegisterMessageClass(newtype->py_descriptor_pool,
- descriptor, result.get()) < 0) {
+ descriptor, newtype) < 0) {
return NULL;
}
@@ -349,7 +320,7 @@ static PyObject* New(PyTypeObject* type,
return result.release();
}
-static void Dealloc(PyMessageMeta *self) {
+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));
@@ -378,10 +349,10 @@ static int InsertEmptyWeakref(PyTypeObject *base_type) {
} // namespace message_meta
-PyTypeObject PyMessageMeta_Type = {
+PyTypeObject CMessageClass_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
FULL_MODULE_NAME ".MessageMeta", // tp_name
- sizeof(PyMessageMeta), // tp_basicsize
+ sizeof(CMessageClass), // tp_basicsize
0, // tp_itemsize
(destructor)message_meta::Dealloc, // tp_dealloc
0, // tp_print
@@ -419,16 +390,16 @@ PyTypeObject PyMessageMeta_Type = {
message_meta::New, // tp_new
};
-static PyMessageMeta* CheckMessageClass(PyTypeObject* cls) {
- if (!PyObject_TypeCheck(cls, &PyMessageMeta_Type)) {
+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<PyMessageMeta*>(cls);
+ return reinterpret_cast<CMessageClass*>(cls);
}
static const Descriptor* GetMessageDescriptor(PyTypeObject* cls) {
- PyMessageMeta* type = CheckMessageClass(cls);
+ CMessageClass* type = CheckMessageClass(cls);
if (type == NULL) {
return NULL;
}
@@ -783,9 +754,9 @@ namespace cmessage {
PyDescriptorPool* GetDescriptorPoolForMessage(CMessage* message) {
// No need to check the type: the type of instances of CMessage is always
- // an instance of PyMessageMeta. Let's prove it with a debug-only check.
+ // an instance of CMessageClass. Let's prove it with a debug-only check.
GOOGLE_DCHECK(PyObject_TypeCheck(message, &CMessage_Type));
- return reinterpret_cast<PyMessageMeta*>(Py_TYPE(message))->py_descriptor_pool;
+ return reinterpret_cast<CMessageClass*>(Py_TYPE(message))->py_descriptor_pool;
}
MessageFactory* GetFactoryForMessage(CMessage* message) {
@@ -1090,6 +1061,10 @@ int InitAttributes(CMessage* self, PyObject* kwargs) {
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 =
@@ -1220,9 +1195,9 @@ int InitAttributes(CMessage* self, PyObject* kwargs) {
// Allocates an incomplete Python Message: the caller must fill self->message,
// self->owner and eventually self->parent.
-CMessage* NewEmptyMessage(PyObject* type, const Descriptor *descriptor) {
+CMessage* NewEmptyMessage(CMessageClass* type) {
CMessage* self = reinterpret_cast<CMessage*>(
- PyType_GenericAlloc(reinterpret_cast<PyTypeObject*>(type), 0));
+ PyType_GenericAlloc(&type->super.ht_type, 0));
if (self == NULL) {
return NULL;
}
@@ -1242,7 +1217,7 @@ CMessage* NewEmptyMessage(PyObject* type, const Descriptor *descriptor) {
// Creates a new C++ message and takes ownership.
static PyObject* New(PyTypeObject* cls,
PyObject* unused_args, PyObject* unused_kwargs) {
- PyMessageMeta* type = CheckMessageClass(cls);
+ CMessageClass* type = CheckMessageClass(cls);
if (type == NULL) {
return NULL;
}
@@ -1258,8 +1233,7 @@ static PyObject* New(PyTypeObject* cls,
return NULL;
}
- CMessage* self = NewEmptyMessage(reinterpret_cast<PyObject*>(type),
- message_descriptor);
+ CMessage* self = NewEmptyMessage(type);
if (self == NULL) {
return NULL;
}
@@ -2023,10 +1997,34 @@ static PyObject* RegisterExtension(PyObject* cls,
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;
@@ -2166,6 +2164,12 @@ static PyObject* ListFields(CMessage* self) {
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;
@@ -2309,14 +2313,13 @@ PyObject* InternalGetSubMessage(
const Message& sub_message = reflection->GetMessage(
*self->message, field_descriptor, pool->message_factory);
- PyObject *message_class = cdescriptor_pool::GetMessageClass(
+ CMessageClass* message_class = cdescriptor_pool::GetMessageClass(
pool, field_descriptor->message_type());
if (message_class == NULL) {
return NULL;
}
- CMessage* cmsg = cmessage::NewEmptyMessage(message_class,
- sub_message.GetDescriptor());
+ CMessage* cmsg = cmessage::NewEmptyMessage(message_class);
if (cmsg == NULL) {
return NULL;
}
@@ -2585,6 +2588,8 @@ static PyMethodDef Methods[] = {
"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." },
@@ -2654,7 +2659,7 @@ PyObject* GetAttr(CMessage* self, PyObject* name) {
const Descriptor* entry_type = field_descriptor->message_type();
const FieldDescriptor* value_type = entry_type->FindFieldByName("value");
if (value_type->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
- PyObject* value_class = cdescriptor_pool::GetMessageClass(
+ CMessageClass* value_class = cdescriptor_pool::GetMessageClass(
GetDescriptorPoolForMessage(self), value_type->message_type());
if (value_class == NULL) {
return NULL;
@@ -2677,7 +2682,7 @@ PyObject* GetAttr(CMessage* self, PyObject* name) {
if (field_descriptor->label() == FieldDescriptor::LABEL_REPEATED) {
PyObject* py_container = NULL;
if (field_descriptor->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
- PyObject *message_class = cdescriptor_pool::GetMessageClass(
+ CMessageClass* message_class = cdescriptor_pool::GetMessageClass(
GetDescriptorPoolForMessage(self), field_descriptor->message_type());
if (message_class == NULL) {
return NULL;
@@ -2749,7 +2754,7 @@ int SetAttr(CMessage* self, PyObject* name, PyObject* value) {
} // namespace cmessage
PyTypeObject CMessage_Type = {
- PyVarObject_HEAD_INIT(&PyMessageMeta_Type, 0)
+ PyVarObject_HEAD_INIT(&CMessageClass_Type, 0)
FULL_MODULE_NAME ".CMessage", // tp_name
sizeof(CMessage), // tp_basicsize
0, // tp_itemsize
@@ -2864,12 +2869,12 @@ bool InitProto2MessageModule(PyObject *m) {
// Initialize constants defined in this file.
InitGlobals();
- PyMessageMeta_Type.tp_base = &PyType_Type;
- if (PyType_Ready(&PyMessageMeta_Type) < 0) {
+ CMessageClass_Type.tp_base = &PyType_Type;
+ if (PyType_Ready(&CMessageClass_Type) < 0) {
return false;
}
PyModule_AddObject(m, "MessageMeta",
- reinterpret_cast<PyObject*>(&PyMessageMeta_Type));
+ reinterpret_cast<PyObject*>(&CMessageClass_Type));
if (PyType_Ready(&CMessage_Type) < 0) {
return false;
@@ -3077,9 +3082,10 @@ bool InitProto2MessageModule(PyObject *m) {
} // namespace protobuf
static PyMethodDef ModuleMethods[] = {
- {"SetAllowOversizeProtos",
- (PyCFunction)google::protobuf::python::cmessage::SetAllowOversizeProtos,
- METH_O, "Enable/disable oversize proto parsing."},
+ {"SetAllowOversizeProtos",
+ (PyCFunction)google::protobuf::python::cmessage::SetAllowOversizeProtos,
+ METH_O, "Enable/disable oversize proto parsing."},
+ { NULL, NULL}
};
#if PY_MAJOR_VERSION >= 3
diff --git a/python/google/protobuf/pyext/message.h b/python/google/protobuf/pyext/message.h
index c2b62649..9dce198f 100644
--- a/python/google/protobuf/pyext/message.h
+++ b/python/google/protobuf/pyext/message.h
@@ -116,12 +116,43 @@ typedef struct 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(PyObject* type, const Descriptor* descriptor);
+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.
diff --git a/python/google/protobuf/pyext/repeated_composite_container.cc b/python/google/protobuf/pyext/repeated_composite_container.cc
index b01123b4..4f339e77 100644
--- a/python/google/protobuf/pyext/repeated_composite_container.cc
+++ b/python/google/protobuf/pyext/repeated_composite_container.cc
@@ -107,8 +107,7 @@ static int UpdateChildMessages(RepeatedCompositeContainer* self) {
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->subclass_init,
- sub_message.GetDescriptor());
+ CMessage* cmsg = cmessage::NewEmptyMessage(self->child_message_class);
ScopedPyObjectPtr py_cmsg(reinterpret_cast<PyObject*>(cmsg));
if (cmsg == NULL) {
return -1;
@@ -140,8 +139,7 @@ static PyObject* AddToAttached(RepeatedCompositeContainer* self,
Message* sub_message =
message->GetReflection()->AddMessage(message,
self->parent_field_descriptor);
- CMessage* cmsg = cmessage::NewEmptyMessage(self->subclass_init,
- sub_message->GetDescriptor());
+ CMessage* cmsg = cmessage::NewEmptyMessage(self->child_message_class);
if (cmsg == NULL)
return NULL;
@@ -168,7 +166,7 @@ static PyObject* AddToReleased(RepeatedCompositeContainer* self,
// Create a new Message detached from the rest.
PyObject* py_cmsg = PyEval_CallObjectWithKeywords(
- self->subclass_init, NULL, kwargs);
+ self->child_message_class->AsPyObject(), NULL, kwargs);
if (py_cmsg == NULL)
return NULL;
@@ -506,7 +504,7 @@ int SetOwner(RepeatedCompositeContainer* self,
PyObject *NewContainer(
CMessage* parent,
const FieldDescriptor* parent_field_descriptor,
- PyObject *concrete_class) {
+ CMessageClass* concrete_class) {
if (!CheckFieldBelongsToMessage(parent_field_descriptor, parent->message)) {
return NULL;
}
@@ -523,7 +521,7 @@ PyObject *NewContainer(
self->parent_field_descriptor = parent_field_descriptor;
self->owner = parent->owner;
Py_INCREF(concrete_class);
- self->subclass_init = concrete_class;
+ self->child_message_class = concrete_class;
self->child_messages = PyList_New(0);
return reinterpret_cast<PyObject*>(self);
@@ -531,7 +529,7 @@ PyObject *NewContainer(
static void Dealloc(RepeatedCompositeContainer* self) {
Py_CLEAR(self->child_messages);
- Py_CLEAR(self->subclass_init);
+ 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();
diff --git a/python/google/protobuf/pyext/repeated_composite_container.h b/python/google/protobuf/pyext/repeated_composite_container.h
index 442ce7e3..25463037 100644
--- a/python/google/protobuf/pyext/repeated_composite_container.h
+++ b/python/google/protobuf/pyext/repeated_composite_container.h
@@ -58,6 +58,7 @@ using internal::shared_ptr;
namespace python {
struct CMessage;
+struct CMessageClass;
// A RepeatedCompositeContainer can be in one of two states: attached
// or released.
@@ -94,8 +95,8 @@ typedef struct RepeatedCompositeContainer {
// calling Clear() or ClearField() on the parent.
Message* message;
- // A callable that is used to create new child messages.
- PyObject* subclass_init;
+ // The type used to create new child messages.
+ CMessageClass* child_message_class;
// A list of child messages.
PyObject* child_messages;
@@ -110,7 +111,7 @@ namespace repeated_composite_container {
PyObject *NewContainer(
CMessage* parent,
const FieldDescriptor* parent_field_descriptor,
- PyObject *concrete_class);
+ CMessageClass *child_message_class);
// Appends a new CMessage to the container and returns it. The
// CMessage is initialized using the content of kwargs.