aboutsummaryrefslogtreecommitdiffhomepage
path: root/python
diff options
context:
space:
mode:
Diffstat (limited to 'python')
-rw-r--r--python/README.md2
-rw-r--r--python/google/protobuf/internal/json_format_test.py3
-rw-r--r--python/google/protobuf/json_format.py6
-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.cc26
-rwxr-xr-xpython/setup.py8
-rwxr-xr-xpython/stubout.py3
9 files changed, 155 insertions, 14 deletions
diff --git a/python/README.md b/python/README.md
index 4c194297..4b48225a 100644
--- a/python/README.md
+++ b/python/README.md
@@ -30,7 +30,7 @@ join the Protocol Buffers discussion list and let us know!
Installation
============
-1) Make sure you have Python 2.6 or newer. If in doubt, run:
+1) Make sure you have Python 2.7 or newer. If in doubt, run:
$ python -V
diff --git a/python/google/protobuf/internal/json_format_test.py b/python/google/protobuf/internal/json_format_test.py
index d891dce1..7f649406 100644
--- a/python/google/protobuf/internal/json_format_test.py
+++ b/python/google/protobuf/internal/json_format_test.py
@@ -791,9 +791,6 @@ class JsonFormatTest(JsonFormatBase):
json_format.Parse(text, parsed_message, ignore_unknown_fields=True)
def testDuplicateField(self):
- # Duplicate key check is not supported for python2.6
- if sys.version_info < (2, 7):
- return
self.CheckError('{"int32Value": 1,\n"int32Value":2}',
'Failed to load JSON: duplicate key int32Value.')
diff --git a/python/google/protobuf/json_format.py b/python/google/protobuf/json_format.py
index 8d338d3e..58c94a47 100644
--- a/python/google/protobuf/json_format.py
+++ b/python/google/protobuf/json_format.py
@@ -396,11 +396,7 @@ def Parse(text, message, ignore_unknown_fields=False):
"""
if not isinstance(text, six.text_type): text = text.decode('utf-8')
try:
- if sys.version_info < (2, 7):
- # object_pair_hook is not supported before python2.7
- js = json.loads(text)
- else:
- js = json.loads(text, object_pairs_hook=_DuplicateChecker)
+ js = json.loads(text, object_pairs_hook=_DuplicateChecker)
except ValueError as e:
raise ParseError('Failed to load JSON: {0}.'.format(str(e)))
return ParseDict(js, message, ignore_unknown_fields)
diff --git a/python/google/protobuf/proto_api.h b/python/google/protobuf/proto_api.h
new file mode 100644
index 00000000..5c076d23
--- /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[] =
+ "google.protobuf.pyext._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..8c933866 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()) {
@@ -113,6 +130,15 @@ extern "C" {
Py_DECREF(m);
return INITFUNC_ERRORVAL;
}
+
+ // Adds the C++ API
+ if (PyObject* api =
+ PyCapsule_New(new ApiImplementation(),
+ google::protobuf::python::PyProtoAPICapsuleName(), NULL)) {
+ PyModule_AddObject(m, "proto_API", api);
+ } else {
+ return INITFUNC_ERRORVAL;
+ }
#if PY_MAJOR_VERSION >= 3
return m;
diff --git a/python/setup.py b/python/setup.py
index a9df075e..335e9114 100755
--- a/python/setup.py
+++ b/python/setup.py
@@ -44,6 +44,7 @@ def GetVersion():
with open(os.path.join('google', 'protobuf', '__init__.py')) as version_file:
exec(version_file.read(), globals())
+ global __version__
return __version__
@@ -148,10 +149,9 @@ class build_py(_build_py):
class test_conformance(_build_py):
target = 'test_python'
def run(self):
- if sys.version_info >= (2, 7):
- # Python 2.6 dodges these extra failures.
- os.environ["CONFORMANCE_PYTHON_EXTRA_FAILURES"] = (
- "--failure_list failure_list_python-post26.txt")
+ # Python 2.6 dodges these extra failures.
+ os.environ["CONFORMANCE_PYTHON_EXTRA_FAILURES"] = (
+ "--failure_list failure_list_python-post26.txt")
cmd = 'cd ../conformance && make %s' % (test_conformance.target)
status = subprocess.check_call(cmd, shell=True)
diff --git a/python/stubout.py b/python/stubout.py
index aee4f2da..ba391045 100755
--- a/python/stubout.py
+++ b/python/stubout.py
@@ -17,6 +17,9 @@
# This file is used for testing. The original is at:
# http://code.google.com/p/pymox/
+import inspect
+
+
class StubOutForTesting:
"""Sample Usage:
You want os.path.exists() to always return true during testing.