aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Colthurst <thomaswc@google.com>2018-05-29 18:26:11 -0400
committerThomas Colthurst <thomaswc@google.com>2018-05-30 10:33:02 -0400
commit7c6514245b6e256cc97dd31a6da1e2c72de487e6 (patch)
tree275ae5a08d02048d24cd230176d8db053deb189c
parent7d1a8fff1b424e63f6bbdd1f9f828f6f9f40e84f (diff)
downloadprotobuf-7c6514245b6e256cc97dd31a6da1e2c72de487e6.tar.gz
protobuf-7c6514245b6e256cc97dd31a6da1e2c72de487e6.tar.bz2
protobuf-7c6514245b6e256cc97dd31a6da1e2c72de487e6.zip
Introduce Proto C API; based on cl/198113115 by amauryfa
-rw-r--r--BUILD10
-rw-r--r--Makefile.am1
-rw-r--r--python/google/protobuf/proto_api.h92
-rw-r--r--python/google/protobuf/pyext/message.cc26
-rw-r--r--python/google/protobuf/pyext/message.h3
-rw-r--r--python/google/protobuf/pyext/message_module.cc17
6 files changed, 148 insertions, 1 deletions
diff --git a/BUILD b/BUILD
index 191ab929..48c5aa7b 100644
--- a/BUILD
+++ b/BUILD
@@ -689,6 +689,7 @@ cc_binary(
linkstatic = 1,
deps = [
":protobuf",
+ ":proto_api",
] + select({
"//conditions:default": [],
":use_fast_cpp_protos": ["//external:python_headers"],
@@ -831,6 +832,15 @@ internal_protobuf_py_tests(
deps = [":python_tests"],
)
+cc_library(
+ name = "proto_api",
+ hdrs = ["python/google/protobuf/proto_api.h"],
+ deps = [
+ ":protobuf_python",
+ "//external:python_headers",
+ ],
+)
+
proto_lang_toolchain(
name = "cc_toolchain",
command_line = "--cpp_out=$(OUT)",
diff --git a/Makefile.am b/Makefile.am
index 8334db60..ac87e8a3 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -804,6 +804,7 @@ python_EXTRA_DIST= \
python/google/protobuf/message.py \
python/google/protobuf/message_factory.py \
python/google/protobuf/python_protobuf.h \
+ python/google/protobuf/proto_api.h \
python/google/protobuf/proto_builder.py \
python/google/protobuf/pyext/README \
python/google/protobuf/pyext/__init__.py \
diff --git a/python/google/protobuf/proto_api.h b/python/google/protobuf/proto_api.h
new file mode 100644
index 00000000..64d8dda9
--- /dev/null
+++ b/python/google/protobuf/proto_api.h
@@ -0,0 +1,92 @@
+// 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 can be included by other C++ libraries, typically extension modules
+// which want to interact with the Python Messages coming from the "cpp"
+// implementation of protocol buffers.
+//
+// Usage:
+// Declare a (probably static) variable to hold the API:
+// const PyProto_API* py_proto_api;
+// In some initialization function, write:
+// py_proto_api = static_cast<const PyProto_API*>(PyCapsule_Import(
+// PyProtoAPICapsuleName(), 0));
+// if (!py_proto_api) { ...handle ImportError... }
+// Then use the methods of the returned class:
+// py_proto_api->GetMessagePointer(...);
+
+#ifndef PYTHON_GOOGLE_PROTOBUF_PROTO_API_H__
+#define PYTHON_GOOGLE_PROTOBUF_PROTO_API_H__
+
+#include <Python.h>
+
+namespace google {
+namespace protobuf {
+
+class Message;
+
+namespace python {
+
+// Note on the implementation:
+// This API is designed after
+// https://docs.python.org/3/extending/extending.html#providing-a-c-api-for-an-extension-module
+// The class below contains no mutable state, and all methods are "const";
+// we use a C++ class instead of a C struct with functions pointers just because
+// the code looks more readable.
+struct PyProto_API {
+ // The API object is created at initialization time and never freed.
+ // This destructor is never called.
+ virtual ~PyProto_API() {}
+
+ // Operations on Messages.
+
+ // If the passed object is a Python Message, returns its internal pointer.
+ // Otherwise, returns NULL with an exception set.
+ virtual const Message* GetMessagePointer(PyObject* msg) const = 0;
+
+ // If the passed object is a Python Message, returns a mutable pointer.
+ // Otherwise, returns NULL with an exception set.
+ // This function will succeed only if there are no other Python objects
+ // pointing to the message, like submessages or repeated containers.
+ // With the current implementation, only empty messages are in this case.
+ virtual Message* GetMutableMessagePointer(PyObject* msg) const = 0;
+};
+
+inline const char* PyProtoAPICapsuleName() {
+ static const char kCapsuleName[] =
+ "protobuf.python.google.protobuf.cpp._message.proto_API";
+ return kCapsuleName;
+}
+
+} // namespace python
+} // namespace protobuf
+} // namespace google
+
+#endif // PYTHON_GOOGLE_PROTOBUF_PROTO_API_H__
diff --git a/python/google/protobuf/pyext/message.cc b/python/google/protobuf/pyext/message.cc
index 5893533a..53736b9c 100644
--- a/python/google/protobuf/pyext/message.cc
+++ b/python/google/protobuf/pyext/message.cc
@@ -2866,17 +2866,38 @@ const Message* (*GetCProtoInsidePyProtoPtr)(PyObject* msg);
Message* (*MutableCProtoInsidePyProtoPtr)(PyObject* msg);
static const Message* GetCProtoInsidePyProtoImpl(PyObject* msg) {
+ const Message* message = PyMessage_GetMessagePointer(msg);
+ if (message == NULL) {
+ PyErr_Clear();
+ return NULL;
+ }
+ return message;
+}
+
+static Message* MutableCProtoInsidePyProtoImpl(PyObject* msg) {
+ Message* message = PyMessage_GetMutableMessagePointer(msg);
+ if (message == NULL) {
+ PyErr_Clear();
+ return NULL;
+ }
+ return message;
+}
+
+const Message* PyMessage_GetMessagePointer(PyObject* msg) {
if (!PyObject_TypeCheck(msg, &CMessage_Type)) {
+ PyErr_SetString(PyExc_TypeError, "Not a Message instance");
return NULL;
}
CMessage* cmsg = reinterpret_cast<CMessage*>(msg);
return cmsg->message;
}
-static Message* MutableCProtoInsidePyProtoImpl(PyObject* msg) {
+Message* PyMessage_GetMutableMessagePointer(PyObject* msg) {
if (!PyObject_TypeCheck(msg, &CMessage_Type)) {
+ PyErr_SetString(PyExc_TypeError, "Not a Message instance");
return NULL;
}
+
CMessage* cmsg = reinterpret_cast<CMessage*>(msg);
if ((cmsg->composite_fields && PyDict_Size(cmsg->composite_fields) != 0) ||
(cmsg->extensions != NULL &&
@@ -2885,6 +2906,9 @@ static Message* MutableCProtoInsidePyProtoImpl(PyObject* msg) {
// 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.
+ PyErr_SetString(PyExc_ValueError,
+ "Cannot reliably get a mutable pointer "
+ "to a message with extra references");
return NULL;
}
cmessage::AssureWritable(cmsg);
diff --git a/python/google/protobuf/pyext/message.h b/python/google/protobuf/pyext/message.h
index 72bcfa83..d754e62a 100644
--- a/python/google/protobuf/pyext/message.h
+++ b/python/google/protobuf/pyext/message.h
@@ -341,6 +341,9 @@ bool CheckFieldBelongsToMessage(const FieldDescriptor* field_descriptor,
extern PyObject* PickleError_class;
+const Message* PyMessage_GetMessagePointer(PyObject* msg);
+Message* PyMessage_GetMutableMessagePointer(PyObject* msg);
+
bool InitProto2MessageModule(PyObject *m);
#if LANG_CXX11
diff --git a/python/google/protobuf/pyext/message_module.cc b/python/google/protobuf/pyext/message_module.cc
index 7c4df47f..f5c8f295 100644
--- a/python/google/protobuf/pyext/message_module.cc
+++ b/python/google/protobuf/pyext/message_module.cc
@@ -31,9 +31,26 @@
#include <Python.h>
#include <google/protobuf/pyext/message.h>
+#include <google/protobuf/proto_api.h>
#include <google/protobuf/message_lite.h>
+namespace {
+
+// C++ API. Clients get at this via proto_api.h
+struct ApiImplementation : google::protobuf::python::PyProto_API {
+ const google::protobuf::Message*
+ GetMessagePointer(PyObject* msg) const override {
+ return google::protobuf::python::PyMessage_GetMessagePointer(msg);
+ }
+ google::protobuf::Message*
+ GetMutableMessagePointer(PyObject* msg) const override {
+ return google::protobuf::python::PyMessage_GetMutableMessagePointer(msg);
+ }
+};
+
+} // namespace
+
static PyObject* GetPythonProto3PreserveUnknownsDefault(
PyObject* /*m*/, PyObject* /*args*/) {
if (google::protobuf::internal::GetProto3PreserveUnknownsDefault()) {