Merge pull request #4698 from ThomasColthurst/proto_c_api

Introduce Proto C API; based on cl/198113115 by amauryfa
pull/1817/merge
Feng Xiao 7 years ago committed by GitHub
commit a7252bf42d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 10
      BUILD
  2. 1
      Makefile.am
  3. 92
      python/google/protobuf/proto_api.h
  4. 26
      python/google/protobuf/pyext/message.cc
  5. 3
      python/google/protobuf/pyext/message.h
  6. 17
      python/google/protobuf/pyext/message_module.cc

10
BUILD

@ -671,6 +671,7 @@ cc_binary(
linkstatic = 1, linkstatic = 1,
deps = [ deps = [
":protobuf", ":protobuf",
":proto_api",
] + select({ ] + select({
"//conditions:default": [], "//conditions:default": [],
":use_fast_cpp_protos": ["//external:python_headers"], ":use_fast_cpp_protos": ["//external:python_headers"],
@ -813,6 +814,15 @@ internal_protobuf_py_tests(
deps = [":python_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( proto_lang_toolchain(
name = "cc_toolchain", name = "cc_toolchain",
command_line = "--cpp_out=$(OUT)", command_line = "--cpp_out=$(OUT)",

@ -804,6 +804,7 @@ python_EXTRA_DIST= \
python/google/protobuf/message.py \ python/google/protobuf/message.py \
python/google/protobuf/message_factory.py \ python/google/protobuf/message_factory.py \
python/google/protobuf/python_protobuf.h \ python/google/protobuf/python_protobuf.h \
python/google/protobuf/proto_api.h \
python/google/protobuf/proto_builder.py \ python/google/protobuf/proto_builder.py \
python/google/protobuf/pyext/README \ python/google/protobuf/pyext/README \
python/google/protobuf/pyext/__init__.py \ python/google/protobuf/pyext/__init__.py \

@ -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__

@ -2866,17 +2866,38 @@ const Message* (*GetCProtoInsidePyProtoPtr)(PyObject* msg);
Message* (*MutableCProtoInsidePyProtoPtr)(PyObject* msg); Message* (*MutableCProtoInsidePyProtoPtr)(PyObject* msg);
static const Message* GetCProtoInsidePyProtoImpl(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)) { if (!PyObject_TypeCheck(msg, &CMessage_Type)) {
PyErr_SetString(PyExc_TypeError, "Not a Message instance");
return NULL; return NULL;
} }
CMessage* cmsg = reinterpret_cast<CMessage*>(msg); CMessage* cmsg = reinterpret_cast<CMessage*>(msg);
return cmsg->message; return cmsg->message;
} }
static Message* MutableCProtoInsidePyProtoImpl(PyObject* msg) { Message* PyMessage_GetMutableMessagePointer(PyObject* msg) {
if (!PyObject_TypeCheck(msg, &CMessage_Type)) { if (!PyObject_TypeCheck(msg, &CMessage_Type)) {
PyErr_SetString(PyExc_TypeError, "Not a Message instance");
return NULL; return NULL;
} }
CMessage* cmsg = reinterpret_cast<CMessage*>(msg); CMessage* cmsg = reinterpret_cast<CMessage*>(msg);
if ((cmsg->composite_fields && PyDict_Size(cmsg->composite_fields) != 0) || if ((cmsg->composite_fields && PyDict_Size(cmsg->composite_fields) != 0) ||
(cmsg->extensions != NULL && (cmsg->extensions != NULL &&
@ -2885,6 +2906,9 @@ static Message* MutableCProtoInsidePyProtoImpl(PyObject* msg) {
// the underlying C++ message back to the CMessage (e.g. removed repeated // the underlying C++ message back to the CMessage (e.g. removed repeated
// composite containers). We only allow direct mutation of the underlying // composite containers). We only allow direct mutation of the underlying
// C++ message if there is no child data in the CMessage. // 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; return NULL;
} }
cmessage::AssureWritable(cmsg); cmessage::AssureWritable(cmsg);

@ -341,6 +341,9 @@ bool CheckFieldBelongsToMessage(const FieldDescriptor* field_descriptor,
extern PyObject* PickleError_class; extern PyObject* PickleError_class;
const Message* PyMessage_GetMessagePointer(PyObject* msg);
Message* PyMessage_GetMutableMessagePointer(PyObject* msg);
bool InitProto2MessageModule(PyObject *m); bool InitProto2MessageModule(PyObject *m);
#if LANG_CXX11 #if LANG_CXX11

@ -31,9 +31,26 @@
#include <Python.h> #include <Python.h>
#include <google/protobuf/pyext/message.h> #include <google/protobuf/pyext/message.h>
#include <google/protobuf/proto_api.h>
#include <google/protobuf/message_lite.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( static PyObject* GetPythonProto3PreserveUnknownsDefault(
PyObject* /*m*/, PyObject* /*args*/) { PyObject* /*m*/, PyObject* /*args*/) {
if (google::protobuf::internal::GetProto3PreserveUnknownsDefault()) { if (google::protobuf::internal::GetProto3PreserveUnknownsDefault()) {

Loading…
Cancel
Save