aboutsummaryrefslogtreecommitdiff
path: root/python
diff options
context:
space:
mode:
Diffstat (limited to 'python')
-rw-r--r--python/README.md (renamed from python/README.txt)33
-rwxr-xr-xpython/ez_setup.py1
-rwxr-xr-xpython/google/protobuf/__init__.py35
-rwxr-xr-xpython/google/protobuf/descriptor.py378
-rw-r--r--python/google/protobuf/descriptor_database.py137
-rw-r--r--python/google/protobuf/descriptor_pool.py671
-rwxr-xr-xpython/google/protobuf/internal/_parameterized.py438
-rw-r--r--python/google/protobuf/internal/api_implementation.cc129
-rwxr-xr-xpython/google/protobuf/internal/api_implementation.py81
-rwxr-xr-xpython/google/protobuf/internal/containers.py73
-rwxr-xr-xpython/google/protobuf/internal/cpp_message.py121
-rwxr-xr-xpython/google/protobuf/internal/decoder.py168
-rw-r--r--python/google/protobuf/internal/descriptor_database_test.py65
-rw-r--r--python/google/protobuf/internal/descriptor_pool_test.py591
-rw-r--r--python/google/protobuf/internal/descriptor_pool_test1.proto96
-rw-r--r--python/google/protobuf/internal/descriptor_pool_test2.proto (renamed from python/google/protobuf/pyext/python_descriptor.h)79
-rw-r--r--python/google/protobuf/internal/descriptor_python_test.py54
-rwxr-xr-xpython/google/protobuf/internal/descriptor_test.py615
-rwxr-xr-xpython/google/protobuf/internal/encoder.py45
-rw-r--r--python/google/protobuf/internal/enum_type_wrapper.py89
-rw-r--r--python/google/protobuf/internal/factory_test1.proto (renamed from python/google/protobuf/pyext/python_protobuf.cc)49
-rw-r--r--python/google/protobuf/internal/factory_test2.proto99
-rwxr-xr-xpython/google/protobuf/internal/generator_test.py113
-rw-r--r--python/google/protobuf/internal/import_test_package/__init__.py33
-rw-r--r--python/google/protobuf/internal/import_test_package/inner.proto37
-rw-r--r--python/google/protobuf/internal/import_test_package/outer.proto39
-rw-r--r--python/google/protobuf/internal/message_factory_python_test.py54
-rw-r--r--python/google/protobuf/internal/message_factory_test.py131
-rwxr-xr-xpython/google/protobuf/internal/message_listener.py2
-rw-r--r--python/google/protobuf/internal/message_python_test.py54
-rwxr-xr-xpython/google/protobuf/internal/message_test.py986
-rw-r--r--python/google/protobuf/internal/missing_enum_values.proto52
-rw-r--r--python/google/protobuf/internal/more_extensions.proto3
-rw-r--r--python/google/protobuf/internal/more_extensions_dynamic.proto50
-rw-r--r--python/google/protobuf/internal/more_messages.proto3
-rw-r--r--python/google/protobuf/internal/proto_builder_test.py77
-rwxr-xr-xpython/google/protobuf/internal/python_message.py304
-rwxr-xr-xpython/google/protobuf/internal/reflection_test.py755
-rwxr-xr-xpython/google/protobuf/internal/service_reflection_test.py10
-rw-r--r--python/google/protobuf/internal/symbol_database_test.py120
-rw-r--r--python/google/protobuf/internal/test_bad_identifiers.proto53
-rwxr-xr-xpython/google/protobuf/internal/test_util.py283
-rwxr-xr-xpython/google/protobuf/internal/text_encoding_test.py68
-rwxr-xr-xpython/google/protobuf/internal/text_format_test.py760
-rwxr-xr-xpython/google/protobuf/internal/type_checkers.py83
-rwxr-xr-xpython/google/protobuf/internal/unknown_fields_test.py264
-rwxr-xr-xpython/google/protobuf/internal/wire_format.py2
-rwxr-xr-xpython/google/protobuf/internal/wire_format_test.py10
-rwxr-xr-xpython/google/protobuf/message.py36
-rw-r--r--python/google/protobuf/message_factory.py155
-rw-r--r--python/google/protobuf/proto_builder.py98
-rw-r--r--python/google/protobuf/pyext/README6
-rw-r--r--python/google/protobuf/pyext/__init__.py0
-rw-r--r--python/google/protobuf/pyext/cpp_message.py57
-rw-r--r--python/google/protobuf/pyext/descriptor.cc1471
-rw-r--r--python/google/protobuf/pyext/descriptor.h92
-rw-r--r--python/google/protobuf/pyext/descriptor_containers.cc1564
-rw-r--r--python/google/protobuf/pyext/descriptor_containers.h95
-rw-r--r--python/google/protobuf/pyext/descriptor_pool.cc370
-rw-r--r--python/google/protobuf/pyext/descriptor_pool.h152
-rw-r--r--python/google/protobuf/pyext/extension_dict.cc309
-rw-r--r--python/google/protobuf/pyext/extension_dict.h132
-rw-r--r--python/google/protobuf/pyext/message.cc2575
-rw-r--r--python/google/protobuf/pyext/message.h310
-rw-r--r--python/google/protobuf/pyext/proto2_api_test.proto40
-rw-r--r--python/google/protobuf/pyext/python-proto2.cc1658
-rw-r--r--python/google/protobuf/pyext/python.proto68
-rw-r--r--python/google/protobuf/pyext/python_descriptor.cc334
-rw-r--r--python/google/protobuf/pyext/python_protobuf.h2
-rw-r--r--python/google/protobuf/pyext/repeated_composite_container.cc776
-rw-r--r--python/google/protobuf/pyext/repeated_composite_container.h177
-rw-r--r--python/google/protobuf/pyext/repeated_scalar_container.cc813
-rw-r--r--python/google/protobuf/pyext/repeated_scalar_container.h118
-rw-r--r--python/google/protobuf/pyext/scoped_pyobject_ptr.h97
-rwxr-xr-xpython/google/protobuf/reflection.py75
-rwxr-xr-xpython/google/protobuf/service.py2
-rwxr-xr-xpython/google/protobuf/service_reflection.py2
-rw-r--r--python/google/protobuf/symbol_database.py185
-rw-r--r--python/google/protobuf/text_encoding.py110
-rwxr-xr-xpython/google/protobuf/text_format.py567
-rwxr-xr-xpython/mox.py2
-rwxr-xr-xpython/setup.py183
82 files changed, 17770 insertions, 3254 deletions
diff --git a/python/README.txt b/python/README.md
index 6dc144e9..ee7e7213 100644
--- a/python/README.txt
+++ b/python/README.md
@@ -1,4 +1,8 @@
Protocol Buffers - Google's data interchange format
+===================================================
+
+[![Build Status](https://travis-ci.org/google/protobuf.svg?branch=master)](https://travis-ci.org/google/protobuf)
+
Copyright 2008 Google Inc.
This directory contains the Python Protocol Buffers runtime library.
@@ -6,7 +10,7 @@ This directory contains the Python Protocol Buffers runtime library.
Normally, this directory comes as part of the protobuf package, available
from:
- http://code.google.com/p/protobuf
+ https://developers.google.com/protocol-buffers/
The complete package includes the C++ source code, which includes the
Protocol Compiler (protoc). If you downloaded this package from PyPI
@@ -26,7 +30,7 @@ join the Protocol Buffers discussion list and let us know!
Installation
============
-1) Make sure you have Python 2.4 or newer. If in doubt, run:
+1) Make sure you have Python 2.6 or newer. If in doubt, run:
$ python -V
@@ -35,7 +39,7 @@ Installation
If you would rather install it manually, you may do so by following
the instructions on this page:
- http://peak.telecommunity.com/DevCenter/EasyInstall#installation-instructions
+ https://packaging.python.org/en/latest/installing.html#setup-for-installing-packages
3) Build the C++ code, or install a binary distribution of protoc. If
you install a binary distribution, make sure that it is the same
@@ -43,9 +47,14 @@ Installation
$ protoc --version
-4) Run the tests:
+4) Build and run the tests:
- $ python setup.py test
+ $ python setup.py build
+ $ python setup.py google_test
+
+ If you want to build/test c++ implementation, run:
+ $ python setup.py build --cpp_implementation
+ $ python setup.py google_test --cpp_implementation
If some tests fail, this library may not work correctly on your
system. Continue at your own risk.
@@ -60,7 +69,11 @@ Installation
5) Install:
- $ python setup.py install
+ $ python setup.py install
+
+ or:
+
+ $ python setup.py install --cpp_implementation
This step may require superuser privileges.
NOTE: To use C++ implementation, you need to install C++ protobuf runtime
@@ -73,13 +86,11 @@ Usage
The complete documentation for Protocol Buffers is available via the
web at:
- http://code.google.com/apis/protocolbuffers/
+ https://developers.google.com/protocol-buffers/
C++ Implementation
==================
-WARNING: This is EXPERIMENTAL and only available for CPython platforms.
-
The C++ implementation for Python messages is built as a Python extension to
improve the overall protobuf Python performance.
@@ -88,7 +99,8 @@ To use the C++ implementation, you need to:
parent directory.
2) Export an environment variable:
- $ export PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION=cpp
+ $ export PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION=cpp
+ $ export PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION_VERSION=2
You need to export this variable before running setup.py script to build and
install the extension. You must also set the variable at runtime, otherwise
@@ -97,4 +109,3 @@ change the default so that C++ implementation is used whenever it is available.
It is strongly recommended to run `python setup.py test` after setting the
variable to "cpp", so the tests will be against C++ implemented Python
messages.
-
diff --git a/python/ez_setup.py b/python/ez_setup.py
index a2cf777d..3aec98e4 100755
--- a/python/ez_setup.py
+++ b/python/ez_setup.py
@@ -107,6 +107,7 @@ def use_setuptools(
except ImportError:
return do_download()
try:
+ return do_download()
pkg_resources.require("setuptools>="+version); return
except pkg_resources.VersionConflict, e:
if was_imported:
diff --git a/python/google/protobuf/__init__.py b/python/google/protobuf/__init__.py
index e69de29b..1345bd5f 100755
--- a/python/google/protobuf/__init__.py
+++ b/python/google/protobuf/__init__.py
@@ -0,0 +1,35 @@
+# 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.
+
+# Needs to stay compatible with Python 2.5 due to GAE.
+#
+# Copyright 2007 Google Inc. All Rights Reserved.
+
+__version__ = '3.0.0a3.dev0'
diff --git a/python/google/protobuf/descriptor.py b/python/google/protobuf/descriptor.py
index cf609bee..f7a58ca0 100755
--- a/python/google/protobuf/descriptor.py
+++ b/python/google/protobuf/descriptor.py
@@ -1,6 +1,6 @@
# Protocol Buffers - Google's data interchange format
# Copyright 2008 Google Inc. All rights reserved.
-# http://code.google.com/p/protobuf/
+# 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
@@ -28,30 +28,59 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+# Needs to stay compatible with Python 2.5 due to GAE.
+#
+# Copyright 2007 Google Inc. All Rights Reserved.
+
"""Descriptors essentially contain exactly the information found in a .proto
file, in types that make this information accessible in Python.
"""
__author__ = 'robinson@google.com (Will Robinson)'
-
from google.protobuf.internal import api_implementation
+_USE_C_DESCRIPTORS = False
if api_implementation.Type() == 'cpp':
- from google.protobuf.internal import cpp_message
+ # Used by MakeDescriptor in cpp mode
+ import os
+ import uuid
+ from google.protobuf.pyext import _message
+ _USE_C_DESCRIPTORS = getattr(_message, '_USE_C_DESCRIPTORS', False)
class Error(Exception):
"""Base error for this module."""
+class TypeTransformationError(Error):
+ """Error transforming between python proto type and corresponding C++ type."""
+
+
+if _USE_C_DESCRIPTORS:
+ # This metaclass allows to override the behavior of code like
+ # isinstance(my_descriptor, FieldDescriptor)
+ # and make it return True when the descriptor is an instance of the extension
+ # type written in C++.
+ class DescriptorMetaclass(type):
+ def __instancecheck__(cls, obj):
+ if super(DescriptorMetaclass, cls).__instancecheck__(obj):
+ return True
+ if isinstance(obj, cls._C_DESCRIPTOR_CLASS):
+ return True
+ return False
+else:
+ # The standard metaclass; nothing changes.
+ DescriptorMetaclass = type
+
+
class DescriptorBase(object):
"""Descriptors base class.
This class is the base of all descriptor classes. It provides common options
- related functionaility.
+ related functionality.
Attributes:
has_options: True if the descriptor has non-default options. Usually it
@@ -61,6 +90,12 @@ class DescriptorBase(object):
avoid some bootstrapping issues.
"""
+ __metaclass__ = DescriptorMetaclass
+ if _USE_C_DESCRIPTORS:
+ # The class, or tuple of classes, that are considered as "virtual
+ # subclasses" of this descriptor class.
+ _C_DESCRIPTOR_CLASS = ()
+
def __init__(self, options, options_class_name):
"""Initialize the descriptor given its options message and the name of the
class of the options message. The name of the class is required in case
@@ -72,6 +107,18 @@ class DescriptorBase(object):
# Does this descriptor have non-default options?
self.has_options = options is not None
+ def _SetOptions(self, options, options_class_name):
+ """Sets the descriptor's options
+
+ This function is used in generated proto2 files to update descriptor
+ options. It must not be used outside proto2.
+ """
+ self._options = options
+ self._options_class_name = options_class_name
+
+ # Does this descriptor have non-default options?
+ self.has_options = options is not None
+
def GetOptions(self):
"""Retrieves descriptor options.
@@ -201,13 +248,33 @@ class Descriptor(_NestedDescriptorBase):
options: (descriptor_pb2.MessageOptions) Protocol message options or None
to use default message options.
+ oneofs: (list of OneofDescriptor) The list of descriptors for oneof fields
+ in this message.
+ oneofs_by_name: (dict str -> OneofDescriptor) Same objects as in |oneofs|,
+ but indexed by "name" attribute.
+
file: (FileDescriptor) Reference to file descriptor.
"""
+ if _USE_C_DESCRIPTORS:
+ _C_DESCRIPTOR_CLASS = _message.Descriptor
+
+ def __new__(cls, name, full_name, filename, containing_type, fields,
+ nested_types, enum_types, extensions, options=None,
+ is_extendable=True, extension_ranges=None, oneofs=None,
+ file=None, serialized_start=None, serialized_end=None,
+ syntax=None):
+ _message.Message._CheckCalledFromGeneratedFile()
+ return _message.Message._GetMessageDescriptor(full_name)
+
+ # NOTE(tmarek): The file argument redefining a builtin is nothing we can
+ # fix right now since we don't know how many clients already rely on the
+ # name of the argument.
def __init__(self, name, full_name, filename, containing_type, fields,
nested_types, enum_types, extensions, options=None,
- is_extendable=True, extension_ranges=None, file=None,
- serialized_start=None, serialized_end=None):
+ is_extendable=True, extension_ranges=None, oneofs=None,
+ file=None, serialized_start=None, serialized_end=None,
+ syntax=None): # pylint:disable=redefined-builtin
"""Arguments to __init__() are as described in the description
of Descriptor fields above.
@@ -217,7 +284,7 @@ class Descriptor(_NestedDescriptorBase):
super(Descriptor, self).__init__(
options, 'MessageOptions', name, full_name, file,
containing_type, serialized_start=serialized_start,
- serialized_end=serialized_start)
+ serialized_end=serialized_end)
# We have fields in addition to fields_by_name and fields_by_number,
# so that:
@@ -231,6 +298,8 @@ class Descriptor(_NestedDescriptorBase):
self.fields_by_name = dict((f.name, f) for f in fields)
self.nested_types = nested_types
+ for nested_type in nested_types:
+ nested_type.containing_type = self
self.nested_types_by_name = dict((t.name, t) for t in nested_types)
self.enum_types = enum_types
@@ -246,9 +315,29 @@ class Descriptor(_NestedDescriptorBase):
self.extensions_by_name = dict((f.name, f) for f in extensions)
self.is_extendable = is_extendable
self.extension_ranges = extension_ranges
+ self.oneofs = oneofs if oneofs is not None else []
+ self.oneofs_by_name = dict((o.name, o) for o in self.oneofs)
+ for oneof in self.oneofs:
+ oneof.containing_type = self
+ self.syntax = syntax or "proto2"
- self._serialized_start = serialized_start
- self._serialized_end = serialized_end
+ def EnumValueName(self, enum, value):
+ """Returns the string name of an enum value.
+
+ This is just a small helper method to simplify a common operation.
+
+ Args:
+ enum: string name of the Enum.
+ value: int, value of the enum.
+
+ Returns:
+ string name of the enum value.
+
+ Raises:
+ KeyError if either the Enum doesn't exist or the value is not a valid
+ value for the enum.
+ """
+ return self.enum_types_by_name[enum].values_by_number[value].name
def CopyToProto(self, proto):
"""Copies this to a descriptor_pb2.DescriptorProto.
@@ -275,7 +364,7 @@ class FieldDescriptor(DescriptorBase):
"""Descriptor for a single field in a .proto file.
- A FieldDescriptor instance has the following attriubtes:
+ A FieldDescriptor instance has the following attributes:
name: (str) Name of this field, exactly as it appears in .proto.
full_name: (str) Name of this field, including containing scope. This is
@@ -316,6 +405,9 @@ class FieldDescriptor(DescriptorBase):
options: (descriptor_pb2.FieldOptions) Protocol message field options or
None to use default field options.
+
+ containing_oneof: (OneofDescriptor) If the field is a member of a oneof
+ union, contains its descriptor. Otherwise, None.
"""
# Must be consistent with C++ FieldDescriptor::Type enum in
@@ -358,6 +450,27 @@ class FieldDescriptor(DescriptorBase):
CPPTYPE_MESSAGE = 10
MAX_CPPTYPE = 10
+ _PYTHON_TO_CPP_PROTO_TYPE_MAP = {
+ TYPE_DOUBLE: CPPTYPE_DOUBLE,
+ TYPE_FLOAT: CPPTYPE_FLOAT,
+ TYPE_ENUM: CPPTYPE_ENUM,
+ TYPE_INT64: CPPTYPE_INT64,
+ TYPE_SINT64: CPPTYPE_INT64,
+ TYPE_SFIXED64: CPPTYPE_INT64,
+ TYPE_UINT64: CPPTYPE_UINT64,
+ TYPE_FIXED64: CPPTYPE_UINT64,
+ TYPE_INT32: CPPTYPE_INT32,
+ TYPE_SFIXED32: CPPTYPE_INT32,
+ TYPE_SINT32: CPPTYPE_INT32,
+ TYPE_UINT32: CPPTYPE_UINT32,
+ TYPE_FIXED32: CPPTYPE_UINT32,
+ TYPE_BYTES: CPPTYPE_STRING,
+ TYPE_STRING: CPPTYPE_STRING,
+ TYPE_BOOL: CPPTYPE_BOOL,
+ TYPE_MESSAGE: CPPTYPE_MESSAGE,
+ TYPE_GROUP: CPPTYPE_MESSAGE
+ }
+
# Must be consistent with C++ FieldDescriptor::Label enum in
# descriptor.h.
#
@@ -367,10 +480,29 @@ class FieldDescriptor(DescriptorBase):
LABEL_REPEATED = 3
MAX_LABEL = 3
+ # Must be consistent with C++ constants kMaxNumber, kFirstReservedNumber,
+ # and kLastReservedNumber in descriptor.h
+ MAX_FIELD_NUMBER = (1 << 29) - 1
+ FIRST_RESERVED_FIELD_NUMBER = 19000
+ LAST_RESERVED_FIELD_NUMBER = 19999
+
+ if _USE_C_DESCRIPTORS:
+ _C_DESCRIPTOR_CLASS = _message.FieldDescriptor
+
+ def __new__(cls, name, full_name, index, number, type, cpp_type, label,
+ default_value, message_type, enum_type, containing_type,
+ is_extension, extension_scope, options=None,
+ has_default_value=True, containing_oneof=None):
+ _message.Message._CheckCalledFromGeneratedFile()
+ if is_extension:
+ return _message.Message._GetExtensionDescriptor(full_name)
+ else:
+ return _message.Message._GetFieldDescriptor(full_name)
+
def __init__(self, name, full_name, index, number, type, cpp_type, label,
default_value, message_type, enum_type, containing_type,
is_extension, extension_scope, options=None,
- has_default_value=True):
+ has_default_value=True, containing_oneof=None):
"""The arguments are as described in the description of FieldDescriptor
attributes above.
@@ -393,14 +525,40 @@ class FieldDescriptor(DescriptorBase):
self.enum_type = enum_type
self.is_extension = is_extension
self.extension_scope = extension_scope
+ self.containing_oneof = containing_oneof
if api_implementation.Type() == 'cpp':
if is_extension:
- self._cdescriptor = cpp_message.GetExtensionDescriptor(full_name)
+ # pylint: disable=protected-access
+ self._cdescriptor = (
+ _message.Message._GetExtensionDescriptor(full_name))
+ # pylint: enable=protected-access
else:
- self._cdescriptor = cpp_message.GetFieldDescriptor(full_name)
+ # pylint: disable=protected-access
+ self._cdescriptor = _message.Message._GetFieldDescriptor(full_name)
+ # pylint: enable=protected-access
else:
self._cdescriptor = None
+ @staticmethod
+ def ProtoTypeToCppProtoType(proto_type):
+ """Converts from a Python proto type to a C++ Proto Type.
+
+ The Python ProtocolBuffer classes specify both the 'Python' datatype and the
+ 'C++' datatype - and they're not the same. This helper method should
+ translate from one to another.
+
+ Args:
+ proto_type: the Python proto type (descriptor.FieldDescriptor.TYPE_*)
+ Returns:
+ descriptor.FieldDescriptor.CPPTYPE_*, the C++ type.
+ Raises:
+ TypeTransformationError: when the Python proto type isn't known.
+ """
+ try:
+ return FieldDescriptor._PYTHON_TO_CPP_PROTO_TYPE_MAP[proto_type]
+ except KeyError:
+ raise TypeTransformationError('Unknown proto_type: %s' % proto_type)
+
class EnumDescriptor(_NestedDescriptorBase):
@@ -427,6 +585,15 @@ class EnumDescriptor(_NestedDescriptorBase):
None to use default enum options.
"""
+ if _USE_C_DESCRIPTORS:
+ _C_DESCRIPTOR_CLASS = _message.EnumDescriptor
+
+ def __new__(cls, name, full_name, filename, values,
+ containing_type=None, options=None, file=None,
+ serialized_start=None, serialized_end=None):
+ _message.Message._CheckCalledFromGeneratedFile()
+ return _message.Message._GetEnumDescriptor(full_name)
+
def __init__(self, name, full_name, filename, values,
containing_type=None, options=None, file=None,
serialized_start=None, serialized_end=None):
@@ -438,7 +605,7 @@ class EnumDescriptor(_NestedDescriptorBase):
super(EnumDescriptor, self).__init__(
options, 'EnumOptions', name, full_name, file,
containing_type, serialized_start=serialized_start,
- serialized_end=serialized_start)
+ serialized_end=serialized_end)
self.values = values
for value in self.values:
@@ -446,9 +613,6 @@ class EnumDescriptor(_NestedDescriptorBase):
self.values_by_name = dict((v.name, v) for v in values)
self.values_by_number = dict((v.number, v) for v in values)
- self._serialized_start = serialized_start
- self._serialized_end = serialized_end
-
def CopyToProto(self, proto):
"""Copies this to a descriptor_pb2.EnumDescriptorProto.
@@ -474,6 +638,17 @@ class EnumValueDescriptor(DescriptorBase):
None to use default enum value options options.
"""
+ if _USE_C_DESCRIPTORS:
+ _C_DESCRIPTOR_CLASS = _message.EnumValueDescriptor
+
+ def __new__(cls, name, index, number, type=None, options=None):
+ _message.Message._CheckCalledFromGeneratedFile()
+ # There is no way we can build a complete EnumValueDescriptor with the
+ # given parameters (the name of the Enum is not known, for example).
+ # Fortunately generated files just pass it to the EnumDescriptor()
+ # constructor, which will ignore it, so returning None is good enough.
+ return None
+
def __init__(self, name, index, number, type=None, options=None):
"""Arguments are as described in the attribute description above."""
super(EnumValueDescriptor, self).__init__(options, 'EnumValueOptions')
@@ -483,6 +658,36 @@ class EnumValueDescriptor(DescriptorBase):
self.type = type
+class OneofDescriptor(object):
+ """Descriptor for a oneof field.
+
+ name: (str) Name of the oneof field.
+ full_name: (str) Full name of the oneof field, including package name.
+ index: (int) 0-based index giving the order of the oneof field inside
+ its containing type.
+ containing_type: (Descriptor) Descriptor of the protocol message
+ type that contains this field. Set by the Descriptor constructor
+ if we're passed into one.
+ fields: (list of FieldDescriptor) The list of field descriptors this
+ oneof can contain.
+ """
+
+ if _USE_C_DESCRIPTORS:
+ _C_DESCRIPTOR_CLASS = _message.OneofDescriptor
+
+ def __new__(cls, name, full_name, index, containing_type, fields):
+ _message.Message._CheckCalledFromGeneratedFile()
+ return _message.Message._GetOneofDescriptor(full_name)
+
+ def __init__(self, name, full_name, index, containing_type, fields):
+ """Arguments are as described in the attribute description above."""
+ self.name = name
+ self.full_name = full_name
+ self.index = index
+ self.containing_type = containing_type
+ self.fields = fields
+
+
class ServiceDescriptor(_NestedDescriptorBase):
"""Descriptor for a service.
@@ -561,23 +766,57 @@ class MethodDescriptor(DescriptorBase):
class FileDescriptor(DescriptorBase):
"""Descriptor for a file. Mimics the descriptor_pb2.FileDescriptorProto.
+ Note that enum_types_by_name, extensions_by_name, and dependencies
+ fields are only set by the message_factory module, and not by the
+ generated proto code.
+
name: name of file, relative to root of source tree.
package: name of the package
+ syntax: string indicating syntax of the file (can be "proto2" or "proto3")
serialized_pb: (str) Byte string of serialized
descriptor_pb2.FileDescriptorProto.
+ dependencies: List of other FileDescriptors this FileDescriptor depends on.
+ message_types_by_name: Dict of message names of their descriptors.
+ enum_types_by_name: Dict of enum names and their descriptors.
+ extensions_by_name: Dict of extension names and their descriptors.
"""
- def __init__(self, name, package, options=None, serialized_pb=None):
+ if _USE_C_DESCRIPTORS:
+ _C_DESCRIPTOR_CLASS = _message.FileDescriptor
+
+ def __new__(cls, name, package, options=None, serialized_pb=None,
+ dependencies=None, syntax=None):
+ # FileDescriptor() is called from various places, not only from generated
+ # files, to register dynamic proto files and messages.
+ # TODO(amauryfa): Expose BuildFile() as a public function and make this
+ # constructor an implementation detail.
+ if serialized_pb:
+ # pylint: disable=protected-access2
+ return _message.Message._BuildFile(serialized_pb)
+ # pylint: enable=protected-access
+ else:
+ return super(FileDescriptor, cls).__new__(cls)
+
+ def __init__(self, name, package, options=None, serialized_pb=None,
+ dependencies=None, syntax=None):
"""Constructor."""
super(FileDescriptor, self).__init__(options, 'FileOptions')
self.message_types_by_name = {}
self.name = name
self.package = package
+ self.syntax = syntax or "proto2"
self.serialized_pb = serialized_pb
+
+ self.enum_types_by_name = {}
+ self.extensions_by_name = {}
+ self.dependencies = (dependencies or [])
+
if (api_implementation.Type() == 'cpp' and
self.serialized_pb is not None):
- cpp_message.BuildFile(self.serialized_pb)
+ # pylint: disable=protected-access
+ _message.Message._BuildFile(self.serialized_pb)
+ # pylint: enable=protected-access
def CopyToProto(self, proto):
"""Copies this to a descriptor_pb2.FileDescriptorProto.
@@ -596,3 +835,104 @@ def _ParseOptions(message, string):
"""
message.ParseFromString(string)
return message
+
+
+def MakeDescriptor(desc_proto, package='', build_file_if_cpp=True,
+ syntax=None):
+ """Make a protobuf Descriptor given a DescriptorProto protobuf.
+
+ Handles nested descriptors. Note that this is limited to the scope of defining
+ a message inside of another message. Composite fields can currently only be
+ resolved if the message is defined in the same scope as the field.
+
+ Args:
+ desc_proto: The descriptor_pb2.DescriptorProto protobuf message.
+ package: Optional package name for the new message Descriptor (string).
+ build_file_if_cpp: Update the C++ descriptor pool if api matches.
+ Set to False on recursion, so no duplicates are created.
+ syntax: The syntax/semantics that should be used. Set to "proto3" to get
+ proto3 field presence semantics.
+ Returns:
+ A Descriptor for protobuf messages.
+ """
+ if api_implementation.Type() == 'cpp' and build_file_if_cpp:
+ # The C++ implementation requires all descriptors to be backed by the same
+ # definition in the C++ descriptor pool. To do this, we build a
+ # FileDescriptorProto with the same definition as this descriptor and build
+ # it into the pool.
+ from google.protobuf import descriptor_pb2
+ file_descriptor_proto = descriptor_pb2.FileDescriptorProto()
+ file_descriptor_proto.message_type.add().MergeFrom(desc_proto)
+
+ # Generate a random name for this proto file to prevent conflicts with
+ # any imported ones. We need to specify a file name so BuildFile accepts
+ # our FileDescriptorProto, but it is not important what that file name
+ # is actually set to.
+ proto_name = str(uuid.uuid4())
+
+ if package:
+ file_descriptor_proto.name = os.path.join(package.replace('.', '/'),
+ proto_name + '.proto')
+ file_descriptor_proto.package = package
+ else:
+ file_descriptor_proto.name = proto_name + '.proto'
+
+ # pylint: disable=protected-access
+ result = _message.Message._BuildFile(
+ file_descriptor_proto.SerializeToString())
+ # pylint: enable=protected-access
+
+ if _USE_C_DESCRIPTORS:
+ return result.message_types_by_name[desc_proto.name]
+
+ full_message_name = [desc_proto.name]
+ if package: full_message_name.insert(0, package)
+
+ # Create Descriptors for enum types
+ enum_types = {}
+ for enum_proto in desc_proto.enum_type:
+ full_name = '.'.join(full_message_name + [enum_proto.name])
+ enum_desc = EnumDescriptor(
+ enum_proto.name, full_name, None, [
+ EnumValueDescriptor(enum_val.name, ii, enum_val.number)
+ for ii, enum_val in enumerate(enum_proto.value)])
+ enum_types[full_name] = enum_desc
+
+ # Create Descriptors for nested types
+ nested_types = {}
+ for nested_proto in desc_proto.nested_type:
+ full_name = '.'.join(full_message_name + [nested_proto.name])
+ # Nested types are just those defined inside of the message, not all types
+ # used by fields in the message, so no loops are possible here.
+ nested_desc = MakeDescriptor(nested_proto,
+ package='.'.join(full_message_name),
+ build_file_if_cpp=False,
+ syntax=syntax)
+ nested_types[full_name] = nested_desc
+
+ fields = []
+ for field_proto in desc_proto.field:
+ full_name = '.'.join(full_message_name + [field_proto.name])
+ enum_desc = None
+ nested_desc = None
+ if field_proto.HasField('type_name'):
+ type_name = field_proto.type_name
+ full_type_name = '.'.join(full_message_name +
+ [type_name[type_name.rfind('.')+1:]])
+ if full_type_name in nested_types:
+ nested_desc = nested_types[full_type_name]
+ elif full_type_name in enum_types:
+ enum_desc = enum_types[full_type_name]
+ # Else type_name references a non-local type, which isn't implemented
+ field = FieldDescriptor(
+ field_proto.name, full_name, field_proto.number - 1,
+ field_proto.number, field_proto.type,
+ FieldDescriptor.ProtoTypeToCppProtoType(field_proto.type),
+ field_proto.label, None, nested_desc, enum_desc, None, False, None,
+ options=field_proto.options, has_default_value=False)
+ fields.append(field)
+
+ desc_name = '.'.join(full_message_name)
+ return Descriptor(desc_proto.name, desc_name, None, None, fields,
+ nested_types.values(), enum_types.values(), [],
+ options=desc_proto.options)
diff --git a/python/google/protobuf/descriptor_database.py b/python/google/protobuf/descriptor_database.py
new file mode 100644
index 00000000..b10021e9
--- /dev/null
+++ b/python/google/protobuf/descriptor_database.py
@@ -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.
+
+"""Provides a container for DescriptorProtos."""
+
+__author__ = 'matthewtoia@google.com (Matt Toia)'
+
+
+class Error(Exception):
+ pass
+
+
+class DescriptorDatabaseConflictingDefinitionError(Error):
+ """Raised when a proto is added with the same name & different descriptor."""
+
+
+class DescriptorDatabase(object):
+ """A container accepting FileDescriptorProtos and maps DescriptorProtos."""
+
+ def __init__(self):
+ self._file_desc_protos_by_file = {}
+ self._file_desc_protos_by_symbol = {}
+
+ def Add(self, file_desc_proto):
+ """Adds the FileDescriptorProto and its types to this database.
+
+ Args:
+ file_desc_proto: The FileDescriptorProto to add.
+ Raises:
+ DescriptorDatabaseException: if an attempt is made to add a proto
+ with the same name but different definition than an exisiting
+ proto in the database.
+ """
+ proto_name = file_desc_proto.name
+ if proto_name not in self._file_desc_protos_by_file:
+ self._file_desc_protos_by_file[proto_name] = file_desc_proto
+ elif self._file_desc_protos_by_file[proto_name] != file_desc_proto:
+ raise DescriptorDatabaseConflictingDefinitionError(
+ '%s already added, but with different descriptor.' % proto_name)
+
+ package = file_desc_proto.package
+ for message in file_desc_proto.message_type:
+ self._file_desc_protos_by_symbol.update(
+ (name, file_desc_proto) for name in _ExtractSymbols(message, package))
+ for enum in file_desc_proto.enum_type:
+ self._file_desc_protos_by_symbol[
+ '.'.join((package, enum.name))] = file_desc_proto
+
+ def FindFileByName(self, name):
+ """Finds the file descriptor proto by file name.
+
+ Typically the file name is a relative path ending to a .proto file. The
+ proto with the given name will have to have been added to this database
+ using the Add method or else an error will be raised.
+
+ Args:
+ name: The file name to find.
+
+ Returns:
+ The file descriptor proto matching the name.
+
+ Raises:
+ KeyError if no file by the given name was added.
+ """
+
+ return self._file_desc_protos_by_file[name]
+
+ def FindFileContainingSymbol(self, symbol):
+ """Finds the file descriptor proto containing the specified symbol.
+
+ The symbol should be a fully qualified name including the file descriptor's
+ package and any containing messages. Some examples:
+
+ 'some.package.name.Message'
+ 'some.package.name.Message.NestedEnum'
+
+ The file descriptor proto containing the specified symbol must be added to
+ this database using the Add method or else an error will be raised.
+
+ Args:
+ symbol: The fully qualified symbol name.
+
+ Returns:
+ The file descriptor proto containing the symbol.
+
+ Raises:
+ KeyError if no file contains the specified symbol.
+ """
+
+ return self._file_desc_protos_by_symbol[symbol]
+
+
+def _ExtractSymbols(desc_proto, package):
+ """Pulls out all the symbols from a descriptor proto.
+
+ Args:
+ desc_proto: The proto to extract symbols from.
+ package: The package containing the descriptor type.
+
+ Yields:
+ The fully qualified name found in the descriptor.
+ """
+
+ message_name = '.'.join((package, desc_proto.name))
+ yield message_name
+ for nested_type in desc_proto.nested_type:
+ for symbol in _ExtractSymbols(nested_type, message_name):
+ yield symbol
+ for enum_type in desc_proto.enum_type:
+ yield '.'.join((message_name, enum_type.name))
diff --git a/python/google/protobuf/descriptor_pool.py b/python/google/protobuf/descriptor_pool.py
new file mode 100644
index 00000000..7e7701f8
--- /dev/null
+++ b/python/google/protobuf/descriptor_pool.py
@@ -0,0 +1,671 @@
+# 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.
+
+"""Provides DescriptorPool to use as a container for proto2 descriptors.
+
+The DescriptorPool is used in conjection with a DescriptorDatabase to maintain
+a collection of protocol buffer descriptors for use when dynamically creating
+message types at runtime.
+
+For most applications protocol buffers should be used via modules generated by
+the protocol buffer compiler tool. This should only be used when the type of
+protocol buffers used in an application or library cannot be predetermined.
+
+Below is a straightforward example on how to use this class:
+
+ pool = DescriptorPool()
+ file_descriptor_protos = [ ... ]
+ for file_descriptor_proto in file_descriptor_protos:
+ pool.Add(file_descriptor_proto)
+ my_message_descriptor = pool.FindMessageTypeByName('some.package.MessageType')
+
+The message descriptor can be used in conjunction with the message_factory
+module in order to create a protocol buffer class that can be encoded and
+decoded.
+
+If you want to get a Python class for the specified proto, use the
+helper functions inside google.protobuf.message_factory
+directly instead of this class.
+"""
+
+__author__ = 'matthewtoia@google.com (Matt Toia)'
+
+import sys
+
+from google.protobuf import descriptor
+from google.protobuf import descriptor_database
+from google.protobuf import text_encoding
+
+
+_USE_C_DESCRIPTORS = descriptor._USE_C_DESCRIPTORS
+
+
+def _NormalizeFullyQualifiedName(name):
+ """Remove leading period from fully-qualified type name.
+
+ Due to b/13860351 in descriptor_database.py, types in the root namespace are
+ generated with a leading period. This function removes that prefix.
+
+ Args:
+ name: A str, the fully-qualified symbol name.
+
+ Returns:
+ A str, the normalized fully-qualified symbol name.
+ """
+ return name.lstrip('.')
+
+
+class DescriptorPool(object):
+ """A collection of protobufs dynamically constructed by descriptor protos."""
+
+ def __init__(self, descriptor_db=None):
+ """Initializes a Pool of proto buffs.
+
+ The descriptor_db argument to the constructor is provided to allow
+ specialized file descriptor proto lookup code to be triggered on demand. An
+ example would be an implementation which will read and compile a file
+ specified in a call to FindFileByName() and not require the call to Add()
+ at all. Results from this database will be cached internally here as well.
+
+ Args:
+ descriptor_db: A secondary source of file descriptors.
+ """
+
+ self._internal_db = descriptor_database.DescriptorDatabase()
+ self._descriptor_db = descriptor_db
+ self._descriptors = {}
+ self._enum_descriptors = {}
+ self._file_descriptors = {}
+
+ def Add(self, file_desc_proto):
+ """Adds the FileDescriptorProto and its types to this pool.
+
+ Args:
+ file_desc_proto: The FileDescriptorProto to add.
+ """
+
+ self._internal_db.Add(file_desc_proto)
+
+ def AddDescriptor(self, desc):
+ """Adds a Descriptor to the pool, non-recursively.
+
+ If the Descriptor contains nested messages or enums, the caller must
+ explicitly register them. This method also registers the FileDescriptor
+ associated with the message.
+
+ Args:
+ desc: A Descriptor.
+ """
+ if not isinstance(desc, descriptor.Descriptor):
+ raise TypeError('Expected instance of descriptor.Descriptor.')
+
+ self._descriptors[desc.full_name] = desc
+ self.AddFileDescriptor(desc.file)
+
+ def AddEnumDescriptor(self, enum_desc):
+ """Adds an EnumDescriptor to the pool.
+
+ This method also registers the FileDescriptor associated with the message.
+
+ Args:
+ enum_desc: An EnumDescriptor.
+ """
+
+ if not isinstance(enum_desc, descriptor.EnumDescriptor):
+ raise TypeError('Expected instance of descriptor.EnumDescriptor.')
+
+ self._enum_descriptors[enum_desc.full_name] = enum_desc
+ self.AddFileDescriptor(enum_desc.file)
+
+ def AddFileDescriptor(self, file_desc):
+ """Adds a FileDescriptor to the pool, non-recursively.
+
+ If the FileDescriptor contains messages or enums, the caller must explicitly
+ register them.
+
+ Args:
+ file_desc: A FileDescriptor.
+ """
+
+ if not isinstance(file_desc, descriptor.FileDescriptor):
+ raise TypeError('Expected instance of descriptor.FileDescriptor.')
+ self._file_descriptors[file_desc.name] = file_desc
+
+ def FindFileByName(self, file_name):
+ """Gets a FileDescriptor by file name.
+
+ Args:
+ file_name: The path to the file to get a descriptor for.
+
+ Returns:
+ A FileDescriptor for the named file.
+
+ Raises:
+ KeyError: if the file can not be found in the pool.
+ """
+
+ try:
+ return self._file_descriptors[file_name]
+ except KeyError:
+ pass
+
+ try:
+ file_proto = self._internal_db.FindFileByName(file_name)
+ except KeyError:
+ _, error, _ = sys.exc_info() #PY25 compatible for GAE.
+ if self._descriptor_db:
+ file_proto = self._descriptor_db.FindFileByName(file_name)
+ else:
+ raise error
+ if not file_proto:
+ raise KeyError('Cannot find a file named %s' % file_name)
+ return self._ConvertFileProtoToFileDescriptor(file_proto)
+
+ def FindFileContainingSymbol(self, symbol):
+ """Gets the FileDescriptor for the file containing the specified symbol.
+
+ Args:
+ symbol: The name of the symbol to search for.
+
+ Returns:
+ A FileDescriptor that contains the specified symbol.
+
+ Raises:
+ KeyError: if the file can not be found in the pool.
+ """
+
+ symbol = _NormalizeFullyQualifiedName(symbol)
+ try:
+ return self._descriptors[symbol].file
+ except KeyError:
+ pass
+
+ try:
+ return self._enum_descriptors[symbol].file
+ except KeyError:
+ pass
+
+ try:
+ file_proto = self._internal_db.FindFileContainingSymbol(symbol)
+ except KeyError:
+ _, error, _ = sys.exc_info() #PY25 compatible for GAE.
+ if self._descriptor_db:
+ file_proto = self._descriptor_db.FindFileContainingSymbol(symbol)
+ else:
+ raise error
+ if not file_proto:
+ raise KeyError('Cannot find a file containing %s' % symbol)
+ return self._ConvertFileProtoToFileDescriptor(file_proto)
+
+ def FindMessageTypeByName(self, full_name):
+ """Loads the named descriptor from the pool.
+
+ Args:
+ full_name: The full name of the descriptor to load.
+
+ Returns:
+ The descriptor for the named type.
+ """
+
+ full_name = _NormalizeFullyQualifiedName(full_name)
+ if full_name not in self._descriptors:
+ self.FindFileContainingSymbol(full_name)
+ return self._descriptors[full_name]
+
+ def FindEnumTypeByName(self, full_name):
+ """Loads the named enum descriptor from the pool.
+
+ Args:
+ full_name: The full name of the enum descriptor to load.
+
+ Returns:
+ The enum descriptor for the named type.
+ """
+
+ full_name = _NormalizeFullyQualifiedName(full_name)
+ if full_name not in self._enum_descriptors:
+ self.FindFileContainingSymbol(full_name)
+ return self._enum_descriptors[full_name]
+
+ def _ConvertFileProtoToFileDescriptor(self, file_proto):
+ """Creates a FileDescriptor from a proto or returns a cached copy.
+
+ This method also has the side effect of loading all the symbols found in
+ the file into the appropriate dictionaries in the pool.
+
+ Args:
+ file_proto: The proto to convert.
+
+ Returns:
+ A FileDescriptor matching the passed in proto.
+ """
+
+ if file_proto.name not in self._file_descriptors:
+ built_deps = list(self._GetDeps(file_proto.dependency))
+ direct_deps = [self.FindFileByName(n) for n in file_proto.dependency]
+
+ file_descriptor = descriptor.FileDescriptor(
+ name=file_proto.name,
+ package=file_proto.package,
+ syntax=file_proto.syntax,
+ options=file_proto.options,
+ serialized_pb=file_proto.SerializeToString(),
+ dependencies=direct_deps)
+ if _USE_C_DESCRIPTORS:
+ # When using C++ descriptors, all objects defined in the file were added
+ # to the C++ database when the FileDescriptor was built above.
+ # Just add them to this descriptor pool.
+ def _AddMessageDescriptor(message_desc):
+ self._descriptors[message_desc.full_name] = message_desc
+ for nested in message_desc.nested_types:
+ _AddMessageDescriptor(nested)
+ for enum_type in message_desc.enum_types:
+ _AddEnumDescriptor(enum_type)
+ def _AddEnumDescriptor(enum_desc):
+ self._enum_descriptors[enum_desc.full_name] = enum_desc
+ for message_type in file_descriptor.message_types_by_name.values():
+ _AddMessageDescriptor(message_type)
+ for enum_type in file_descriptor.enum_types_by_name.values():
+ _AddEnumDescriptor(enum_type)
+ else:
+ scope = {}
+
+ # This loop extracts all the message and enum types from all the
+ # dependencies of the file_proto. This is necessary to create the
+ # scope of available message types when defining the passed in
+ # file proto.
+ for dependency in built_deps:
+ scope.update(self._ExtractSymbols(
+ dependency.message_types_by_name.values()))
+ scope.update((_PrefixWithDot(enum.full_name), enum)
+ for enum in dependency.enum_types_by_name.values())
+
+ for message_type in file_proto.message_type:
+ message_desc = self._ConvertMessageDescriptor(
+ message_type, file_proto.package, file_descriptor, scope,
+ file_proto.syntax)
+ file_descriptor.message_types_by_name[message_desc.name] = (
+ message_desc)
+
+ for enum_type in file_proto.enum_type:
+ file_descriptor.enum_types_by_name[enum_type.name] = (
+ self._ConvertEnumDescriptor(enum_type, file_proto.package,
+ file_descriptor, None, scope))
+
+ for index, extension_proto in enumerate(file_proto.extension):
+ extension_desc = self.MakeFieldDescriptor(
+ extension_proto, file_proto.package, index, is_extension=True)
+ extension_desc.containing_type = self._GetTypeFromScope(
+ file_descriptor.package, extension_proto.extendee, scope)
+ self.SetFieldType(extension_proto, extension_desc,
+ file_descriptor.package, scope)
+ file_descriptor.extensions_by_name[extension_desc.name] = (
+ extension_desc)
+
+ for desc_proto in file_proto.message_type:
+ self.SetAllFieldTypes(file_proto.package, desc_proto, scope)
+
+ if file_proto.package:
+ desc_proto_prefix = _PrefixWithDot(file_proto.package)
+ else:
+ desc_proto_prefix = ''
+
+ for desc_proto in file_proto.message_type:
+ desc = self._GetTypeFromScope(
+ desc_proto_prefix, desc_proto.name, scope)
+ file_descriptor.message_types_by_name[desc_proto.name] = desc
+
+ self.Add(file_proto)
+ self._file_descriptors[file_proto.name] = file_descriptor
+
+ return self._file_descriptors[file_proto.name]
+
+ def _ConvertMessageDescriptor(self, desc_proto, package=None, file_desc=None,
+ scope=None, syntax=None):
+ """Adds the proto to the pool in the specified package.
+
+ Args:
+ desc_proto: The descriptor_pb2.DescriptorProto protobuf message.
+ package: The package the proto should be located in.
+ file_desc: The file containing this message.
+ scope: Dict mapping short and full symbols to message and enum types.
+
+ Returns:
+ The added descriptor.
+ """
+
+ if package:
+ desc_name = '.'.join((package, desc_proto.name))
+ else:
+ desc_name = desc_proto.name
+
+ if file_desc is None:
+ file_name = None
+ else:
+ file_name = file_desc.name
+
+ if scope is None:
+ scope = {}
+
+ nested = [
+ self._ConvertMessageDescriptor(
+ nested, desc_name, file_desc, scope, syntax)
+ for nested in desc_proto.nested_type]
+ enums = [
+ self._ConvertEnumDescriptor(enum, desc_name, file_desc, None, scope)
+ for enum in desc_proto.enum_type]
+ fields = [self.MakeFieldDescriptor(field, desc_name, index)
+ for index, field in enumerate(desc_proto.field)]
+ extensions = [
+ self.MakeFieldDescriptor(extension, desc_name, index, is_extension=True)
+ for index, extension in enumerate(desc_proto.extension)]
+ oneofs = [
+ descriptor.OneofDescriptor(desc.name, '.'.join((desc_name, desc.name)),
+ index, None, [])
+ for index, desc in enumerate(desc_proto.oneof_decl)]
+ extension_ranges = [(r.start, r.end) for r in desc_proto.extension_range]
+ if extension_ranges:
+ is_extendable = True
+ else:
+ is_extendable = False
+ desc = descriptor.Descriptor(
+ name=desc_proto.name,
+ full_name=desc_name,
+ filename=file_name,
+ containing_type=None,
+ fields=fields,
+ oneofs=oneofs,
+ nested_types=nested,
+ enum_types=enums,
+ extensions=extensions,
+ options=desc_proto.options,
+ is_extendable=is_extendable,
+ extension_ranges=extension_ranges,
+ file=file_desc,
+ serialized_start=None,
+ serialized_end=None,
+ syntax=syntax)
+ for nested in desc.nested_types:
+ nested.containing_type = desc
+ for enum in desc.enum_types:
+ enum.containing_type = desc
+ for field_index, field_desc in enumerate(desc_proto.field):
+ if field_desc.HasField('oneof_index'):
+ oneof_index = field_desc.oneof_index
+ oneofs[oneof_index].fields.append(fields[field_index])
+ fields[field_index].containing_oneof = oneofs[oneof_index]
+
+ scope[_PrefixWithDot(desc_name)] = desc
+ self._descriptors[desc_name] = desc
+ return desc
+
+ def _ConvertEnumDescriptor(self, enum_proto, package=None, file_desc=None,
+ containing_type=None, scope=None):
+ """Make a protobuf EnumDescriptor given an EnumDescriptorProto protobuf.
+
+ Args:
+ enum_proto: The descriptor_pb2.EnumDescriptorProto protobuf message.
+ package: Optional package name for the new message EnumDescriptor.
+ file_desc: The file containing the enum descriptor.
+ containing_type: The type containing this enum.
+ scope: Scope containing available types.
+
+ Returns:
+ The added descriptor
+ """
+
+ if package:
+ enum_name = '.'.join((package, enum_proto.name))
+ else:
+ enum_name = enum_proto.name
+
+ if file_desc is None:
+ file_name = None
+ else:
+ file_name = file_desc.name
+
+ values = [self._MakeEnumValueDescriptor(value, index)
+ for index, value in enumerate(enum_proto.value)]
+ desc = descriptor.EnumDescriptor(name=enum_proto.name,
+ full_name=enum_name,
+ filename=file_name,
+ file=file_desc,
+ values=values,
+ containing_type=containing_type,
+ options=enum_proto.options)
+ scope['.%s' % enum_name] = desc
+ self._enum_descriptors[enum_name] = desc
+ return desc
+
+ def MakeFieldDescriptor(self, field_proto, message_name, index,
+ is_extension=False):
+ """Creates a field descriptor from a FieldDescriptorProto.
+
+ For message and enum type fields, this method will do a look up
+ in the pool for the appropriate descriptor for that type. If it
+ is unavailable, it will fall back to the _source function to
+ create it. If this type is still unavailable, construction will
+ fail.
+
+ Args:
+ field_proto: The proto describing the field.
+ message_name: The name of the containing message.
+ index: Index of the field
+ is_extension: Indication that this field is for an extension.
+
+ Returns:
+ An initialized FieldDescriptor object
+ """
+
+ if message_name:
+ full_name = '.'.join((message_name, field_proto.name))
+ else:
+ full_name = field_proto.name
+
+ return descriptor.FieldDescriptor(
+ name=field_proto.name,
+ full_name=full_name,
+ index=index,
+ number=field_proto.number,
+ type=field_proto.type,
+ cpp_type=None,
+ message_type=None,
+ enum_type=None,
+ containing_type=None,
+ label=field_proto.label,
+ has_default_value=False,
+ default_value=None,
+ is_extension=is_extension,
+ extension_scope=None,
+ options=field_proto.options)
+
+ def SetAllFieldTypes(self, package, desc_proto, scope):
+ """Sets all the descriptor's fields's types.
+
+ This method also sets the containing types on any extensions.
+
+ Args:
+ package: The current package of desc_proto.
+ desc_proto: The message descriptor to update.
+ scope: Enclosing scope of available types.
+ """
+
+ package = _PrefixWithDot(package)
+
+ main_desc = self._GetTypeFromScope(package, desc_proto.name, scope)
+
+ if package == '.':
+ nested_package = _PrefixWithDot(desc_proto.name)
+ else:
+ nested_package = '.'.join([package, desc_proto.name])
+
+ for field_proto, field_desc in zip(desc_proto.field, main_desc.fields):
+ self.SetFieldType(field_proto, field_desc, nested_package, scope)
+
+ for extension_proto, extension_desc in (
+ zip(desc_proto.extension, main_desc.extensions)):
+ extension_desc.containing_type = self._GetTypeFromScope(
+ nested_package, extension_proto.extendee, scope)
+ self.SetFieldType(extension_proto, extension_desc, nested_package, scope)
+
+ for nested_type in desc_proto.nested_type:
+ self.SetAllFieldTypes(nested_package, nested_type, scope)
+
+ def SetFieldType(self, field_proto, field_desc, package, scope):
+ """Sets the field's type, cpp_type, message_type and enum_type.
+
+ Args:
+ field_proto: Data about the field in proto format.
+ field_desc: The descriptor to modiy.
+ package: The package the field's container is in.
+ scope: Enclosing scope of available types.
+ """
+ if field_proto.type_name:
+ desc = self._GetTypeFromScope(package, field_proto.type_name, scope)
+ else:
+ desc = None
+
+ if not field_proto.HasField('type'):
+ if isinstance(desc, descriptor.Descriptor):
+ field_proto.type = descriptor.FieldDescriptor.TYPE_MESSAGE
+ else:
+ field_proto.type = descriptor.FieldDescriptor.TYPE_ENUM
+
+ field_desc.cpp_type = descriptor.FieldDescriptor.ProtoTypeToCppProtoType(
+ field_proto.type)
+
+ if (field_proto.type == descriptor.FieldDescriptor.TYPE_MESSAGE
+ or field_proto.type == descriptor.FieldDescriptor.TYPE_GROUP):
+ field_desc.message_type = desc
+
+ if field_proto.type == descriptor.FieldDescriptor.TYPE_ENUM:
+ field_desc.enum_type = desc
+
+ if field_proto.label == descriptor.FieldDescriptor.LABEL_REPEATED:
+ field_desc.has_default_value = False
+ field_desc.default_value = []
+ elif field_proto.HasField('default_value'):
+ field_desc.has_default_value = True
+ if (field_proto.type == descriptor.FieldDescriptor.TYPE_DOUBLE or
+ field_proto.type == descriptor.FieldDescriptor.TYPE_FLOAT):
+ field_desc.default_value = float(field_proto.default_value)
+ elif field_proto.type == descriptor.FieldDescriptor.TYPE_STRING:
+ field_desc.default_value = field_proto.default_value
+ elif field_proto.type == descriptor.FieldDescriptor.TYPE_BOOL:
+ field_desc.default_value = field_proto.default_value.lower() == 'true'
+ elif field_proto.type == descriptor.FieldDescriptor.TYPE_ENUM:
+ field_desc.default_value = field_desc.enum_type.values_by_name[
+ field_proto.default_value].number
+ elif field_proto.type == descriptor.FieldDescriptor.TYPE_BYTES:
+ field_desc.default_value = text_encoding.CUnescape(
+ field_proto.default_value)
+ else:
+ field_desc.default_value = int(field_proto.default_value)
+ else:
+ field_desc.has_default_value = False
+ field_desc.default_value = None
+
+ field_desc.type = field_proto.type
+
+ def _MakeEnumValueDescriptor(self, value_proto, index):
+ """Creates a enum value descriptor object from a enum value proto.
+
+ Args:
+ value_proto: The proto describing the enum value.
+ index: The index of the enum value.
+
+ Returns:
+ An initialized EnumValueDescriptor object.
+ """
+
+ return descriptor.EnumValueDescriptor(
+ name=value_proto.name,
+ index=index,
+ number=value_proto.number,
+ options=value_proto.options,
+ type=None)
+
+ def _ExtractSymbols(self, descriptors):
+ """Pulls out all the symbols from descriptor protos.
+
+ Args:
+ descriptors: The messages to extract descriptors from.
+ Yields:
+ A two element tuple of the type name and descriptor object.
+ """
+
+ for desc in descriptors:
+ yield (_PrefixWithDot(desc.full_name), desc)
+ for symbol in self._ExtractSymbols(desc.nested_types):
+ yield symbol
+ for enum in desc.enum_types:
+ yield (_PrefixWithDot(enum.full_name), enum)
+
+ def _GetDeps(self, dependencies):
+ """Recursively finds dependencies for file protos.
+
+ Args:
+ dependencies: The names of the files being depended on.
+
+ Yields:
+ Each direct and indirect dependency.
+ """
+
+ for dependency in dependencies:
+ dep_desc = self.FindFileByName(dependency)
+ yield dep_desc
+ for parent_dep in dep_desc.dependencies:
+ yield parent_dep
+
+ def _GetTypeFromScope(self, package, type_name, scope):
+ """Finds a given type name in the current scope.
+
+ Args:
+ package: The package the proto should be located in.
+ type_name: The name of the type to be found in the scope.
+ scope: Dict mapping short and full symbols to message and enum types.
+
+ Returns:
+ The descriptor for the requested type.
+ """
+ if type_name not in scope:
+ components = _PrefixWithDot(package).split('.')
+ while components:
+ possible_match = '.'.join(components + [type_name])
+ if possible_match in scope:
+ type_name = possible_match
+ break
+ else:
+ components.pop(-1)
+ return scope[type_name]
+
+
+def _PrefixWithDot(name):
+ return name if name.startswith('.') else '.%s' % name
diff --git a/python/google/protobuf/internal/_parameterized.py b/python/google/protobuf/internal/_parameterized.py
new file mode 100755
index 00000000..6ed23308
--- /dev/null
+++ b/python/google/protobuf/internal/_parameterized.py
@@ -0,0 +1,438 @@
+#! /usr/bin/python
+#
+# 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.
+
+"""Adds support for parameterized tests to Python's unittest TestCase class.
+
+A parameterized test is a method in a test case that is invoked with different
+argument tuples.
+
+A simple example:
+
+ class AdditionExample(parameterized.ParameterizedTestCase):
+ @parameterized.Parameters(
+ (1, 2, 3),
+ (4, 5, 9),
+ (1, 1, 3))
+ def testAddition(self, op1, op2, result):
+ self.assertEquals(result, op1 + op2)
+
+
+Each invocation is a separate test case and properly isolated just
+like a normal test method, with its own setUp/tearDown cycle. In the
+example above, there are three separate testcases, one of which will
+fail due to an assertion error (1 + 1 != 3).
+
+Parameters for invididual test cases can be tuples (with positional parameters)
+or dictionaries (with named parameters):
+
+ class AdditionExample(parameterized.ParameterizedTestCase):
+ @parameterized.Parameters(
+ {'op1': 1, 'op2': 2, 'result': 3},
+ {'op1': 4, 'op2': 5, 'result': 9},
+ )
+ def testAddition(self, op1, op2, result):
+ self.assertEquals(result, op1 + op2)
+
+If a parameterized test fails, the error message will show the
+original test name (which is modified internally) and the arguments
+for the specific invocation, which are part of the string returned by
+the shortDescription() method on test cases.
+
+The id method of the test, used internally by the unittest framework,
+is also modified to show the arguments. To make sure that test names
+stay the same across several invocations, object representations like
+
+ >>> class Foo(object):
+ ... pass
+ >>> repr(Foo())
+ '<__main__.Foo object at 0x23d8610>'
+
+are turned into '<__main__.Foo>'. For even more descriptive names,
+especially in test logs, you can use the NamedParameters decorator. In
+this case, only tuples are supported, and the first parameters has to
+be a string (or an object that returns an apt name when converted via
+str()):
+
+ class NamedExample(parameterized.ParameterizedTestCase):
+ @parameterized.NamedParameters(
+ ('Normal', 'aa', 'aaa', True),
+ ('EmptyPrefix', '', 'abc', True),
+ ('BothEmpty', '', '', True))
+ def testStartsWith(self, prefix, string, result):
+ self.assertEquals(result, strings.startswith(prefix))
+
+Named tests also have the benefit that they can be run individually
+from the command line:
+
+ $ testmodule.py NamedExample.testStartsWithNormal
+ .
+ --------------------------------------------------------------------
+ Ran 1 test in 0.000s
+
+ OK
+
+Parameterized Classes
+=====================
+If invocation arguments are shared across test methods in a single
+ParameterizedTestCase class, instead of decorating all test methods
+individually, the class itself can be decorated:
+
+ @parameterized.Parameters(
+ (1, 2, 3)
+ (4, 5, 9))
+ class ArithmeticTest(parameterized.ParameterizedTestCase):
+ def testAdd(self, arg1, arg2, result):
+ self.assertEqual(arg1 + arg2, result)
+
+ def testSubtract(self, arg2, arg2, result):
+ self.assertEqual(result - arg1, arg2)
+
+Inputs from Iterables
+=====================
+If parameters should be shared across several test cases, or are dynamically
+created from other sources, a single non-tuple iterable can be passed into
+the decorator. This iterable will be used to obtain the test cases:
+
+ class AdditionExample(parameterized.ParameterizedTestCase):
+ @parameterized.Parameters(
+ c.op1, c.op2, c.result for c in testcases
+ )
+ def testAddition(self, op1, op2, result):
+ self.assertEquals(result, op1 + op2)
+
+
+Single-Argument Test Methods
+============================
+If a test method takes only one argument, the single argument does not need to
+be wrapped into a tuple:
+
+ class NegativeNumberExample(parameterized.ParameterizedTestCase):
+ @parameterized.Parameters(
+ -1, -3, -4, -5
+ )
+ def testIsNegative(self, arg):
+ self.assertTrue(IsNegative(arg))
+"""
+
+__author__ = 'tmarek@google.com (Torsten Marek)'
+
+import collections
+import functools
+import re
+import types
+import unittest
+import uuid
+
+from google.apputils import basetest
+
+ADDR_RE = re.compile(r'\<([a-zA-Z0-9_\-\.]+) object at 0x[a-fA-F0-9]+\>')
+_SEPARATOR = uuid.uuid1().hex
+_FIRST_ARG = object()
+_ARGUMENT_REPR = object()
+
+
+def _CleanRepr(obj):
+ return ADDR_RE.sub(r'<\1>', repr(obj))
+
+
+# Helper function formerly from the unittest module, removed from it in
+# Python 2.7.
+def _StrClass(cls):
+ return '%s.%s' % (cls.__module__, cls.__name__)
+
+
+def _NonStringIterable(obj):
+ return (isinstance(obj, collections.Iterable) and not
+ isinstance(obj, basestring))
+
+
+def _FormatParameterList(testcase_params):
+ if isinstance(testcase_params, collections.Mapping):
+ return ', '.join('%s=%s' % (argname, _CleanRepr(value))
+ for argname, value in testcase_params.iteritems())
+ elif _NonStringIterable(testcase_params):
+ return ', '.join(map(_CleanRepr, testcase_params))
+ else:
+ return _FormatParameterList((testcase_params,))
+
+
+class _ParameterizedTestIter(object):
+ """Callable and iterable class for producing new test cases."""
+
+ def __init__(self, test_method, testcases, naming_type):
+ """Returns concrete test functions for a test and a list of parameters.
+
+ The naming_type is used to determine the name of the concrete
+ functions as reported by the unittest framework. If naming_type is
+ _FIRST_ARG, the testcases must be tuples, and the first element must
+ have a string representation that is a valid Python identifier.
+
+ Args:
+ test_method: The decorated test method.
+ testcases: (list of tuple/dict) A list of parameter
+ tuples/dicts for individual test invocations.
+ naming_type: The test naming type, either _NAMED or _ARGUMENT_REPR.
+ """
+ self._test_method = test_method
+ self.testcases = testcases
+ self._naming_type = naming_type
+
+ def __call__(self, *args, **kwargs):
+ raise RuntimeError('You appear to be running a parameterized test case '
+ 'without having inherited from parameterized.'
+ 'ParameterizedTestCase. This is bad because none of '
+ 'your test cases are actually being run.')
+
+ def __iter__(self):
+ test_method = self._test_method
+ naming_type = self._naming_type
+
+ def MakeBoundParamTest(testcase_params):
+ @functools.wraps(test_method)
+ def BoundParamTest(self):
+ if isinstance(testcase_params, collections.Mapping):
+ test_method(self, **testcase_params)
+ elif _NonStringIterable(testcase_params):
+ test_method(self, *testcase_params)
+ else:
+ test_method(self, testcase_params)
+
+ if naming_type is _FIRST_ARG:
+ # Signal the metaclass that the name of the test function is unique
+ # and descriptive.
+ BoundParamTest.__x_use_name__ = True
+ BoundParamTest.__name__ += str(testcase_params[0])
+ testcase_params = testcase_params[1:]
+ elif naming_type is _ARGUMENT_REPR:
+ # __x_extra_id__ is used to pass naming information to the __new__
+ # method of TestGeneratorMetaclass.
+ # The metaclass will make sure to create a unique, but nondescriptive
+ # name for this test.
+ BoundParamTest.__x_extra_id__ = '(%s)' % (
+ _FormatParameterList(testcase_params),)
+ else:
+ raise RuntimeError('%s is not a valid naming type.' % (naming_type,))
+
+ BoundParamTest.__doc__ = '%s(%s)' % (
+ BoundParamTest.__name__, _FormatParameterList(testcase_params))
+ if test_method.__doc__:
+ BoundParamTest.__doc__ += '\n%s' % (test_method.__doc__,)
+ return BoundParamTest
+ return (MakeBoundParamTest(c) for c in self.testcases)
+
+
+def _IsSingletonList(testcases):
+ """True iff testcases contains only a single non-tuple element."""
+ return len(testcases) == 1 and not isinstance(testcases[0], tuple)
+
+
+def _ModifyClass(class_object, testcases, naming_type):
+ assert not getattr(class_object, '_id_suffix', None), (
+ 'Cannot add parameters to %s,'
+ ' which already has parameterized methods.' % (class_object,))
+ class_object._id_suffix = id_suffix = {}
+ for name, obj in class_object.__dict__.items():
+ if (name.startswith(unittest.TestLoader.testMethodPrefix)
+ and isinstance(obj, types.FunctionType)):
+ delattr(class_object, name)
+ methods = {}
+ _UpdateClassDictForParamTestCase(
+ methods, id_suffix, name,
+ _ParameterizedTestIter(obj, testcases, naming_type))
+ for name, meth in methods.iteritems():
+ setattr(class_object, name, meth)
+
+
+def _ParameterDecorator(naming_type, testcases):
+ """Implementation of the parameterization decorators.
+
+ Args:
+ naming_type: The naming type.
+ testcases: Testcase parameters.
+
+ Returns:
+ A function for modifying the decorated object.
+ """
+ def _Apply(obj):
+ if isinstance(obj, type):
+ _ModifyClass(
+ obj,
+ list(testcases) if not isinstance(testcases, collections.Sequence)
+ else testcases,
+ naming_type)
+ return obj
+ else:
+ return _ParameterizedTestIter(obj, testcases, naming_type)
+
+ if _IsSingletonList(testcases):
+ assert _NonStringIterable(testcases[0]), (
+ 'Single parameter argument must be a non-string iterable')
+ testcases = testcases[0]
+
+ return _Apply
+
+
+def Parameters(*testcases):
+ """A decorator for creating parameterized tests.
+
+ See the module docstring for a usage example.
+ Args:
+ *testcases: Parameters for the decorated method, either a single
+ iterable, or a list of tuples/dicts/objects (for tests
+ with only one argument).
+
+ Returns:
+ A test generator to be handled by TestGeneratorMetaclass.
+ """
+ return _ParameterDecorator(_ARGUMENT_REPR, testcases)
+
+
+def NamedParameters(*testcases):
+ """A decorator for creating parameterized tests.
+
+ See the module docstring for a usage example. The first element of
+ each parameter tuple should be a string and will be appended to the
+ name of the test method.
+
+ Args:
+ *testcases: Parameters for the decorated method, either a single
+ iterable, or a list of tuples.
+
+ Returns:
+ A test generator to be handled by TestGeneratorMetaclass.
+ """
+ return _ParameterDecorator(_FIRST_ARG, testcases)
+
+
+class TestGeneratorMetaclass(type):
+ """Metaclass for test cases with test generators.
+
+ A test generator is an iterable in a testcase that produces callables. These
+ callables must be single-argument methods. These methods are injected into
+ the class namespace and the original iterable is removed. If the name of the
+ iterable conforms to the test pattern, the injected methods will be picked
+ up as tests by the unittest framework.
+
+ In general, it is supposed to be used in conjuction with the
+ Parameters decorator.
+ """
+
+ def __new__(mcs, class_name, bases, dct):
+ dct['_id_suffix'] = id_suffix = {}
+ for name, obj in dct.items():
+ if (name.startswith(unittest.TestLoader.testMethodPrefix) and
+ _NonStringIterable(obj)):
+ iterator = iter(obj)
+ dct.pop(name)
+ _UpdateClassDictForParamTestCase(dct, id_suffix, name, iterator)
+
+ return type.__new__(mcs, class_name, bases, dct)
+
+
+def _UpdateClassDictForParamTestCase(dct, id_suffix, name, iterator):
+ """Adds individual test cases to a dictionary.
+
+ Args:
+ dct: The target dictionary.
+ id_suffix: The dictionary for mapping names to test IDs.
+ name: The original name of the test case.
+ iterator: The iterator generating the individual test cases.
+ """
+ for idx, func in enumerate(iterator):
+ assert callable(func), 'Test generators must yield callables, got %r' % (
+ func,)
+ if getattr(func, '__x_use_name__', False):
+ new_name = func.__name__
+ else:
+ new_name = '%s%s%d' % (name, _SEPARATOR, idx)
+ assert new_name not in dct, (
+ 'Name of parameterized test case "%s" not unique' % (new_name,))
+ dct[new_name] = func
+ id_suffix[new_name] = getattr(func, '__x_extra_id__', '')
+
+
+class ParameterizedTestCase(basetest.TestCase):
+ """Base class for test cases using the Parameters decorator."""
+ __metaclass__ = TestGeneratorMetaclass
+
+ def _OriginalName(self):
+ return self._testMethodName.split(_SEPARATOR)[0]
+
+ def __str__(self):
+ return '%s (%s)' % (self._OriginalName(), _StrClass(self.__class__))
+
+ def id(self): # pylint: disable=invalid-name
+ """Returns the descriptive ID of the test.
+
+ This is used internally by the unittesting framework to get a name
+ for the test to be used in reports.
+
+ Returns:
+ The test id.
+ """
+ return '%s.%s%s' % (_StrClass(self.__class__),
+ self._OriginalName(),
+ self._id_suffix.get(self._testMethodName, ''))
+
+
+def CoopParameterizedTestCase(other_base_class):
+ """Returns a new base class with a cooperative metaclass base.
+
+ This enables the ParameterizedTestCase to be used in combination
+ with other base classes that have custom metaclasses, such as
+ mox.MoxTestBase.
+
+ Only works with metaclasses that do not override type.__new__.
+
+ Example:
+
+ import google3
+ import mox
+
+ from google3.testing.pybase import parameterized
+
+ class ExampleTest(parameterized.CoopParameterizedTestCase(mox.MoxTestBase)):
+ ...
+
+ Args:
+ other_base_class: (class) A test case base class.
+
+ Returns:
+ A new class object.
+ """
+ metaclass = type(
+ 'CoopMetaclass',
+ (other_base_class.__metaclass__,
+ TestGeneratorMetaclass), {})
+ return metaclass(
+ 'CoopParameterizedTestCase',
+ (other_base_class, ParameterizedTestCase), {})
diff --git a/python/google/protobuf/internal/api_implementation.cc b/python/google/protobuf/internal/api_implementation.cc
new file mode 100644
index 00000000..6db12e8d
--- /dev/null
+++ b/python/google/protobuf/internal/api_implementation.cc
@@ -0,0 +1,129 @@
+// 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 <Python.h>
+
+namespace google {
+namespace protobuf {
+namespace python {
+
+// Version constant.
+// This is either 0 for python, 1 for CPP V1, 2 for CPP V2.
+//
+// 0 is default and is equivalent to
+// PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION=python
+//
+// 1 is set with -DPYTHON_PROTO2_CPP_IMPL_V1 and is equivalent to
+// PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION=cpp
+// and
+// PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION_VERSION=1
+//
+// 2 is set with -DPYTHON_PROTO2_CPP_IMPL_V2 and is equivalent to
+// PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION=cpp
+// and
+// PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION_VERSION=2
+#ifdef PYTHON_PROTO2_CPP_IMPL_V1
+#error "PYTHON_PROTO2_CPP_IMPL_V1 is no longer supported."
+#else
+#ifdef PYTHON_PROTO2_CPP_IMPL_V2
+static int kImplVersion = 2;
+#else
+#ifdef PYTHON_PROTO2_PYTHON_IMPL
+static int kImplVersion = 0;
+#else
+
+static int kImplVersion = -1; // -1 means "Unspecified by compiler flags".
+
+#endif // PYTHON_PROTO2_PYTHON_IMPL
+#endif // PYTHON_PROTO2_CPP_IMPL_V2
+#endif // PYTHON_PROTO2_CPP_IMPL_V1
+
+static const char* kImplVersionName = "api_version";
+
+static const char* kModuleName = "_api_implementation";
+static const char kModuleDocstring[] =
+"_api_implementation is a module that exposes compile-time constants that\n"
+"determine the default API implementation to use for Python proto2.\n"
+"\n"
+"It complements api_implementation.py by setting defaults using compile-time\n"
+"constants defined in C, such that one can set defaults at compilation\n"
+"(e.g. with blaze flag --copt=-DPYTHON_PROTO2_CPP_IMPL_V2).";
+
+#if PY_MAJOR_VERSION >= 3
+static struct PyModuleDef _module = {
+ PyModuleDef_HEAD_INIT,
+ kModuleName,
+ kModuleDocstring,
+ -1,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+#define INITFUNC PyInit__api_implementation
+#define INITFUNC_ERRORVAL NULL
+#else
+#define INITFUNC init_api_implementation
+#define INITFUNC_ERRORVAL
+#endif
+
+extern "C" {
+ PyMODINIT_FUNC INITFUNC() {
+#if PY_MAJOR_VERSION >= 3
+ PyObject *module = PyModule_Create(&_module);
+#else
+ PyObject *module = Py_InitModule3(
+ const_cast<char*>(kModuleName),
+ NULL,
+ const_cast<char*>(kModuleDocstring));
+#endif
+ if (module == NULL) {
+ return INITFUNC_ERRORVAL;
+ }
+
+ // Adds the module variable "api_version".
+ if (PyModule_AddIntConstant(
+ module,
+ const_cast<char*>(kImplVersionName),
+ kImplVersion))
+#if PY_MAJOR_VERSION < 3
+ return;
+#else
+ { Py_DECREF(module); return NULL; }
+
+ return module;
+#endif
+ }
+}
+
+} // namespace python
+} // namespace protobuf
+} // namespace google
diff --git a/python/google/protobuf/internal/api_implementation.py b/python/google/protobuf/internal/api_implementation.py
index b3e412e2..8ba4357c 100755
--- a/python/google/protobuf/internal/api_implementation.py
+++ b/python/google/protobuf/internal/api_implementation.py
@@ -1,6 +1,6 @@
# Protocol Buffers - Google's data interchange format
# Copyright 2008 Google Inc. All rights reserved.
-# http://code.google.com/p/protobuf/
+# 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
@@ -28,32 +28,70 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+"""Determine which implementation of the protobuf API is used in this process.
"""
-This module is the central entity that determines which implementation of the
-API is used.
-"""
-
-__author__ = 'petar@google.com (Petar Petrov)'
import os
+import sys
+
+try:
+ # pylint: disable=g-import-not-at-top
+ from google.protobuf.internal import _api_implementation
+ # The compile-time constants in the _api_implementation module can be used to
+ # switch to a certain implementation of the Python API at build time.
+ _api_version = _api_implementation.api_version
+ _proto_extension_modules_exist_in_build = True
+except ImportError:
+ _api_version = -1 # Unspecified by compiler flags.
+ _proto_extension_modules_exist_in_build = False
+
+if _api_version == 1:
+ raise ValueError('api_version=1 is no longer supported.')
+if _api_version < 0: # Still unspecified?
+ try:
+ # The presence of this module in a build allows the proto implementation to
+ # be upgraded merely via build deps rather than a compiler flag or the
+ # runtime environment variable.
+ # pylint: disable=g-import-not-at-top
+ from google.protobuf import _use_fast_cpp_protos
+ # Work around a known issue in the classic bootstrap .par import hook.
+ if not _use_fast_cpp_protos:
+ raise ImportError('_use_fast_cpp_protos import succeeded but was None')
+ del _use_fast_cpp_protos
+ _api_version = 2
+ except ImportError:
+ if _proto_extension_modules_exist_in_build:
+ if sys.version_info[0] >= 3: # Python 3 defaults to C++ impl v2.
+ _api_version = 2
+ # TODO(b/17427486): Make Python 2 default to C++ impl v2.
+
+_default_implementation_type = (
+ 'python' if _api_version <= 0 else 'cpp')
+
# This environment variable can be used to switch to a certain implementation
-# of the Python API. Right now only 'python' and 'cpp' are valid values. Any
-# other value will be ignored.
+# of the Python API, overriding the compile-time constants in the
+# _api_implementation module. Right now only 'python' and 'cpp' are valid
+# values. Any other value will be ignored.
_implementation_type = os.getenv('PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION',
- 'python')
-
+ _default_implementation_type)
if _implementation_type != 'python':
- # For now, by default use the pure-Python implementation.
- # The code below checks if the C extension is available and
- # uses it if it is available.
_implementation_type = 'cpp'
- ## Determine automatically which implementation to use.
- #try:
- # from google.protobuf.internal import cpp_message
- # _implementation_type = 'cpp'
- #except ImportError, e:
- # _implementation_type = 'python'
+
+# This environment variable can be used to switch between the two
+# 'cpp' implementations, overriding the compile-time constants in the
+# _api_implementation module. Right now only 1 and 2 are valid values. Any other
+# value will be ignored.
+_implementation_version_str = os.getenv(
+ 'PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION_VERSION', '2')
+
+if _implementation_version_str != '2':
+ raise ValueError(
+ 'unsupported PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION_VERSION: "' +
+ _implementation_version_str + '" (supported versions: 2)'
+ )
+
+_implementation_version = int(_implementation_version_str)
# Usage of this function is discouraged. Clients shouldn't care which
@@ -62,3 +100,8 @@ if _implementation_type != 'python':
# Please don't use this function if possible.
def Type():
return _implementation_type
+
+
+# See comment on 'Type' above.
+def Version():
+ return _implementation_version
diff --git a/python/google/protobuf/internal/containers.py b/python/google/protobuf/internal/containers.py
index 097a3c26..d976f9e1 100755
--- a/python/google/protobuf/internal/containers.py
+++ b/python/google/protobuf/internal/containers.py
@@ -1,6 +1,6 @@
# Protocol Buffers - Google's data interchange format
# Copyright 2008 Google Inc. All rights reserved.
-# http://code.google.com/p/protobuf/
+# 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
@@ -41,7 +41,6 @@ are:
__author__ = 'petar@google.com (Petar Petrov)'
-
class BaseContainer(object):
"""Base container class."""
@@ -78,8 +77,13 @@ class BaseContainer(object):
def __repr__(self):
return repr(self._values)
- def sort(self, sort_function=cmp):
- self._values.sort(sort_function)
+ def sort(self, *args, **kwargs):
+ # Continue to support the old sort_function keyword argument.
+ # This is expected to be a rare occurrence, so use LBYL to avoid
+ # the overhead of actually catching KeyError.
+ if 'sort_function' in kwargs:
+ kwargs['cmp'] = kwargs.pop('sort_function')
+ self._values.sort(*args, **kwargs)
class RepeatedScalarFieldContainer(BaseContainer):
@@ -103,29 +107,34 @@ class RepeatedScalarFieldContainer(BaseContainer):
def append(self, value):
"""Appends an item to the list. Similar to list.append()."""
- self._type_checker.CheckValue(value)
- self._values.append(value)
+ self._values.append(self._type_checker.CheckValue(value))
if not self._message_listener.dirty:
self._message_listener.Modified()
def insert(self, key, value):
"""Inserts the item at the specified position. Similar to list.insert()."""
- self._type_checker.CheckValue(value)
- self._values.insert(key, value)
+ self._values.insert(key, self._type_checker.CheckValue(value))
if not self._message_listener.dirty:
self._message_listener.Modified()
def extend(self, elem_seq):
- """Extends by appending the given sequence. Similar to list.extend()."""
- if not elem_seq:
- return
+ """Extends by appending the given iterable. Similar to list.extend()."""
- new_values = []
- for elem in elem_seq:
- self._type_checker.CheckValue(elem)
- new_values.append(elem)
- self._values.extend(new_values)
- self._message_listener.Modified()
+ if elem_seq is None:
+ return
+ try:
+ elem_seq_iter = iter(elem_seq)
+ except TypeError:
+ if not elem_seq:
+ # silently ignore falsy inputs :-/.
+ # TODO(ptucker): Deprecate this behavior. b/18413862
+ return
+ raise
+
+ new_values = [self._type_checker.CheckValue(elem) for elem in elem_seq_iter]
+ if new_values:
+ self._values.extend(new_values)
+ self._message_listener.Modified()
def MergeFrom(self, other):
"""Appends the contents of another repeated field of the same type to this
@@ -139,11 +148,21 @@ class RepeatedScalarFieldContainer(BaseContainer):
self._values.remove(elem)
self._message_listener.Modified()
+ def pop(self, key=-1):
+ """Removes and returns an item at a given index. Similar to list.pop()."""
+ value = self._values[key]
+ self.__delitem__(key)
+ return value
+
def __setitem__(self, key, value):
"""Sets the item on the specified position."""
- self._type_checker.CheckValue(value)
- self._values[key] = value
- self._message_listener.Modified()
+ if isinstance(key, slice): # PY3
+ if key.step is not None:
+ raise ValueError('Extended slices not supported')
+ self.__setslice__(key.start, key.stop, value)
+ else:
+ self._values[key] = self._type_checker.CheckValue(value)
+ self._message_listener.Modified()
def __getslice__(self, start, stop):
"""Retrieves the subset of items from between the specified indices."""
@@ -153,8 +172,7 @@ class RepeatedScalarFieldContainer(BaseContainer):
"""Sets the subset of items from between the specified indices."""
new_values = []
for value in values:
- self._type_checker.CheckValue(value)
- new_values.append(value)
+ new_values.append(self._type_checker.CheckValue(value))
self._values[start:stop] = new_values
self._message_listener.Modified()
@@ -235,6 +253,17 @@ class RepeatedCompositeFieldContainer(BaseContainer):
"""
self.extend(other._values)
+ def remove(self, elem):
+ """Removes an item from the list. Similar to list.remove()."""
+ self._values.remove(elem)
+ self._message_listener.Modified()
+
+ def pop(self, key=-1):
+ """Removes and returns an item at a given index. Similar to list.pop()."""
+ value = self._values[key]
+ self.__delitem__(key)
+ return value
+
def __getslice__(self, start, stop):
"""Retrieves the subset of items from between the specified indices."""
return self._values[start:stop]
diff --git a/python/google/protobuf/internal/cpp_message.py b/python/google/protobuf/internal/cpp_message.py
index 3f426502..0313cb0b 100755
--- a/python/google/protobuf/internal/cpp_message.py
+++ b/python/google/protobuf/internal/cpp_message.py
@@ -1,6 +1,6 @@
# Protocol Buffers - Google's data interchange format
# Copyright 2008 Google Inc. All rights reserved.
-# http://code.google.com/p/protobuf/
+# 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
@@ -34,8 +34,10 @@ Descriptor objects at runtime backed by the protocol buffer C++ API.
__author__ = 'petar@google.com (Petar Petrov)'
+import copy_reg
import operator
from google.protobuf.internal import _net_proto2___python
+from google.protobuf.internal import enum_type_wrapper
from google.protobuf import message
@@ -156,10 +158,12 @@ class RepeatedScalarContainer(object):
def __hash__(self):
raise TypeError('unhashable object')
- def sort(self, sort_function=cmp):
- values = self[slice(None, None, None)]
- values.sort(sort_function)
- self._cmsg.AssignRepeatedScalar(self._cfield_descriptor, values)
+ def sort(self, *args, **kwargs):
+ # Maintain compatibility with the previous interface.
+ if 'sort_function' in kwargs:
+ kwargs['cmp'] = kwargs.pop('sort_function')
+ self._cmsg.AssignRepeatedScalar(self._cfield_descriptor,
+ sorted(self, *args, **kwargs))
def RepeatedScalarProperty(cdescriptor):
@@ -202,6 +206,12 @@ class RepeatedCompositeContainer(object):
for message in elem_seq:
self.add().MergeFrom(message)
+ def remove(self, value):
+ # TODO(protocol-devel): This is inefficient as it needs to generate a
+ # message pointer for each message only to do index(). Move this to a C++
+ # extension function.
+ self.__delitem__(self[slice(None, None, None)].index(value))
+
def MergeFrom(self, other):
for message in other[:]:
self.add().MergeFrom(message)
@@ -236,27 +246,29 @@ class RepeatedCompositeContainer(object):
def __hash__(self):
raise TypeError('unhashable object')
- def sort(self, sort_function=cmp):
- messages = []
- for index in range(len(self)):
- # messages[i][0] is where the i-th element of the new array has to come
- # from.
- # messages[i][1] is where the i-th element of the old array has to go.
- messages.append([index, 0, self[index]])
- messages.sort(lambda x,y: sort_function(x[2], y[2]))
+ def sort(self, cmp=None, key=None, reverse=False, **kwargs):
+ # Maintain compatibility with the old interface.
+ if cmp is None and 'sort_function' in kwargs:
+ cmp = kwargs.pop('sort_function')
- # Remember which position each elements has to move to.
- for i in range(len(messages)):
- messages[messages[i][0]][1] = i
+ # The cmp function, if provided, is passed the results of the key function,
+ # so we only need to wrap one of them.
+ if key is None:
+ index_key = self.__getitem__
+ else:
+ index_key = lambda i: key(self[i])
+
+ # Sort the list of current indexes by the underlying object.
+ indexes = range(len(self))
+ indexes.sort(cmp=cmp, key=index_key, reverse=reverse)
# Apply the transposition.
- for i in range(len(messages)):
- from_position = messages[i][0]
- if i == from_position:
+ for dest, src in enumerate(indexes):
+ if dest == src:
continue
- self._cmsg.SwapRepeatedFieldElements(
- self._cfield_descriptor, i, from_position)
- messages[messages[i][1]][0] = from_position
+ self._cmsg.SwapRepeatedFieldElements(self._cfield_descriptor, dest, src)
+ # Don't swap the same value twice.
+ indexes[src] = src
def RepeatedCompositeProperty(cdescriptor, message_type):
@@ -359,11 +371,12 @@ class ExtensionDict(object):
return None
-def NewMessage(message_descriptor, dictionary):
+def NewMessage(bases, message_descriptor, dictionary):
"""Creates a new protocol message *class*."""
_AddClassAttributesForNestedExtensions(message_descriptor, dictionary)
_AddEnumValues(message_descriptor, dictionary)
_AddDescriptors(message_descriptor, dictionary)
+ return bases
def InitMessage(message_descriptor, cls):
@@ -372,6 +385,7 @@ def InitMessage(message_descriptor, cls):
_AddInitMethod(message_descriptor, cls)
_AddMessageMethods(message_descriptor, cls)
_AddPropertiesForExtensions(message_descriptor, cls)
+ copy_reg.pickle(cls, lambda obj: (cls, (), obj.__getstate__()))
def _AddDescriptors(message_descriptor, dictionary):
@@ -387,7 +401,7 @@ def _AddDescriptors(message_descriptor, dictionary):
field.full_name)
dictionary['__slots__'] = list(dictionary['__descriptors'].iterkeys()) + [
- '_cmsg', '_owner', '_composite_fields', 'Extensions']
+ '_cmsg', '_owner', '_composite_fields', 'Extensions', '_HACK_REFCOUNTS']
def _AddEnumValues(message_descriptor, dictionary):
@@ -398,6 +412,7 @@ def _AddEnumValues(message_descriptor, dictionary):
dictionary: Class dictionary that should be populated.
"""
for enum_type in message_descriptor.enum_types:
+ dictionary[enum_type.name] = enum_type_wrapper.EnumTypeWrapper(enum_type)
for enum_value in enum_type.values:
dictionary[enum_value.name] = enum_value.number
@@ -439,28 +454,35 @@ def _AddInitMethod(message_descriptor, cls):
def Init(self, **kwargs):
"""Message constructor."""
cmessage = kwargs.pop('__cmessage', None)
- if cmessage is None:
- self._cmsg = NewCMessage(message_descriptor.full_name)
- else:
+ if cmessage:
self._cmsg = cmessage
+ else:
+ self._cmsg = NewCMessage(message_descriptor.full_name)
# Keep a reference to the owner, as the owner keeps a reference to the
# underlying protocol buffer message.
owner = kwargs.pop('__owner', None)
- if owner is not None:
+ if owner:
self._owner = owner
- self.Extensions = ExtensionDict(self)
+ if message_descriptor.is_extendable:
+ self.Extensions = ExtensionDict(self)
+ else:
+ # Reference counting in the C++ code is broken and depends on
+ # the Extensions reference to keep this object alive during unit
+ # tests (see b/4856052). Remove this once b/4945904 is fixed.
+ self._HACK_REFCOUNTS = self
self._composite_fields = {}
for field_name, field_value in kwargs.iteritems():
field_cdescriptor = self.__descriptors.get(field_name, None)
- if field_cdescriptor is None:
+ if not field_cdescriptor:
raise ValueError('Protocol message has no "%s" field.' % field_name)
if field_cdescriptor.label == _LABEL_REPEATED:
if field_cdescriptor.cpp_type == _CPPTYPE_MESSAGE:
+ field_name = getattr(self, field_name)
for val in field_value:
- getattr(self, field_name).add().MergeFrom(val)
+ field_name.add().MergeFrom(val)
else:
getattr(self, field_name).extend(field_value)
elif field_cdescriptor.cpp_type == _CPPTYPE_MESSAGE:
@@ -497,12 +519,34 @@ def _AddMessageMethods(message_descriptor, cls):
return self._cmsg.HasField(field_name)
def ClearField(self, field_name):
+ child_cmessage = None
if field_name in self._composite_fields:
+ child_field = self._composite_fields[field_name]
del self._composite_fields[field_name]
- self._cmsg.ClearField(field_name)
+
+ child_cdescriptor = self.__descriptors[field_name]
+ # TODO(anuraag): Support clearing repeated message fields as well.
+ if (child_cdescriptor.label != _LABEL_REPEATED and
+ child_cdescriptor.cpp_type == _CPPTYPE_MESSAGE):
+ child_field._owner = None
+ child_cmessage = child_field._cmsg
+
+ if child_cmessage is not None:
+ self._cmsg.ClearField(field_name, child_cmessage)
+ else:
+ self._cmsg.ClearField(field_name)
def Clear(self):
- return self._cmsg.Clear()
+ cmessages_to_release = []
+ for field_name, child_field in self._composite_fields.iteritems():
+ child_cdescriptor = self.__descriptors[field_name]
+ # TODO(anuraag): Support clearing repeated message fields as well.
+ if (child_cdescriptor.label != _LABEL_REPEATED and
+ child_cdescriptor.cpp_type == _CPPTYPE_MESSAGE):
+ child_field._owner = None
+ cmessages_to_release.append((child_cdescriptor, child_field._cmsg))
+ self._composite_fields.clear()
+ self._cmsg.Clear(cmessages_to_release)
def IsInitialized(self, errors=None):
if self._cmsg.IsInitialized():
@@ -514,8 +558,8 @@ def _AddMessageMethods(message_descriptor, cls):
def SerializeToString(self):
if not self.IsInitialized():
raise message.EncodeError(
- 'Message is missing required fields: ' +
- ','.join(self.FindInitializationErrors()))
+ 'Message %s is missing required fields: %s' % (
+ self._cmsg.full_name, ','.join(self.FindInitializationErrors())))
return self._cmsg.SerializeToString()
def SerializePartialToString(self):
@@ -534,7 +578,8 @@ def _AddMessageMethods(message_descriptor, cls):
def MergeFrom(self, msg):
if not isinstance(msg, cls):
raise TypeError(
- "Parameter to MergeFrom() must be instance of same class.")
+ "Parameter to MergeFrom() must be instance of same class: "
+ "expected %s got %s." % (cls.__name__, type(msg).__name__))
self._cmsg.MergeFrom(msg._cmsg)
def CopyFrom(self, msg):
@@ -565,7 +610,7 @@ def _AddMessageMethods(message_descriptor, cls):
return self._cmsg.FindInitializationErrors()
def __str__(self):
- return self._cmsg.DebugString()
+ return str(self._cmsg)
def __eq__(self, other):
if self is other:
@@ -581,6 +626,8 @@ def _AddMessageMethods(message_descriptor, cls):
raise TypeError('unhashable object')
def __unicode__(self):
+ # Lazy import to prevent circular import when text_format imports this file.
+ from google.protobuf import text_format
return text_format.MessageToString(self, as_utf8=True).decode('utf-8')
# Attach the local methods to the message class.
diff --git a/python/google/protobuf/internal/decoder.py b/python/google/protobuf/internal/decoder.py
index 55f746f5..0f500606 100755
--- a/python/google/protobuf/internal/decoder.py
+++ b/python/google/protobuf/internal/decoder.py
@@ -1,6 +1,6 @@
# Protocol Buffers - Google's data interchange format
# Copyright 2008 Google Inc. All rights reserved.
-# http://code.google.com/p/protobuf/
+# 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
@@ -28,6 +28,10 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#PY25 compatible for GAE.
+#
+# Copyright 2009 Google Inc. All Rights Reserved.
+
"""Code for decoding protocol buffer primitives.
This code is very similar to encoder.py -- read the docs for that module first.
@@ -81,6 +85,8 @@ we repeatedly read a tag, look up the corresponding decoder, and invoke it.
__author__ = 'kenton@google.com (Kenton Varda)'
import struct
+import sys ##PY25
+_PY2 = sys.version_info[0] < 3 ##PY25
from google.protobuf.internal import encoder
from google.protobuf.internal import wire_format
from google.protobuf import message
@@ -98,7 +104,7 @@ _NAN = _POS_INF * 0
_DecodeError = message.DecodeError
-def _VarintDecoder(mask):
+def _VarintDecoder(mask, result_type):
"""Return an encoder for a basic varint value (does not include tag).
Decoded values will be bitwise-anded with the given mask before being
@@ -109,15 +115,18 @@ def _VarintDecoder(mask):
"""
local_ord = ord
+ py2 = _PY2 ##PY25
+##!PY25 py2 = str is bytes
def DecodeVarint(buffer, pos):
result = 0
shift = 0
while 1:
- b = local_ord(buffer[pos])
+ b = local_ord(buffer[pos]) if py2 else buffer[pos]
result |= ((b & 0x7f) << shift)
pos += 1
if not (b & 0x80):
result &= mask
+ result = result_type(result)
return (result, pos)
shift += 7
if shift >= 64:
@@ -125,15 +134,17 @@ def _VarintDecoder(mask):
return DecodeVarint
-def _SignedVarintDecoder(mask):
+def _SignedVarintDecoder(mask, result_type):
"""Like _VarintDecoder() but decodes signed values."""
local_ord = ord
+ py2 = _PY2 ##PY25
+##!PY25 py2 = str is bytes
def DecodeVarint(buffer, pos):
result = 0
shift = 0
while 1:
- b = local_ord(buffer[pos])
+ b = local_ord(buffer[pos]) if py2 else buffer[pos]
result |= ((b & 0x7f) << shift)
pos += 1
if not (b & 0x80):
@@ -142,19 +153,23 @@ def _SignedVarintDecoder(mask):
result |= ~mask
else:
result &= mask
+ result = result_type(result)
return (result, pos)
shift += 7
if shift >= 64:
raise _DecodeError('Too many bytes when decoding varint.')
return DecodeVarint
+# We force 32-bit values to int and 64-bit values to long to make
+# alternate implementations where the distinction is more significant
+# (e.g. the C++ implementation) simpler.
-_DecodeVarint = _VarintDecoder((1 << 64) - 1)
-_DecodeSignedVarint = _SignedVarintDecoder((1 << 64) - 1)
+_DecodeVarint = _VarintDecoder((1 << 64) - 1, long)
+_DecodeSignedVarint = _SignedVarintDecoder((1 << 64) - 1, long)
# Use these versions for values which must be limited to 32 bits.
-_DecodeVarint32 = _VarintDecoder((1 << 32) - 1)
-_DecodeSignedVarint32 = _SignedVarintDecoder((1 << 32) - 1)
+_DecodeVarint32 = _VarintDecoder((1 << 32) - 1, int)
+_DecodeSignedVarint32 = _SignedVarintDecoder((1 << 32) - 1, int)
def ReadTag(buffer, pos):
@@ -168,8 +183,10 @@ def ReadTag(buffer, pos):
use that, but not in Python.
"""
+ py2 = _PY2 ##PY25
+##!PY25 py2 = str is bytes
start = pos
- while ord(buffer[pos]) & 0x80:
+ while (ord(buffer[pos]) if py2 else buffer[pos]) & 0x80:
pos += 1
pos += 1
return (buffer[start:pos], pos)
@@ -284,6 +301,7 @@ def _FloatDecoder():
"""
local_unpack = struct.unpack
+ b = (lambda x:x) if _PY2 else lambda x:x.encode('latin1') ##PY25
def InnerDecode(buffer, pos):
# We expect a 32-bit value in little-endian byte order. Bit 1 is the sign
@@ -294,13 +312,17 @@ def _FloatDecoder():
# If this value has all its exponent bits set, then it's non-finite.
# In Python 2.4, struct.unpack will convert it to a finite 64-bit value.
# To avoid that, we parse it specially.
- if ((float_bytes[3] in '\x7F\xFF')
- and (float_bytes[2] >= '\x80')):
+ if ((float_bytes[3:4] in b('\x7F\xFF')) ##PY25
+##!PY25 if ((float_bytes[3:4] in b'\x7F\xFF')
+ and (float_bytes[2:3] >= b('\x80'))): ##PY25
+##!PY25 and (float_bytes[2:3] >= b'\x80')):
# If at least one significand bit is set...
- if float_bytes[0:3] != '\x00\x00\x80':
+ if float_bytes[0:3] != b('\x00\x00\x80'): ##PY25
+##!PY25 if float_bytes[0:3] != b'\x00\x00\x80':
return (_NAN, new_pos)
# If sign bit is set...
- if float_bytes[3] == '\xFF':
+ if float_bytes[3:4] == b('\xFF'): ##PY25
+##!PY25 if float_bytes[3:4] == b'\xFF':
return (_NEG_INF, new_pos)
return (_POS_INF, new_pos)
@@ -319,6 +341,7 @@ def _DoubleDecoder():
"""
local_unpack = struct.unpack
+ b = (lambda x:x) if _PY2 else lambda x:x.encode('latin1') ##PY25
def InnerDecode(buffer, pos):
# We expect a 64-bit value in little-endian byte order. Bit 1 is the sign
@@ -329,9 +352,12 @@ def _DoubleDecoder():
# If this value has all its exponent bits set and at least one significand
# bit set, it's not a number. In Python 2.4, struct.unpack will treat it
# as inf or -inf. To avoid that, we treat it specially.
- if ((double_bytes[7] in '\x7F\xFF')
- and (double_bytes[6] >= '\xF0')
- and (double_bytes[0:7] != '\x00\x00\x00\x00\x00\x00\xF0')):
+##!PY25 if ((double_bytes[7:8] in b'\x7F\xFF')
+##!PY25 and (double_bytes[6:7] >= b'\xF0')
+##!PY25 and (double_bytes[0:7] != b'\x00\x00\x00\x00\x00\x00\xF0')):
+ if ((double_bytes[7:8] in b('\x7F\xFF')) ##PY25
+ and (double_bytes[6:7] >= b('\xF0')) ##PY25
+ and (double_bytes[0:7] != b('\x00\x00\x00\x00\x00\x00\xF0'))): ##PY25
return (_NAN, new_pos)
# Note that we expect someone up-stack to catch struct.error and convert
@@ -342,10 +368,86 @@ def _DoubleDecoder():
return _SimpleDecoder(wire_format.WIRETYPE_FIXED64, InnerDecode)
+def EnumDecoder(field_number, is_repeated, is_packed, key, new_default):
+ enum_type = key.enum_type
+ if is_packed:
+ local_DecodeVarint = _DecodeVarint
+ def DecodePackedField(buffer, pos, end, message, field_dict):
+ value = field_dict.get(key)
+ if value is None:
+ value = field_dict.setdefault(key, new_default(message))
+ (endpoint, pos) = local_DecodeVarint(buffer, pos)
+ endpoint += pos
+ if endpoint > end:
+ raise _DecodeError('Truncated message.')
+ while pos < endpoint:
+ value_start_pos = pos
+ (element, pos) = _DecodeSignedVarint32(buffer, pos)
+ if element in enum_type.values_by_number:
+ value.append(element)
+ else:
+ if not message._unknown_fields:
+ message._unknown_fields = []
+ tag_bytes = encoder.TagBytes(field_number,
+ wire_format.WIRETYPE_VARINT)
+ message._unknown_fields.append(
+ (tag_bytes, buffer[value_start_pos:pos]))
+ if pos > endpoint:
+ if element in enum_type.values_by_number:
+ del value[-1] # Discard corrupt value.
+ else:
+ del message._unknown_fields[-1]
+ raise _DecodeError('Packed element was truncated.')
+ return pos
+ return DecodePackedField
+ elif is_repeated:
+ tag_bytes = encoder.TagBytes(field_number, wire_format.WIRETYPE_VARINT)
+ tag_len = len(tag_bytes)
+ def DecodeRepeatedField(buffer, pos, end, message, field_dict):
+ value = field_dict.get(key)
+ if value is None:
+ value = field_dict.setdefault(key, new_default(message))
+ while 1:
+ (element, new_pos) = _DecodeSignedVarint32(buffer, pos)
+ if element in enum_type.values_by_number:
+ value.append(element)
+ else:
+ if not message._unknown_fields:
+ message._unknown_fields = []
+ message._unknown_fields.append(
+ (tag_bytes, buffer[pos:new_pos]))
+ # Predict that the next tag is another copy of the same repeated
+ # field.
+ pos = new_pos + tag_len
+ if buffer[new_pos:pos] != tag_bytes or new_pos >= end:
+ # Prediction failed. Return.
+ if new_pos > end:
+ raise _DecodeError('Truncated message.')
+ return new_pos
+ return DecodeRepeatedField
+ else:
+ def DecodeField(buffer, pos, end, message, field_dict):
+ value_start_pos = pos
+ (enum_value, pos) = _DecodeSignedVarint32(buffer, pos)
+ if pos > end:
+ raise _DecodeError('Truncated message.')
+ if enum_value in enum_type.values_by_number:
+ field_dict[key] = enum_value
+ else:
+ if not message._unknown_fields:
+ message._unknown_fields = []
+ tag_bytes = encoder.TagBytes(field_number,
+ wire_format.WIRETYPE_VARINT)
+ message._unknown_fields.append(
+ (tag_bytes, buffer[value_start_pos:pos]))
+ return pos
+ return DecodeField
+
+
# --------------------------------------------------------------------
-Int32Decoder = EnumDecoder = _SimpleDecoder(
+Int32Decoder = _SimpleDecoder(
wire_format.WIRETYPE_VARINT, _DecodeSignedVarint32)
Int64Decoder = _SimpleDecoder(
@@ -380,6 +482,14 @@ def StringDecoder(field_number, is_repeated, is_packed, key, new_default):
local_DecodeVarint = _DecodeVarint
local_unicode = unicode
+ def _ConvertToUnicode(byte_str):
+ try:
+ return local_unicode(byte_str, 'utf-8')
+ except UnicodeDecodeError, e:
+ # add more information to the error message and re-raise it.
+ e.reason = '%s in field: %s' % (e, key.full_name)
+ raise
+
assert not is_packed
if is_repeated:
tag_bytes = encoder.TagBytes(field_number,
@@ -394,7 +504,7 @@ def StringDecoder(field_number, is_repeated, is_packed, key, new_default):
new_pos = pos + size
if new_pos > end:
raise _DecodeError('Truncated string.')
- value.append(local_unicode(buffer[pos:new_pos], 'utf-8'))
+ value.append(_ConvertToUnicode(buffer[pos:new_pos]))
# Predict that the next tag is another copy of the same repeated field.
pos = new_pos + tag_len
if buffer[new_pos:pos] != tag_bytes or new_pos == end:
@@ -407,7 +517,7 @@ def StringDecoder(field_number, is_repeated, is_packed, key, new_default):
new_pos = pos + size
if new_pos > end:
raise _DecodeError('Truncated string.')
- field_dict[key] = local_unicode(buffer[pos:new_pos], 'utf-8')
+ field_dict[key] = _ConvertToUnicode(buffer[pos:new_pos])
return new_pos
return DecodeField
@@ -511,9 +621,6 @@ def MessageDecoder(field_number, is_repeated, is_packed, key, new_default):
if value is None:
value = field_dict.setdefault(key, new_default(message))
while 1:
- value = field_dict.get(key)
- if value is None:
- value = field_dict.setdefault(key, new_default(message))
# Read length.
(size, pos) = local_DecodeVarint(buffer, pos)
new_pos = pos + size
@@ -576,6 +683,7 @@ def MessageSetItemDecoder(extensions_by_number):
local_SkipField = SkipField
def DecodeItem(buffer, pos, end, message, field_dict):
+ message_set_item_start = pos
type_id = -1
message_start = -1
message_end = -1
@@ -614,6 +722,11 @@ def MessageSetItemDecoder(extensions_by_number):
# The only reason _InternalParse would return early is if it encountered
# an end-group tag.
raise _DecodeError('Unexpected end-group tag.')
+ else:
+ if not message._unknown_fields:
+ message._unknown_fields = []
+ message._unknown_fields.append((MESSAGE_SET_ITEM_TAG,
+ buffer[message_set_item_start:pos]))
return pos
@@ -625,8 +738,10 @@ def MessageSetItemDecoder(extensions_by_number):
def _SkipVarint(buffer, pos, end):
"""Skip a varint value. Returns the new position."""
-
- while ord(buffer[pos]) & 0x80:
+ # Previously ord(buffer[pos]) raised IndexError when pos is out of range.
+ # With this code, ord(b'') raises TypeError. Both are handled in
+ # python_message.py to generate a 'Truncated message' error.
+ while ord(buffer[pos:pos+1]) & 0x80:
pos += 1
pos += 1
if pos > end:
@@ -693,7 +808,6 @@ def _FieldSkipper():
]
wiretype_mask = wire_format.TAG_TYPE_MASK
- local_ord = ord
def SkipField(buffer, pos, end, tag_bytes):
"""Skips a field with the specified tag.
@@ -706,7 +820,7 @@ def _FieldSkipper():
"""
# The wire type is always in the first byte since varints are little-endian.
- wire_type = local_ord(tag_bytes[0]) & wiretype_mask
+ wire_type = ord(tag_bytes[0:1]) & wiretype_mask
return WIRETYPE_TO_SKIPPER[wire_type](buffer, pos, end)
return SkipField
diff --git a/python/google/protobuf/internal/descriptor_database_test.py b/python/google/protobuf/internal/descriptor_database_test.py
new file mode 100644
index 00000000..8970f5c2
--- /dev/null
+++ b/python/google/protobuf/internal/descriptor_database_test.py
@@ -0,0 +1,65 @@
+#! /usr/bin/python
+#
+# 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.
+
+"""Tests for google.protobuf.descriptor_database."""
+
+__author__ = 'matthewtoia@google.com (Matt Toia)'
+
+from google.apputils import basetest
+from google.protobuf import descriptor_pb2
+from google.protobuf.internal import factory_test2_pb2
+from google.protobuf import descriptor_database
+
+
+class DescriptorDatabaseTest(basetest.TestCase):
+
+ def testAdd(self):
+ db = descriptor_database.DescriptorDatabase()
+ file_desc_proto = descriptor_pb2.FileDescriptorProto.FromString(
+ factory_test2_pb2.DESCRIPTOR.serialized_pb)
+ db.Add(file_desc_proto)
+
+ self.assertEquals(file_desc_proto, db.FindFileByName(
+ 'google/protobuf/internal/factory_test2.proto'))
+ self.assertEquals(file_desc_proto, db.FindFileContainingSymbol(
+ 'google.protobuf.python.internal.Factory2Message'))
+ self.assertEquals(file_desc_proto, db.FindFileContainingSymbol(
+ 'google.protobuf.python.internal.Factory2Message.NestedFactory2Message'))
+ self.assertEquals(file_desc_proto, db.FindFileContainingSymbol(
+ 'google.protobuf.python.internal.Factory2Enum'))
+ self.assertEquals(file_desc_proto, db.FindFileContainingSymbol(
+ 'google.protobuf.python.internal.Factory2Message.NestedFactory2Enum'))
+ self.assertEquals(file_desc_proto, db.FindFileContainingSymbol(
+ 'google.protobuf.python.internal.MessageWithNestedEnumOnly.NestedEnum'))
+
+if __name__ == '__main__':
+ basetest.main()
diff --git a/python/google/protobuf/internal/descriptor_pool_test.py b/python/google/protobuf/internal/descriptor_pool_test.py
new file mode 100644
index 00000000..11ef61c5
--- /dev/null
+++ b/python/google/protobuf/internal/descriptor_pool_test.py
@@ -0,0 +1,591 @@
+#! /usr/bin/python
+#
+# 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.
+
+"""Tests for google.protobuf.descriptor_pool."""
+
+__author__ = 'matthewtoia@google.com (Matt Toia)'
+
+import os
+import unittest
+
+from google.apputils import basetest
+from google.protobuf import unittest_pb2
+from google.protobuf import descriptor_pb2
+from google.protobuf.internal import api_implementation
+from google.protobuf.internal import descriptor_pool_test1_pb2
+from google.protobuf.internal import descriptor_pool_test2_pb2
+from google.protobuf.internal import factory_test1_pb2
+from google.protobuf.internal import factory_test2_pb2
+from google.protobuf import descriptor
+from google.protobuf import descriptor_database
+from google.protobuf import descriptor_pool
+from google.protobuf import symbol_database
+
+
+class DescriptorPoolTest(basetest.TestCase):
+
+ def setUp(self):
+ self.pool = descriptor_pool.DescriptorPool()
+ self.factory_test1_fd = descriptor_pb2.FileDescriptorProto.FromString(
+ factory_test1_pb2.DESCRIPTOR.serialized_pb)
+ self.factory_test2_fd = descriptor_pb2.FileDescriptorProto.FromString(
+ factory_test2_pb2.DESCRIPTOR.serialized_pb)
+ self.pool.Add(self.factory_test1_fd)
+ self.pool.Add(self.factory_test2_fd)
+
+ def testFindFileByName(self):
+ name1 = 'google/protobuf/internal/factory_test1.proto'
+ file_desc1 = self.pool.FindFileByName(name1)
+ self.assertIsInstance(file_desc1, descriptor.FileDescriptor)
+ self.assertEquals(name1, file_desc1.name)
+ self.assertEquals('google.protobuf.python.internal', file_desc1.package)
+ self.assertIn('Factory1Message', file_desc1.message_types_by_name)
+
+ name2 = 'google/protobuf/internal/factory_test2.proto'
+ file_desc2 = self.pool.FindFileByName(name2)
+ self.assertIsInstance(file_desc2, descriptor.FileDescriptor)
+ self.assertEquals(name2, file_desc2.name)
+ self.assertEquals('google.protobuf.python.internal', file_desc2.package)
+ self.assertIn('Factory2Message', file_desc2.message_types_by_name)
+
+ def testFindFileByNameFailure(self):
+ with self.assertRaises(KeyError):
+ self.pool.FindFileByName('Does not exist')
+
+ def testFindFileContainingSymbol(self):
+ file_desc1 = self.pool.FindFileContainingSymbol(
+ 'google.protobuf.python.internal.Factory1Message')
+ self.assertIsInstance(file_desc1, descriptor.FileDescriptor)
+ self.assertEquals('google/protobuf/internal/factory_test1.proto',
+ file_desc1.name)
+ self.assertEquals('google.protobuf.python.internal', file_desc1.package)
+ self.assertIn('Factory1Message', file_desc1.message_types_by_name)
+
+ file_desc2 = self.pool.FindFileContainingSymbol(
+ 'google.protobuf.python.internal.Factory2Message')
+ self.assertIsInstance(file_desc2, descriptor.FileDescriptor)
+ self.assertEquals('google/protobuf/internal/factory_test2.proto',
+ file_desc2.name)
+ self.assertEquals('google.protobuf.python.internal', file_desc2.package)
+ self.assertIn('Factory2Message', file_desc2.message_types_by_name)
+
+ def testFindFileContainingSymbolFailure(self):
+ with self.assertRaises(KeyError):
+ self.pool.FindFileContainingSymbol('Does not exist')
+
+ def testFindMessageTypeByName(self):
+ msg1 = self.pool.FindMessageTypeByName(
+ 'google.protobuf.python.internal.Factory1Message')
+ self.assertIsInstance(msg1, descriptor.Descriptor)
+ self.assertEquals('Factory1Message', msg1.name)
+ self.assertEquals('google.protobuf.python.internal.Factory1Message',
+ msg1.full_name)
+ self.assertEquals(None, msg1.containing_type)
+
+ nested_msg1 = msg1.nested_types[0]
+ self.assertEquals('NestedFactory1Message', nested_msg1.name)
+ self.assertEquals(msg1, nested_msg1.containing_type)
+
+ nested_enum1 = msg1.enum_types[0]
+ self.assertEquals('NestedFactory1Enum', nested_enum1.name)
+ self.assertEquals(msg1, nested_enum1.containing_type)
+
+ self.assertEquals(nested_msg1, msg1.fields_by_name[
+ 'nested_factory_1_message'].message_type)
+ self.assertEquals(nested_enum1, msg1.fields_by_name[
+ 'nested_factory_1_enum'].enum_type)
+
+ msg2 = self.pool.FindMessageTypeByName(
+ 'google.protobuf.python.internal.Factory2Message')
+ self.assertIsInstance(msg2, descriptor.Descriptor)
+ self.assertEquals('Factory2Message', msg2.name)
+ self.assertEquals('google.protobuf.python.internal.Factory2Message',
+ msg2.full_name)
+ self.assertIsNone(msg2.containing_type)
+
+ nested_msg2 = msg2.nested_types[0]
+ self.assertEquals('NestedFactory2Message', nested_msg2.name)
+ self.assertEquals(msg2, nested_msg2.containing_type)
+
+ nested_enum2 = msg2.enum_types[0]
+ self.assertEquals('NestedFactory2Enum', nested_enum2.name)
+ self.assertEquals(msg2, nested_enum2.containing_type)
+
+ self.assertEquals(nested_msg2, msg2.fields_by_name[
+ 'nested_factory_2_message'].message_type)
+ self.assertEquals(nested_enum2, msg2.fields_by_name[
+ 'nested_factory_2_enum'].enum_type)
+
+ self.assertTrue(msg2.fields_by_name['int_with_default'].has_default_value)
+ self.assertEquals(
+ 1776, msg2.fields_by_name['int_with_default'].default_value)
+
+ self.assertTrue(
+ msg2.fields_by_name['double_with_default'].has_default_value)
+ self.assertEquals(
+ 9.99, msg2.fields_by_name['double_with_default'].default_value)
+
+ self.assertTrue(
+ msg2.fields_by_name['string_with_default'].has_default_value)
+ self.assertEquals(
+ 'hello world', msg2.fields_by_name['string_with_default'].default_value)
+
+ self.assertTrue(msg2.fields_by_name['bool_with_default'].has_default_value)
+ self.assertFalse(msg2.fields_by_name['bool_with_default'].default_value)
+
+ self.assertTrue(msg2.fields_by_name['enum_with_default'].has_default_value)
+ self.assertEquals(
+ 1, msg2.fields_by_name['enum_with_default'].default_value)
+
+ msg3 = self.pool.FindMessageTypeByName(
+ 'google.protobuf.python.internal.Factory2Message.NestedFactory2Message')
+ self.assertEquals(nested_msg2, msg3)
+
+ self.assertTrue(msg2.fields_by_name['bytes_with_default'].has_default_value)
+ self.assertEquals(
+ b'a\xfb\x00c',
+ msg2.fields_by_name['bytes_with_default'].default_value)
+
+ self.assertEqual(1, len(msg2.oneofs))
+ self.assertEqual(1, len(msg2.oneofs_by_name))
+ self.assertEqual(2, len(msg2.oneofs[0].fields))
+ for name in ['oneof_int', 'oneof_string']:
+ self.assertEqual(msg2.oneofs[0],
+ msg2.fields_by_name[name].containing_oneof)
+ self.assertIn(msg2.fields_by_name[name], msg2.oneofs[0].fields)
+
+ def testFindMessageTypeByNameFailure(self):
+ with self.assertRaises(KeyError):
+ self.pool.FindMessageTypeByName('Does not exist')
+
+ def testFindEnumTypeByName(self):
+ enum1 = self.pool.FindEnumTypeByName(
+ 'google.protobuf.python.internal.Factory1Enum')
+ self.assertIsInstance(enum1, descriptor.EnumDescriptor)
+ self.assertEquals(0, enum1.values_by_name['FACTORY_1_VALUE_0'].number)
+ self.assertEquals(1, enum1.values_by_name['FACTORY_1_VALUE_1'].number)
+
+ nested_enum1 = self.pool.FindEnumTypeByName(
+ 'google.protobuf.python.internal.Factory1Message.NestedFactory1Enum')
+ self.assertIsInstance(nested_enum1, descriptor.EnumDescriptor)
+ self.assertEquals(
+ 0, nested_enum1.values_by_name['NESTED_FACTORY_1_VALUE_0'].number)
+ self.assertEquals(
+ 1, nested_enum1.values_by_name['NESTED_FACTORY_1_VALUE_1'].number)
+
+ enum2 = self.pool.FindEnumTypeByName(
+ 'google.protobuf.python.internal.Factory2Enum')
+ self.assertIsInstance(enum2, descriptor.EnumDescriptor)
+ self.assertEquals(0, enum2.values_by_name['FACTORY_2_VALUE_0'].number)
+ self.assertEquals(1, enum2.values_by_name['FACTORY_2_VALUE_1'].number)
+
+ nested_enum2 = self.pool.FindEnumTypeByName(
+ 'google.protobuf.python.internal.Factory2Message.NestedFactory2Enum')
+ self.assertIsInstance(nested_enum2, descriptor.EnumDescriptor)
+ self.assertEquals(
+ 0, nested_enum2.values_by_name['NESTED_FACTORY_2_VALUE_0'].number)
+ self.assertEquals(
+ 1, nested_enum2.values_by_name['NESTED_FACTORY_2_VALUE_1'].number)
+
+ def testFindEnumTypeByNameFailure(self):
+ with self.assertRaises(KeyError):
+ self.pool.FindEnumTypeByName('Does not exist')
+
+ def testUserDefinedDB(self):
+ db = descriptor_database.DescriptorDatabase()
+ self.pool = descriptor_pool.DescriptorPool(db)
+ db.Add(self.factory_test1_fd)
+ db.Add(self.factory_test2_fd)
+ self.testFindMessageTypeByName()
+
+ def testComplexNesting(self):
+ test1_desc = descriptor_pb2.FileDescriptorProto.FromString(
+ descriptor_pool_test1_pb2.DESCRIPTOR.serialized_pb)
+ test2_desc = descriptor_pb2.FileDescriptorProto.FromString(
+ descriptor_pool_test2_pb2.DESCRIPTOR.serialized_pb)
+ self.pool.Add(test1_desc)
+ self.pool.Add(test2_desc)
+ TEST1_FILE.CheckFile(self, self.pool)
+ TEST2_FILE.CheckFile(self, self.pool)
+
+
+ def testEnumDefaultValue(self):
+ """Test the default value of enums which don't start at zero."""
+ def _CheckDefaultValue(file_descriptor):
+ default_value = (file_descriptor
+ .message_types_by_name['DescriptorPoolTest1']
+ .fields_by_name['nested_enum']
+ .default_value)
+ self.assertEqual(default_value,
+ descriptor_pool_test1_pb2.DescriptorPoolTest1.BETA)
+ # First check what the generated descriptor contains.
+ _CheckDefaultValue(descriptor_pool_test1_pb2.DESCRIPTOR)
+ # Then check the generated pool. Normally this is the same descriptor.
+ file_descriptor = symbol_database.Default().pool.FindFileByName(
+ 'google/protobuf/internal/descriptor_pool_test1.proto')
+ self.assertIs(file_descriptor, descriptor_pool_test1_pb2.DESCRIPTOR)
+ _CheckDefaultValue(file_descriptor)
+
+ # Then check the dynamic pool and its internal DescriptorDatabase.
+ descriptor_proto = descriptor_pb2.FileDescriptorProto.FromString(
+ descriptor_pool_test1_pb2.DESCRIPTOR.serialized_pb)
+ self.pool.Add(descriptor_proto)
+ # And do the same check as above
+ file_descriptor = self.pool.FindFileByName(
+ 'google/protobuf/internal/descriptor_pool_test1.proto')
+ _CheckDefaultValue(file_descriptor)
+
+
+class ProtoFile(object):
+
+ def __init__(self, name, package, messages, dependencies=None):
+ self.name = name
+ self.package = package
+ self.messages = messages
+ self.dependencies = dependencies or []
+
+ def CheckFile(self, test, pool):
+ file_desc = pool.FindFileByName(self.name)
+ test.assertEquals(self.name, file_desc.name)
+ test.assertEquals(self.package, file_desc.package)
+ dependencies_names = [f.name for f in file_desc.dependencies]
+ test.assertEqual(self.dependencies, dependencies_names)
+ for name, msg_type in self.messages.items():
+ msg_type.CheckType(test, None, name, file_desc)
+
+
+class EnumType(object):
+
+ def __init__(self, values):
+ self.values = values
+
+ def CheckType(self, test, msg_desc, name, file_desc):
+ enum_desc = msg_desc.enum_types_by_name[name]
+ test.assertEqual(name, enum_desc.name)
+ expected_enum_full_name = '.'.join([msg_desc.full_name, name])
+ test.assertEqual(expected_enum_full_name, enum_desc.full_name)
+ test.assertEqual(msg_desc, enum_desc.containing_type)
+ test.assertEqual(file_desc, enum_desc.file)
+ for index, (value, number) in enumerate(self.values):
+ value_desc = enum_desc.values_by_name[value]
+ test.assertEqual(value, value_desc.name)
+ test.assertEqual(index, value_desc.index)
+ test.assertEqual(number, value_desc.number)
+ test.assertEqual(enum_desc, value_desc.type)
+ test.assertIn(value, msg_desc.enum_values_by_name)
+
+
+class MessageType(object):
+
+ def __init__(self, type_dict, field_list, is_extendable=False,
+ extensions=None):
+ self.type_dict = type_dict
+ self.field_list = field_list
+ self.is_extendable = is_extendable
+ self.extensions = extensions or []
+
+ def CheckType(self, test, containing_type_desc, name, file_desc):
+ if containing_type_desc is None:
+ desc = file_desc.message_types_by_name[name]
+ expected_full_name = '.'.join([file_desc.package, name])
+ else:
+ desc = containing_type_desc.nested_types_by_name[name]
+ expected_full_name = '.'.join([containing_type_desc.full_name, name])
+
+ test.assertEqual(name, desc.name)
+ test.assertEqual(expected_full_name, desc.full_name)
+ test.assertEqual(containing_type_desc, desc.containing_type)
+ test.assertEqual(desc.file, file_desc)
+ test.assertEqual(self.is_extendable, desc.is_extendable)
+ for name, subtype in self.type_dict.items():
+ subtype.CheckType(test, desc, name, file_desc)
+
+ for index, (name, field) in enumerate(self.field_list):
+ field.CheckField(test, desc, name, index)
+
+ for index, (name, field) in enumerate(self.extensions):
+ field.CheckField(test, desc, name, index)
+
+
+class EnumField(object):
+
+ def __init__(self, number, type_name, default_value):
+ self.number = number
+ self.type_name = type_name
+ self.default_value = default_value
+
+ def CheckField(self, test, msg_desc, name, index):
+ field_desc = msg_desc.fields_by_name[name]
+ enum_desc = msg_desc.enum_types_by_name[self.type_name]
+ test.assertEqual(name, field_desc.name)
+ expected_field_full_name = '.'.join([msg_desc.full_name, name])
+ test.assertEqual(expected_field_full_name, field_desc.full_name)
+ test.assertEqual(index, field_desc.index)
+ test.assertEqual(self.number, field_desc.number)
+ test.assertEqual(descriptor.FieldDescriptor.TYPE_ENUM, field_desc.type)
+ test.assertEqual(descriptor.FieldDescriptor.CPPTYPE_ENUM,
+ field_desc.cpp_type)
+ test.assertTrue(field_desc.has_default_value)
+ test.assertEqual(enum_desc.values_by_name[self.default_value].number,
+ field_desc.default_value)
+ test.assertEqual(msg_desc, field_desc.containing_type)
+ test.assertEqual(enum_desc, field_desc.enum_type)
+
+
+class MessageField(object):
+
+ def __init__(self, number, type_name):
+ self.number = number
+ self.type_name = type_name
+
+ def CheckField(self, test, msg_desc, name, index):
+ field_desc = msg_desc.fields_by_name[name]
+ field_type_desc = msg_desc.nested_types_by_name[self.type_name]
+ test.assertEqual(name, field_desc.name)
+ expected_field_full_name = '.'.join([msg_desc.full_name, name])
+ test.assertEqual(expected_field_full_name, field_desc.full_name)
+ test.assertEqual(index, field_desc.index)
+ test.assertEqual(self.number, field_desc.number)
+ test.assertEqual(descriptor.FieldDescriptor.TYPE_MESSAGE, field_desc.type)
+ test.assertEqual(descriptor.FieldDescriptor.CPPTYPE_MESSAGE,
+ field_desc.cpp_type)
+ test.assertFalse(field_desc.has_default_value)
+ test.assertEqual(msg_desc, field_desc.containing_type)
+ test.assertEqual(field_type_desc, field_desc.message_type)
+
+
+class StringField(object):
+
+ def __init__(self, number, default_value):
+ self.number = number
+ self.default_value = default_value
+
+ def CheckField(self, test, msg_desc, name, index):
+ field_desc = msg_desc.fields_by_name[name]
+ test.assertEqual(name, field_desc.name)
+ expected_field_full_name = '.'.join([msg_desc.full_name, name])
+ test.assertEqual(expected_field_full_name, field_desc.full_name)
+ test.assertEqual(index, field_desc.index)
+ test.assertEqual(self.number, field_desc.number)
+ test.assertEqual(descriptor.FieldDescriptor.TYPE_STRING, field_desc.type)
+ test.assertEqual(descriptor.FieldDescriptor.CPPTYPE_STRING,
+ field_desc.cpp_type)
+ test.assertTrue(field_desc.has_default_value)
+ test.assertEqual(self.default_value, field_desc.default_value)
+
+
+class ExtensionField(object):
+
+ def __init__(self, number, extended_type):
+ self.number = number
+ self.extended_type = extended_type
+
+ def CheckField(self, test, msg_desc, name, index):
+ field_desc = msg_desc.extensions_by_name[name]
+ test.assertEqual(name, field_desc.name)
+ expected_field_full_name = '.'.join([msg_desc.full_name, name])
+ test.assertEqual(expected_field_full_name, field_desc.full_name)
+ test.assertEqual(self.number, field_desc.number)
+ test.assertEqual(index, field_desc.index)
+ test.assertEqual(descriptor.FieldDescriptor.TYPE_MESSAGE, field_desc.type)
+ test.assertEqual(descriptor.FieldDescriptor.CPPTYPE_MESSAGE,
+ field_desc.cpp_type)
+ test.assertFalse(field_desc.has_default_value)
+ test.assertTrue(field_desc.is_extension)
+ test.assertEqual(msg_desc, field_desc.extension_scope)
+ test.assertEqual(msg_desc, field_desc.message_type)
+ test.assertEqual(self.extended_type, field_desc.containing_type.name)
+
+
+class AddDescriptorTest(basetest.TestCase):
+
+ def _TestMessage(self, prefix):
+ pool = descriptor_pool.DescriptorPool()
+ pool.AddDescriptor(unittest_pb2.TestAllTypes.DESCRIPTOR)
+ self.assertEquals(
+ 'protobuf_unittest.TestAllTypes',
+ pool.FindMessageTypeByName(
+ prefix + 'protobuf_unittest.TestAllTypes').full_name)
+
+ # AddDescriptor is not recursive.
+ with self.assertRaises(KeyError):
+ pool.FindMessageTypeByName(
+ prefix + 'protobuf_unittest.TestAllTypes.NestedMessage')
+
+ pool.AddDescriptor(unittest_pb2.TestAllTypes.NestedMessage.DESCRIPTOR)
+ self.assertEquals(
+ 'protobuf_unittest.TestAllTypes.NestedMessage',
+ pool.FindMessageTypeByName(
+ prefix + 'protobuf_unittest.TestAllTypes.NestedMessage').full_name)
+
+ # Files are implicitly also indexed when messages are added.
+ self.assertEquals(
+ 'google/protobuf/unittest.proto',
+ pool.FindFileByName(
+ 'google/protobuf/unittest.proto').name)
+
+ self.assertEquals(
+ 'google/protobuf/unittest.proto',
+ pool.FindFileContainingSymbol(
+ prefix + 'protobuf_unittest.TestAllTypes.NestedMessage').name)
+
+ def testMessage(self):
+ self._TestMessage('')
+ self._TestMessage('.')
+
+ def _TestEnum(self, prefix):
+ pool = descriptor_pool.DescriptorPool()
+ pool.AddEnumDescriptor(unittest_pb2.ForeignEnum.DESCRIPTOR)
+ self.assertEquals(
+ 'protobuf_unittest.ForeignEnum',
+ pool.FindEnumTypeByName(
+ prefix + 'protobuf_unittest.ForeignEnum').full_name)
+
+ # AddEnumDescriptor is not recursive.
+ with self.assertRaises(KeyError):
+ pool.FindEnumTypeByName(
+ prefix + 'protobuf_unittest.ForeignEnum.NestedEnum')
+
+ pool.AddEnumDescriptor(unittest_pb2.TestAllTypes.NestedEnum.DESCRIPTOR)
+ self.assertEquals(
+ 'protobuf_unittest.TestAllTypes.NestedEnum',
+ pool.FindEnumTypeByName(
+ prefix + 'protobuf_unittest.TestAllTypes.NestedEnum').full_name)
+
+ # Files are implicitly also indexed when enums are added.
+ self.assertEquals(
+ 'google/protobuf/unittest.proto',
+ pool.FindFileByName(
+ 'google/protobuf/unittest.proto').name)
+
+ self.assertEquals(
+ 'google/protobuf/unittest.proto',
+ pool.FindFileContainingSymbol(
+ prefix + 'protobuf_unittest.TestAllTypes.NestedEnum').name)
+
+ def testEnum(self):
+ self._TestEnum('')
+ self._TestEnum('.')
+
+ def testFile(self):
+ pool = descriptor_pool.DescriptorPool()
+ pool.AddFileDescriptor(unittest_pb2.DESCRIPTOR)
+ self.assertEquals(
+ 'google/protobuf/unittest.proto',
+ pool.FindFileByName(
+ 'google/protobuf/unittest.proto').name)
+
+ # AddFileDescriptor is not recursive; messages and enums within files must
+ # be explicitly registered.
+ with self.assertRaises(KeyError):
+ pool.FindFileContainingSymbol(
+ 'protobuf_unittest.TestAllTypes')
+
+
+TEST1_FILE = ProtoFile(
+ 'google/protobuf/internal/descriptor_pool_test1.proto',
+ 'google.protobuf.python.internal',
+ {
+ 'DescriptorPoolTest1': MessageType({
+ 'NestedEnum': EnumType([('ALPHA', 1), ('BETA', 2)]),
+ 'NestedMessage': MessageType({
+ 'NestedEnum': EnumType([('EPSILON', 5), ('ZETA', 6)]),
+ 'DeepNestedMessage': MessageType({
+ 'NestedEnum': EnumType([('ETA', 7), ('THETA', 8)]),
+ }, [
+ ('nested_enum', EnumField(1, 'NestedEnum', 'ETA')),
+ ('nested_field', StringField(2, 'theta')),
+ ]),
+ }, [
+ ('nested_enum', EnumField(1, 'NestedEnum', 'ZETA')),
+ ('nested_field', StringField(2, 'beta')),
+ ('deep_nested_message', MessageField(3, 'DeepNestedMessage')),
+ ])
+ }, [
+ ('nested_enum', EnumField(1, 'NestedEnum', 'BETA')),
+ ('nested_message', MessageField(2, 'NestedMessage')),
+ ], is_extendable=True),
+
+ 'DescriptorPoolTest2': MessageType({
+ 'NestedEnum': EnumType([('GAMMA', 3), ('DELTA', 4)]),
+ 'NestedMessage': MessageType({
+ 'NestedEnum': EnumType([('IOTA', 9), ('KAPPA', 10)]),
+ 'DeepNestedMessage': MessageType({
+ 'NestedEnum': EnumType([('LAMBDA', 11), ('MU', 12)]),
+ }, [
+ ('nested_enum', EnumField(1, 'NestedEnum', 'MU')),
+ ('nested_field', StringField(2, 'lambda')),
+ ]),
+ }, [
+ ('nested_enum', EnumField(1, 'NestedEnum', 'IOTA')),
+ ('nested_field', StringField(2, 'delta')),
+ ('deep_nested_message', MessageField(3, 'DeepNestedMessage')),
+ ])
+ }, [
+ ('nested_enum', EnumField(1, 'NestedEnum', 'GAMMA')),
+ ('nested_message', MessageField(2, 'NestedMessage')),
+ ]),
+ })
+
+
+TEST2_FILE = ProtoFile(
+ 'google/protobuf/internal/descriptor_pool_test2.proto',
+ 'google.protobuf.python.internal',
+ {
+ 'DescriptorPoolTest3': MessageType({
+ 'NestedEnum': EnumType([('NU', 13), ('XI', 14)]),
+ 'NestedMessage': MessageType({
+ 'NestedEnum': EnumType([('OMICRON', 15), ('PI', 16)]),
+ 'DeepNestedMessage': MessageType({
+ 'NestedEnum': EnumType([('RHO', 17), ('SIGMA', 18)]),
+ }, [
+ ('nested_enum', EnumField(1, 'NestedEnum', 'RHO')),
+ ('nested_field', StringField(2, 'sigma')),
+ ]),
+ }, [
+ ('nested_enum', EnumField(1, 'NestedEnum', 'PI')),
+ ('nested_field', StringField(2, 'nu')),
+ ('deep_nested_message', MessageField(3, 'DeepNestedMessage')),
+ ])
+ }, [
+ ('nested_enum', EnumField(1, 'NestedEnum', 'XI')),
+ ('nested_message', MessageField(2, 'NestedMessage')),
+ ], extensions=[
+ ('descriptor_pool_test',
+ ExtensionField(1001, 'DescriptorPoolTest1')),
+ ]),
+ },
+ dependencies=['google/protobuf/internal/descriptor_pool_test1.proto'])
+
+
+if __name__ == '__main__':
+ basetest.main()
diff --git a/python/google/protobuf/internal/descriptor_pool_test1.proto b/python/google/protobuf/internal/descriptor_pool_test1.proto
new file mode 100644
index 00000000..00816b78
--- /dev/null
+++ b/python/google/protobuf/internal/descriptor_pool_test1.proto
@@ -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.
+
+syntax = "proto2";
+
+package google.protobuf.python.internal;
+
+
+message DescriptorPoolTest1 {
+ extensions 1000 to max;
+
+ enum NestedEnum {
+ ALPHA = 1;
+ BETA = 2;
+ }
+
+ optional NestedEnum nested_enum = 1 [default = BETA];
+
+ message NestedMessage {
+ enum NestedEnum {
+ EPSILON = 5;
+ ZETA = 6;
+ }
+ optional NestedEnum nested_enum = 1 [default = ZETA];
+ optional string nested_field = 2 [default = "beta"];
+ optional DeepNestedMessage deep_nested_message = 3;
+
+ message DeepNestedMessage {
+ enum NestedEnum {
+ ETA = 7;
+ THETA = 8;
+ }
+ optional NestedEnum nested_enum = 1 [default = ETA];
+ optional string nested_field = 2 [default = "theta"];
+ }
+ }
+
+ optional NestedMessage nested_message = 2;
+}
+
+message DescriptorPoolTest2 {
+ enum NestedEnum {
+ GAMMA = 3;
+ DELTA = 4;
+ }
+
+ optional NestedEnum nested_enum = 1 [default = GAMMA];
+
+ message NestedMessage {
+ enum NestedEnum {
+ IOTA = 9;
+ KAPPA = 10;
+ }
+ optional NestedEnum nested_enum = 1 [default = IOTA];
+ optional string nested_field = 2 [default = "delta"];
+ optional DeepNestedMessage deep_nested_message = 3;
+
+ message DeepNestedMessage {
+ enum NestedEnum {
+ LAMBDA = 11;
+ MU = 12;
+ }
+ optional NestedEnum nested_enum = 1 [default = MU];
+ optional string nested_field = 2 [default = "lambda"];
+ }
+ }
+
+ optional NestedMessage nested_message = 2;
+}
diff --git a/python/google/protobuf/pyext/python_descriptor.h b/python/google/protobuf/internal/descriptor_pool_test2.proto
index 5232680f..e3fa660c 100644
--- a/python/google/protobuf/pyext/python_descriptor.h
+++ b/python/google/protobuf/internal/descriptor_pool_test2.proto
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -28,60 +28,45 @@
// (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)
+syntax = "proto2";
-#ifndef GOOGLE_PROTOBUF_PYTHON_DESCRIPTOR_H__
-#define GOOGLE_PROTOBUF_PYTHON_DESCRIPTOR_H__
+package google.protobuf.python.internal;
-#include <Python.h>
-#include <structmember.h>
+import "google/protobuf/internal/descriptor_pool_test1.proto";
-#include <google/protobuf/descriptor.h>
-#if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN)
-typedef int Py_ssize_t;
-#define PY_SSIZE_T_MAX INT_MAX
-#define PY_SSIZE_T_MIN INT_MIN
-#endif
+message DescriptorPoolTest3 {
-namespace google {
-namespace protobuf {
-namespace python {
+ extend DescriptorPoolTest1 {
+ optional DescriptorPoolTest3 descriptor_pool_test = 1001;
+ }
-typedef struct {
- PyObject_HEAD
+ enum NestedEnum {
+ NU = 13;
+ XI = 14;
+ }
- // The proto2 descriptor that this object represents.
- const google::protobuf::FieldDescriptor* descriptor;
+ optional NestedEnum nested_enum = 1 [default = XI];
- // Full name of the field (PyString).
- PyObject* full_name;
+ message NestedMessage {
+ enum NestedEnum {
+ OMICRON = 15;
+ PI = 16;
+ }
+ optional NestedEnum nested_enum = 1 [default = PI];
+ optional string nested_field = 2 [default = "nu"];
+ optional DeepNestedMessage deep_nested_message = 3;
- // Name of the field (PyString).
- PyObject* name;
+ message DeepNestedMessage {
+ enum NestedEnum {
+ RHO = 17;
+ SIGMA = 18;
+ }
+ optional NestedEnum nested_enum = 1 [default = RHO];
+ optional string nested_field = 2 [default = "sigma"];
+ }
+ }
- // C++ type of the field (PyLong).
- PyObject* cpp_type;
+ optional NestedMessage nested_message = 2;
+}
- // Name of the field (PyLong).
- PyObject* label;
-
- // Identity of the descriptor (PyLong used as a poiner).
- PyObject* id;
-} CFieldDescriptor;
-
-extern PyTypeObject CFieldDescriptor_Type;
-
-extern PyTypeObject CDescriptorPool_Type;
-
-
-PyObject* Python_NewCDescriptorPool(PyObject* ignored, PyObject* args);
-PyObject* Python_BuildFile(PyObject* ignored, PyObject* args);
-bool InitDescriptor();
-google::protobuf::DescriptorPool* GetDescriptorPool();
-
-} // namespace python
-} // namespace protobuf
-
-} // namespace google
-#endif // GOOGLE_PROTOBUF_PYTHON_DESCRIPTOR_H__
diff --git a/python/google/protobuf/internal/descriptor_python_test.py b/python/google/protobuf/internal/descriptor_python_test.py
new file mode 100644
index 00000000..5471ae02
--- /dev/null
+++ b/python/google/protobuf/internal/descriptor_python_test.py
@@ -0,0 +1,54 @@
+#! /usr/bin/python
+#
+# 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.
+
+"""Unittest for descriptor.py for the pure Python implementation."""
+
+import os
+os.environ['PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION'] = 'python'
+
+# We must set the implementation version above before the google3 imports.
+# pylint: disable=g-import-not-at-top
+from google.apputils import basetest
+from google.protobuf.internal import api_implementation
+# Run all tests from the original module by putting them in our namespace.
+# pylint: disable=wildcard-import
+from google.protobuf.internal.descriptor_test import *
+
+
+class ConfirmPurePythonTest(basetest.TestCase):
+
+ def testImplementationSetting(self):
+ self.assertEqual('python', api_implementation.Type())
+
+
+if __name__ == '__main__':
+ basetest.main()
diff --git a/python/google/protobuf/internal/descriptor_test.py b/python/google/protobuf/internal/descriptor_test.py
index 05c27452..50c4dbba 100755
--- a/python/google/protobuf/internal/descriptor_test.py
+++ b/python/google/protobuf/internal/descriptor_test.py
@@ -2,7 +2,7 @@
#
# Protocol Buffers - Google's data interchange format
# Copyright 2008 Google Inc. All rights reserved.
-# http://code.google.com/p/protobuf/
+# 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
@@ -34,11 +34,16 @@
__author__ = 'robinson@google.com (Will Robinson)'
-import unittest
+import sys
+
+from google.apputils import basetest
+from google.protobuf import unittest_custom_options_pb2
from google.protobuf import unittest_import_pb2
from google.protobuf import unittest_pb2
from google.protobuf import descriptor_pb2
+from google.protobuf.internal import api_implementation
from google.protobuf import descriptor
+from google.protobuf import symbol_database
from google.protobuf import text_format
@@ -47,44 +52,31 @@ name: 'TestEmptyMessage'
"""
-class DescriptorTest(unittest.TestCase):
+class DescriptorTest(basetest.TestCase):
def setUp(self):
- self.my_file = descriptor.FileDescriptor(
+ file_proto = descriptor_pb2.FileDescriptorProto(
name='some/filename/some.proto',
- package='protobuf_unittest'
- )
- self.my_enum = descriptor.EnumDescriptor(
- name='ForeignEnum',
- full_name='protobuf_unittest.ForeignEnum',
- filename=None,
- file=self.my_file,
- values=[
- descriptor.EnumValueDescriptor(name='FOREIGN_FOO', index=0, number=4),
- descriptor.EnumValueDescriptor(name='FOREIGN_BAR', index=1, number=5),
- descriptor.EnumValueDescriptor(name='FOREIGN_BAZ', index=2, number=6),
- ])
- self.my_message = descriptor.Descriptor(
- name='NestedMessage',
- full_name='protobuf_unittest.TestAllTypes.NestedMessage',
- filename=None,
- file=self.my_file,
- containing_type=None,
- fields=[
- descriptor.FieldDescriptor(
- name='bb',
- full_name='protobuf_unittest.TestAllTypes.NestedMessage.bb',
- index=0, number=1,
- type=5, cpp_type=1, label=1,
- has_default_value=False, default_value=0,
- message_type=None, enum_type=None, containing_type=None,
- is_extension=False, extension_scope=None),
- ],
- nested_types=[],
- enum_types=[
- self.my_enum,
- ],
- extensions=[])
+ package='protobuf_unittest')
+ message_proto = file_proto.message_type.add(
+ name='NestedMessage')
+ message_proto.field.add(
+ name='bb',
+ number=1,
+ type=descriptor_pb2.FieldDescriptorProto.TYPE_INT32,
+ label=descriptor_pb2.FieldDescriptorProto.LABEL_OPTIONAL)
+ enum_proto = message_proto.enum_type.add(
+ name='ForeignEnum')
+ enum_proto.value.add(name='FOREIGN_FOO', number=4)
+ enum_proto.value.add(name='FOREIGN_BAR', number=5)
+ enum_proto.value.add(name='FOREIGN_BAZ', number=6)
+
+ descriptor_pool = symbol_database.Default().pool
+ descriptor_pool.Add(file_proto)
+ self.my_file = descriptor_pool.FindFileByName(file_proto.name)
+ self.my_message = self.my_file.message_types_by_name[message_proto.name]
+ self.my_enum = self.my_message.enum_types_by_name[enum_proto.name]
+
self.my_method = descriptor.MethodDescriptor(
name='Bar',
full_name='protobuf_unittest.TestService.Bar',
@@ -101,6 +93,15 @@ class DescriptorTest(unittest.TestCase):
self.my_method
])
+ def testEnumValueName(self):
+ self.assertEqual(self.my_message.EnumValueName('ForeignEnum', 4),
+ 'FOREIGN_FOO')
+
+ self.assertEqual(
+ self.my_message.enum_types_by_name[
+ 'ForeignEnum'].values_by_number[4].name,
+ self.my_message.EnumValueName('ForeignEnum', 4))
+
def testEnumFixups(self):
self.assertEqual(self.my_enum, self.my_enum.values[0].type)
@@ -125,6 +126,262 @@ class DescriptorTest(unittest.TestCase):
self.assertEqual(self.my_service.GetOptions(),
descriptor_pb2.ServiceOptions())
+ def testSimpleCustomOptions(self):
+ file_descriptor = unittest_custom_options_pb2.DESCRIPTOR
+ message_descriptor =\
+ unittest_custom_options_pb2.TestMessageWithCustomOptions.DESCRIPTOR
+ field_descriptor = message_descriptor.fields_by_name["field1"]
+ enum_descriptor = message_descriptor.enum_types_by_name["AnEnum"]
+ enum_value_descriptor =\
+ message_descriptor.enum_values_by_name["ANENUM_VAL2"]
+ service_descriptor =\
+ unittest_custom_options_pb2.TestServiceWithCustomOptions.DESCRIPTOR
+ method_descriptor = service_descriptor.FindMethodByName("Foo")
+
+ file_options = file_descriptor.GetOptions()
+ file_opt1 = unittest_custom_options_pb2.file_opt1
+ self.assertEqual(9876543210, file_options.Extensions[file_opt1])
+ message_options = message_descriptor.GetOptions()
+ message_opt1 = unittest_custom_options_pb2.message_opt1
+ self.assertEqual(-56, message_options.Extensions[message_opt1])
+ field_options = field_descriptor.GetOptions()
+ field_opt1 = unittest_custom_options_pb2.field_opt1
+ self.assertEqual(8765432109, field_options.Extensions[field_opt1])
+ field_opt2 = unittest_custom_options_pb2.field_opt2
+ self.assertEqual(42, field_options.Extensions[field_opt2])
+ enum_options = enum_descriptor.GetOptions()
+ enum_opt1 = unittest_custom_options_pb2.enum_opt1
+ self.assertEqual(-789, enum_options.Extensions[enum_opt1])
+ enum_value_options = enum_value_descriptor.GetOptions()
+ enum_value_opt1 = unittest_custom_options_pb2.enum_value_opt1
+ self.assertEqual(123, enum_value_options.Extensions[enum_value_opt1])
+
+ service_options = service_descriptor.GetOptions()
+ service_opt1 = unittest_custom_options_pb2.service_opt1
+ self.assertEqual(-9876543210, service_options.Extensions[service_opt1])
+ method_options = method_descriptor.GetOptions()
+ method_opt1 = unittest_custom_options_pb2.method_opt1
+ self.assertEqual(unittest_custom_options_pb2.METHODOPT1_VAL2,
+ method_options.Extensions[method_opt1])
+
+ message_descriptor = (
+ unittest_custom_options_pb2.DummyMessageContainingEnum.DESCRIPTOR)
+ self.assertTrue(file_descriptor.has_options)
+ self.assertFalse(message_descriptor.has_options)
+
+ def testDifferentCustomOptionTypes(self):
+ kint32min = -2**31
+ kint64min = -2**63
+ kint32max = 2**31 - 1
+ kint64max = 2**63 - 1
+ kuint32max = 2**32 - 1
+ kuint64max = 2**64 - 1
+
+ message_descriptor =\
+ unittest_custom_options_pb2.CustomOptionMinIntegerValues.DESCRIPTOR
+ message_options = message_descriptor.GetOptions()
+ self.assertEqual(False, message_options.Extensions[
+ unittest_custom_options_pb2.bool_opt])
+ self.assertEqual(kint32min, message_options.Extensions[
+ unittest_custom_options_pb2.int32_opt])
+ self.assertEqual(kint64min, message_options.Extensions[
+ unittest_custom_options_pb2.int64_opt])
+ self.assertEqual(0, message_options.Extensions[
+ unittest_custom_options_pb2.uint32_opt])
+ self.assertEqual(0, message_options.Extensions[
+ unittest_custom_options_pb2.uint64_opt])
+ self.assertEqual(kint32min, message_options.Extensions[
+ unittest_custom_options_pb2.sint32_opt])
+ self.assertEqual(kint64min, message_options.Extensions[
+ unittest_custom_options_pb2.sint64_opt])
+ self.assertEqual(0, message_options.Extensions[
+ unittest_custom_options_pb2.fixed32_opt])
+ self.assertEqual(0, message_options.Extensions[
+ unittest_custom_options_pb2.fixed64_opt])
+ self.assertEqual(kint32min, message_options.Extensions[
+ unittest_custom_options_pb2.sfixed32_opt])
+ self.assertEqual(kint64min, message_options.Extensions[
+ unittest_custom_options_pb2.sfixed64_opt])
+
+ message_descriptor =\
+ unittest_custom_options_pb2.CustomOptionMaxIntegerValues.DESCRIPTOR
+ message_options = message_descriptor.GetOptions()
+ self.assertEqual(True, message_options.Extensions[
+ unittest_custom_options_pb2.bool_opt])
+ self.assertEqual(kint32max, message_options.Extensions[
+ unittest_custom_options_pb2.int32_opt])
+ self.assertEqual(kint64max, message_options.Extensions[
+ unittest_custom_options_pb2.int64_opt])
+ self.assertEqual(kuint32max, message_options.Extensions[
+ unittest_custom_options_pb2.uint32_opt])
+ self.assertEqual(kuint64max, message_options.Extensions[
+ unittest_custom_options_pb2.uint64_opt])
+ self.assertEqual(kint32max, message_options.Extensions[
+ unittest_custom_options_pb2.sint32_opt])
+ self.assertEqual(kint64max, message_options.Extensions[
+ unittest_custom_options_pb2.sint64_opt])
+ self.assertEqual(kuint32max, message_options.Extensions[
+ unittest_custom_options_pb2.fixed32_opt])
+ self.assertEqual(kuint64max, message_options.Extensions[
+ unittest_custom_options_pb2.fixed64_opt])
+ self.assertEqual(kint32max, message_options.Extensions[
+ unittest_custom_options_pb2.sfixed32_opt])
+ self.assertEqual(kint64max, message_options.Extensions[
+ unittest_custom_options_pb2.sfixed64_opt])
+
+ message_descriptor =\
+ unittest_custom_options_pb2.CustomOptionOtherValues.DESCRIPTOR
+ message_options = message_descriptor.GetOptions()
+ self.assertEqual(-100, message_options.Extensions[
+ unittest_custom_options_pb2.int32_opt])
+ self.assertAlmostEqual(12.3456789, message_options.Extensions[
+ unittest_custom_options_pb2.float_opt], 6)
+ self.assertAlmostEqual(1.234567890123456789, message_options.Extensions[
+ unittest_custom_options_pb2.double_opt])
+ self.assertEqual("Hello, \"World\"", message_options.Extensions[
+ unittest_custom_options_pb2.string_opt])
+ self.assertEqual(b"Hello\0World", message_options.Extensions[
+ unittest_custom_options_pb2.bytes_opt])
+ dummy_enum = unittest_custom_options_pb2.DummyMessageContainingEnum
+ self.assertEqual(
+ dummy_enum.TEST_OPTION_ENUM_TYPE2,
+ message_options.Extensions[unittest_custom_options_pb2.enum_opt])
+
+ message_descriptor =\
+ unittest_custom_options_pb2.SettingRealsFromPositiveInts.DESCRIPTOR
+ message_options = message_descriptor.GetOptions()
+ self.assertAlmostEqual(12, message_options.Extensions[
+ unittest_custom_options_pb2.float_opt], 6)
+ self.assertAlmostEqual(154, message_options.Extensions[
+ unittest_custom_options_pb2.double_opt])
+
+ message_descriptor =\
+ unittest_custom_options_pb2.SettingRealsFromNegativeInts.DESCRIPTOR
+ message_options = message_descriptor.GetOptions()
+ self.assertAlmostEqual(-12, message_options.Extensions[
+ unittest_custom_options_pb2.float_opt], 6)
+ self.assertAlmostEqual(-154, message_options.Extensions[
+ unittest_custom_options_pb2.double_opt])
+
+ def testComplexExtensionOptions(self):
+ descriptor =\
+ unittest_custom_options_pb2.VariousComplexOptions.DESCRIPTOR
+ options = descriptor.GetOptions()
+ self.assertEqual(42, options.Extensions[
+ unittest_custom_options_pb2.complex_opt1].foo)
+ self.assertEqual(324, options.Extensions[
+ unittest_custom_options_pb2.complex_opt1].Extensions[
+ unittest_custom_options_pb2.quux])
+ self.assertEqual(876, options.Extensions[
+ unittest_custom_options_pb2.complex_opt1].Extensions[
+ unittest_custom_options_pb2.corge].qux)
+ self.assertEqual(987, options.Extensions[
+ unittest_custom_options_pb2.complex_opt2].baz)
+ self.assertEqual(654, options.Extensions[
+ unittest_custom_options_pb2.complex_opt2].Extensions[
+ unittest_custom_options_pb2.grault])
+ self.assertEqual(743, options.Extensions[
+ unittest_custom_options_pb2.complex_opt2].bar.foo)
+ self.assertEqual(1999, options.Extensions[
+ unittest_custom_options_pb2.complex_opt2].bar.Extensions[
+ unittest_custom_options_pb2.quux])
+ self.assertEqual(2008, options.Extensions[
+ unittest_custom_options_pb2.complex_opt2].bar.Extensions[
+ unittest_custom_options_pb2.corge].qux)
+ self.assertEqual(741, options.Extensions[
+ unittest_custom_options_pb2.complex_opt2].Extensions[
+ unittest_custom_options_pb2.garply].foo)
+ self.assertEqual(1998, options.Extensions[
+ unittest_custom_options_pb2.complex_opt2].Extensions[
+ unittest_custom_options_pb2.garply].Extensions[
+ unittest_custom_options_pb2.quux])
+ self.assertEqual(2121, options.Extensions[
+ unittest_custom_options_pb2.complex_opt2].Extensions[
+ unittest_custom_options_pb2.garply].Extensions[
+ unittest_custom_options_pb2.corge].qux)
+ self.assertEqual(1971, options.Extensions[
+ unittest_custom_options_pb2.ComplexOptionType2
+ .ComplexOptionType4.complex_opt4].waldo)
+ self.assertEqual(321, options.Extensions[
+ unittest_custom_options_pb2.complex_opt2].fred.waldo)
+ self.assertEqual(9, options.Extensions[
+ unittest_custom_options_pb2.complex_opt3].qux)
+ self.assertEqual(22, options.Extensions[
+ unittest_custom_options_pb2.complex_opt3].complexoptiontype5.plugh)
+ self.assertEqual(24, options.Extensions[
+ unittest_custom_options_pb2.complexopt6].xyzzy)
+
+ # Check that aggregate options were parsed and saved correctly in
+ # the appropriate descriptors.
+ def testAggregateOptions(self):
+ file_descriptor = unittest_custom_options_pb2.DESCRIPTOR
+ message_descriptor =\
+ unittest_custom_options_pb2.AggregateMessage.DESCRIPTOR
+ field_descriptor = message_descriptor.fields_by_name["fieldname"]
+ enum_descriptor = unittest_custom_options_pb2.AggregateEnum.DESCRIPTOR
+ enum_value_descriptor = enum_descriptor.values_by_name["VALUE"]
+ service_descriptor =\
+ unittest_custom_options_pb2.AggregateService.DESCRIPTOR
+ method_descriptor = service_descriptor.FindMethodByName("Method")
+
+ # Tests for the different types of data embedded in fileopt
+ file_options = file_descriptor.GetOptions().Extensions[
+ unittest_custom_options_pb2.fileopt]
+ self.assertEqual(100, file_options.i)
+ self.assertEqual("FileAnnotation", file_options.s)
+ self.assertEqual("NestedFileAnnotation", file_options.sub.s)
+ self.assertEqual("FileExtensionAnnotation", file_options.file.Extensions[
+ unittest_custom_options_pb2.fileopt].s)
+ self.assertEqual("EmbeddedMessageSetElement", file_options.mset.Extensions[
+ unittest_custom_options_pb2.AggregateMessageSetElement
+ .message_set_extension].s)
+
+ # Simple tests for all the other types of annotations
+ self.assertEqual(
+ "MessageAnnotation",
+ message_descriptor.GetOptions().Extensions[
+ unittest_custom_options_pb2.msgopt].s)
+ self.assertEqual(
+ "FieldAnnotation",
+ field_descriptor.GetOptions().Extensions[
+ unittest_custom_options_pb2.fieldopt].s)
+ self.assertEqual(
+ "EnumAnnotation",
+ enum_descriptor.GetOptions().Extensions[
+ unittest_custom_options_pb2.enumopt].s)
+ self.assertEqual(
+ "EnumValueAnnotation",
+ enum_value_descriptor.GetOptions().Extensions[
+ unittest_custom_options_pb2.enumvalopt].s)
+ self.assertEqual(
+ "ServiceAnnotation",
+ service_descriptor.GetOptions().Extensions[
+ unittest_custom_options_pb2.serviceopt].s)
+ self.assertEqual(
+ "MethodAnnotation",
+ method_descriptor.GetOptions().Extensions[
+ unittest_custom_options_pb2.methodopt].s)
+
+ def testNestedOptions(self):
+ nested_message =\
+ unittest_custom_options_pb2.NestedOptionType.NestedMessage.DESCRIPTOR
+ self.assertEqual(1001, nested_message.GetOptions().Extensions[
+ unittest_custom_options_pb2.message_opt1])
+ nested_field = nested_message.fields_by_name["nested_field"]
+ self.assertEqual(1002, nested_field.GetOptions().Extensions[
+ unittest_custom_options_pb2.field_opt1])
+ outer_message =\
+ unittest_custom_options_pb2.NestedOptionType.DESCRIPTOR
+ nested_enum = outer_message.enum_types_by_name["NestedEnum"]
+ self.assertEqual(1003, nested_enum.GetOptions().Extensions[
+ unittest_custom_options_pb2.enum_opt1])
+ nested_enum_value = outer_message.enum_values_by_name["NESTED_ENUM_VALUE"]
+ self.assertEqual(1004, nested_enum_value.GetOptions().Extensions[
+ unittest_custom_options_pb2.enum_value_opt1])
+ nested_extension = outer_message.extensions_by_name["nested_extension"]
+ self.assertEqual(1005, nested_extension.GetOptions().Extensions[
+ unittest_custom_options_pb2.field_opt2])
+
def testFileDescriptorReferences(self):
self.assertEqual(self.my_enum.file, self.my_file)
self.assertEqual(self.my_message.file, self.my_file)
@@ -133,8 +390,110 @@ class DescriptorTest(unittest.TestCase):
self.assertEqual(self.my_file.name, 'some/filename/some.proto')
self.assertEqual(self.my_file.package, 'protobuf_unittest')
-
-class DescriptorCopyToProtoTest(unittest.TestCase):
+ @basetest.unittest.skipIf(
+ api_implementation.Type() != 'cpp' or api_implementation.Version() != 2,
+ 'Immutability of descriptors is only enforced in v2 implementation')
+ def testImmutableCppDescriptor(self):
+ message_descriptor = unittest_pb2.TestAllTypes.DESCRIPTOR
+ with self.assertRaises(AttributeError):
+ message_descriptor.fields_by_name = None
+ with self.assertRaises(TypeError):
+ message_descriptor.fields_by_name['Another'] = None
+ with self.assertRaises(TypeError):
+ message_descriptor.fields.append(None)
+
+
+class GeneratedDescriptorTest(basetest.TestCase):
+ """Tests for the properties of descriptors in generated code."""
+
+ def CheckMessageDescriptor(self, message_descriptor):
+ # Basic properties
+ self.assertEqual(message_descriptor.name, 'TestAllTypes')
+ self.assertEqual(message_descriptor.full_name,
+ 'protobuf_unittest.TestAllTypes')
+ # Test equality and hashability
+ self.assertEqual(message_descriptor, message_descriptor)
+ self.assertEqual(message_descriptor.fields[0].containing_type,
+ message_descriptor)
+ self.assertIn(message_descriptor, [message_descriptor])
+ self.assertIn(message_descriptor, {message_descriptor: None})
+ # Test field containers
+ self.CheckDescriptorSequence(message_descriptor.fields)
+ self.CheckDescriptorMapping(message_descriptor.fields_by_name)
+ self.CheckDescriptorMapping(message_descriptor.fields_by_number)
+
+ def CheckFieldDescriptor(self, field_descriptor):
+ # Basic properties
+ self.assertEqual(field_descriptor.name, 'optional_int32')
+ self.assertEqual(field_descriptor.full_name,
+ 'protobuf_unittest.TestAllTypes.optional_int32')
+ self.assertEqual(field_descriptor.containing_type.name, 'TestAllTypes')
+ # Test equality and hashability
+ self.assertEqual(field_descriptor, field_descriptor)
+ self.assertEqual(
+ field_descriptor.containing_type.fields_by_name['optional_int32'],
+ field_descriptor)
+ self.assertIn(field_descriptor, [field_descriptor])
+ self.assertIn(field_descriptor, {field_descriptor: None})
+
+ def CheckDescriptorSequence(self, sequence):
+ # Verifies that a property like 'messageDescriptor.fields' has all the
+ # properties of an immutable abc.Sequence.
+ self.assertGreater(len(sequence), 0) # Sized
+ self.assertEqual(len(sequence), len(list(sequence))) # Iterable
+ item = sequence[0]
+ self.assertEqual(item, sequence[0])
+ self.assertIn(item, sequence) # Container
+ self.assertEqual(sequence.index(item), 0)
+ self.assertEqual(sequence.count(item), 1)
+ reversed_iterator = reversed(sequence)
+ self.assertEqual(list(reversed_iterator), list(sequence)[::-1])
+ self.assertRaises(StopIteration, next, reversed_iterator)
+
+ def CheckDescriptorMapping(self, mapping):
+ # Verifies that a property like 'messageDescriptor.fields' has all the
+ # properties of an immutable abc.Mapping.
+ self.assertGreater(len(mapping), 0) # Sized
+ self.assertEqual(len(mapping), len(list(mapping))) # Iterable
+ if sys.version_info.major >= 3:
+ key, item = next(iter(mapping.items()))
+ else:
+ key, item = mapping.items()[0]
+ self.assertIn(key, mapping) # Container
+ self.assertEqual(mapping.get(key), item)
+ # keys(), iterkeys() &co
+ item = (next(iter(mapping.keys())), next(iter(mapping.values())))
+ self.assertEqual(item, next(iter(mapping.items())))
+ if sys.version_info.major < 3:
+ def CheckItems(seq, iterator):
+ self.assertEqual(next(iterator), seq[0])
+ self.assertEqual(list(iterator), seq[1:])
+ CheckItems(mapping.keys(), mapping.iterkeys())
+ CheckItems(mapping.values(), mapping.itervalues())
+ CheckItems(mapping.items(), mapping.iteritems())
+
+ def testDescriptor(self):
+ message_descriptor = unittest_pb2.TestAllTypes.DESCRIPTOR
+ self.CheckMessageDescriptor(message_descriptor)
+ field_descriptor = message_descriptor.fields_by_name['optional_int32']
+ self.CheckFieldDescriptor(field_descriptor)
+
+ def testCppDescriptorContainer(self):
+ # Check that the collection is still valid even if the parent disappeared.
+ enum = unittest_pb2.TestAllTypes.DESCRIPTOR.enum_types_by_name['NestedEnum']
+ values = enum.values
+ del enum
+ self.assertEqual('FOO', values[0].name)
+
+ def testCppDescriptorContainer_Iterator(self):
+ # Same test with the iterator
+ enum = unittest_pb2.TestAllTypes.DESCRIPTOR.enum_types_by_name['NestedEnum']
+ values_iter = iter(enum.values)
+ del enum
+ self.assertEqual('FOO', next(values_iter).name)
+
+
+class DescriptorCopyToProtoTest(basetest.TestCase):
"""Tests for CopyTo functions of Descriptor."""
def _AssertProtoEqual(self, actual_proto, expected_class, expected_ascii):
@@ -269,45 +628,49 @@ class DescriptorCopyToProtoTest(unittest.TestCase):
descriptor_pb2.DescriptorProto,
TEST_MESSAGE_WITH_SEVERAL_EXTENSIONS_ASCII)
- def testCopyToProto_FileDescriptor(self):
- UNITTEST_IMPORT_FILE_DESCRIPTOR_ASCII = ("""
- name: 'google/protobuf/unittest_import.proto'
- package: 'protobuf_unittest_import'
- message_type: <
- name: 'ImportMessage'
- field: <
- name: 'd'
- number: 1
- label: 1 # Optional
- type: 5 # TYPE_INT32
- >
- >
- """ +
- """enum_type: <
- name: 'ImportEnum'
- value: <
- name: 'IMPORT_FOO'
- number: 7
- >
- value: <
- name: 'IMPORT_BAR'
- number: 8
- >
- value: <
- name: 'IMPORT_BAZ'
- number: 9
- >
- >
- options: <
- java_package: 'com.google.protobuf.test'
- optimize_for: 1 # SPEED
- >
- """)
-
- self._InternalTestCopyToProto(
- unittest_import_pb2.DESCRIPTOR,
- descriptor_pb2.FileDescriptorProto,
- UNITTEST_IMPORT_FILE_DESCRIPTOR_ASCII)
+ # Disable this test so we can make changes to the proto file.
+ # TODO(xiaofeng): Enable this test after cl/55530659 is submitted.
+ #
+ # def testCopyToProto_FileDescriptor(self):
+ # UNITTEST_IMPORT_FILE_DESCRIPTOR_ASCII = ("""
+ # name: 'google/protobuf/unittest_import.proto'
+ # package: 'protobuf_unittest_import'
+ # dependency: 'google/protobuf/unittest_import_public.proto'
+ # message_type: <
+ # name: 'ImportMessage'
+ # field: <
+ # name: 'd'
+ # number: 1
+ # label: 1 # Optional
+ # type: 5 # TYPE_INT32
+ # >
+ # >
+ # """ +
+ # """enum_type: <
+ # name: 'ImportEnum'
+ # value: <
+ # name: 'IMPORT_FOO'
+ # number: 7
+ # >
+ # value: <
+ # name: 'IMPORT_BAR'
+ # number: 8
+ # >
+ # value: <
+ # name: 'IMPORT_BAZ'
+ # number: 9
+ # >
+ # >
+ # options: <
+ # java_package: 'com.google.protobuf.test'
+ # optimize_for: 1 # SPEED
+ # >
+ # public_dependency: 0
+ # """)
+ # self._InternalTestCopyToProto(
+ # unittest_import_pb2.DESCRIPTOR,
+ # descriptor_pb2.FileDescriptorProto,
+ # UNITTEST_IMPORT_FILE_DESCRIPTOR_ASCII)
def testCopyToProto_ServiceDescriptor(self):
TEST_SERVICE_ASCII = """
@@ -323,12 +686,94 @@ class DescriptorCopyToProtoTest(unittest.TestCase):
output_type: '.protobuf_unittest.BarResponse'
>
"""
-
- self._InternalTestCopyToProto(
- unittest_pb2.TestService.DESCRIPTOR,
- descriptor_pb2.ServiceDescriptorProto,
- TEST_SERVICE_ASCII)
-
+ # TODO(rocking): enable this test after the proto descriptor change is
+ # checked in.
+ #self._InternalTestCopyToProto(
+ # unittest_pb2.TestService.DESCRIPTOR,
+ # descriptor_pb2.ServiceDescriptorProto,
+ # TEST_SERVICE_ASCII)
+
+
+class MakeDescriptorTest(basetest.TestCase):
+
+ def testMakeDescriptorWithNestedFields(self):
+ file_descriptor_proto = descriptor_pb2.FileDescriptorProto()
+ file_descriptor_proto.name = 'Foo2'
+ message_type = file_descriptor_proto.message_type.add()
+ message_type.name = file_descriptor_proto.name
+ nested_type = message_type.nested_type.add()
+ nested_type.name = 'Sub'
+ enum_type = nested_type.enum_type.add()
+ enum_type.name = 'FOO'
+ enum_type_val = enum_type.value.add()
+ enum_type_val.name = 'BAR'
+ enum_type_val.number = 3
+ field = message_type.field.add()
+ field.number = 1
+ field.name = 'uint64_field'
+ field.label = descriptor.FieldDescriptor.LABEL_REQUIRED
+ field.type = descriptor.FieldDescriptor.TYPE_UINT64
+ field = message_type.field.add()
+ field.number = 2
+ field.name = 'nested_message_field'
+ field.label = descriptor.FieldDescriptor.LABEL_REQUIRED
+ field.type = descriptor.FieldDescriptor.TYPE_MESSAGE
+ field.type_name = 'Sub'
+ enum_field = nested_type.field.add()
+ enum_field.number = 2
+ enum_field.name = 'bar_field'
+ enum_field.label = descriptor.FieldDescriptor.LABEL_REQUIRED
+ enum_field.type = descriptor.FieldDescriptor.TYPE_ENUM
+ enum_field.type_name = 'Foo2.Sub.FOO'
+
+ result = descriptor.MakeDescriptor(message_type)
+ self.assertEqual(result.fields[0].cpp_type,
+ descriptor.FieldDescriptor.CPPTYPE_UINT64)
+ self.assertEqual(result.fields[1].cpp_type,
+ descriptor.FieldDescriptor.CPPTYPE_MESSAGE)
+ self.assertEqual(result.fields[1].message_type.containing_type,
+ result)
+ self.assertEqual(result.nested_types[0].fields[0].full_name,
+ 'Foo2.Sub.bar_field')
+ self.assertEqual(result.nested_types[0].fields[0].enum_type,
+ result.nested_types[0].enum_types[0])
+
+ def testMakeDescriptorWithUnsignedIntField(self):
+ file_descriptor_proto = descriptor_pb2.FileDescriptorProto()
+ file_descriptor_proto.name = 'Foo'
+ message_type = file_descriptor_proto.message_type.add()
+ message_type.name = file_descriptor_proto.name
+ enum_type = message_type.enum_type.add()
+ enum_type.name = 'FOO'
+ enum_type_val = enum_type.value.add()
+ enum_type_val.name = 'BAR'
+ enum_type_val.number = 3
+ field = message_type.field.add()
+ field.number = 1
+ field.name = 'uint64_field'
+ field.label = descriptor.FieldDescriptor.LABEL_REQUIRED
+ field.type = descriptor.FieldDescriptor.TYPE_UINT64
+ enum_field = message_type.field.add()
+ enum_field.number = 2
+ enum_field.name = 'bar_field'
+ enum_field.label = descriptor.FieldDescriptor.LABEL_REQUIRED
+ enum_field.type = descriptor.FieldDescriptor.TYPE_ENUM
+ enum_field.type_name = 'Foo.FOO'
+
+ result = descriptor.MakeDescriptor(message_type)
+ self.assertEqual(result.fields[0].cpp_type,
+ descriptor.FieldDescriptor.CPPTYPE_UINT64)
+
+
+ def testMakeDescriptorWithOptions(self):
+ descriptor_proto = descriptor_pb2.DescriptorProto()
+ aggregate_message = unittest_custom_options_pb2.AggregateMessage
+ aggregate_message.DESCRIPTOR.CopyToProto(descriptor_proto)
+ reformed_descriptor = descriptor.MakeDescriptor(descriptor_proto)
+
+ options = reformed_descriptor.GetOptions()
+ self.assertEquals(101,
+ options.Extensions[unittest_custom_options_pb2.msgopt].i)
if __name__ == '__main__':
- unittest.main()
+ basetest.main()
diff --git a/python/google/protobuf/internal/encoder.py b/python/google/protobuf/internal/encoder.py
index 777975e8..38a5138a 100755
--- a/python/google/protobuf/internal/encoder.py
+++ b/python/google/protobuf/internal/encoder.py
@@ -1,6 +1,6 @@
# Protocol Buffers - Google's data interchange format
# Copyright 2008 Google Inc. All rights reserved.
-# http://code.google.com/p/protobuf/
+# 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
@@ -28,6 +28,10 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#PY25 compatible for GAE.
+#
+# Copyright 2009 Google Inc. All Rights Reserved.
+
"""Code for encoding protocol message primitives.
Contains the logic for encoding every logical protocol field type
@@ -67,6 +71,8 @@ sizer rather than when calling them. In particular:
__author__ = 'kenton@google.com (Kenton Varda)'
import struct
+import sys ##PY25
+_PY2 = sys.version_info[0] < 3 ##PY25
from google.protobuf.internal import wire_format
@@ -340,7 +346,8 @@ def MessageSetItemSizer(field_number):
def _VarintEncoder():
"""Return an encoder for a basic varint value (does not include tag)."""
- local_chr = chr
+ local_chr = _PY2 and chr or (lambda x: bytes((x,))) ##PY25
+##!PY25 local_chr = chr if bytes is str else lambda x: bytes((x,))
def EncodeVarint(write, value):
bits = value & 0x7f
value >>= 7
@@ -357,7 +364,8 @@ def _SignedVarintEncoder():
"""Return an encoder for a basic signed varint value (does not include
tag)."""
- local_chr = chr
+ local_chr = _PY2 and chr or (lambda x: bytes((x,))) ##PY25
+##!PY25 local_chr = chr if bytes is str else lambda x: bytes((x,))
def EncodeSignedVarint(write, value):
if value < 0:
value += (1 << 64)
@@ -382,7 +390,8 @@ def _VarintBytes(value):
pieces = []
_EncodeVarint(pieces.append, value)
- return "".join(pieces)
+ return "".encode("latin1").join(pieces) ##PY25
+##!PY25 return b"".join(pieces)
def TagBytes(field_number, wire_type):
@@ -520,26 +529,33 @@ def _FloatingPointEncoder(wire_type, format):
format: The format string to pass to struct.pack().
"""
+ b = _PY2 and (lambda x:x) or (lambda x:x.encode('latin1')) ##PY25
value_size = struct.calcsize(format)
if value_size == 4:
def EncodeNonFiniteOrRaise(write, value):
# Remember that the serialized form uses little-endian byte order.
if value == _POS_INF:
- write('\x00\x00\x80\x7F')
+ write(b('\x00\x00\x80\x7F')) ##PY25
+##!PY25 write(b'\x00\x00\x80\x7F')
elif value == _NEG_INF:
- write('\x00\x00\x80\xFF')
+ write(b('\x00\x00\x80\xFF')) ##PY25
+##!PY25 write(b'\x00\x00\x80\xFF')
elif value != value: # NaN
- write('\x00\x00\xC0\x7F')
+ write(b('\x00\x00\xC0\x7F')) ##PY25
+##!PY25 write(b'\x00\x00\xC0\x7F')
else:
raise
elif value_size == 8:
def EncodeNonFiniteOrRaise(write, value):
if value == _POS_INF:
- write('\x00\x00\x00\x00\x00\x00\xF0\x7F')
+ write(b('\x00\x00\x00\x00\x00\x00\xF0\x7F')) ##PY25
+##!PY25 write(b'\x00\x00\x00\x00\x00\x00\xF0\x7F')
elif value == _NEG_INF:
- write('\x00\x00\x00\x00\x00\x00\xF0\xFF')
+ write(b('\x00\x00\x00\x00\x00\x00\xF0\xFF')) ##PY25
+##!PY25 write(b'\x00\x00\x00\x00\x00\x00\xF0\xFF')
elif value != value: # NaN
- write('\x00\x00\x00\x00\x00\x00\xF8\x7F')
+ write(b('\x00\x00\x00\x00\x00\x00\xF8\x7F')) ##PY25
+##!PY25 write(b'\x00\x00\x00\x00\x00\x00\xF8\x7F')
else:
raise
else:
@@ -615,8 +631,10 @@ DoubleEncoder = _FloatingPointEncoder(wire_format.WIRETYPE_FIXED64, '<d')
def BoolEncoder(field_number, is_repeated, is_packed):
"""Returns an encoder for a boolean field."""
- false_byte = chr(0)
- true_byte = chr(1)
+##!PY25 false_byte = b'\x00'
+##!PY25 true_byte = b'\x01'
+ false_byte = '\x00'.encode('latin1') ##PY25
+ true_byte = '\x01'.encode('latin1') ##PY25
if is_packed:
tag_bytes = TagBytes(field_number, wire_format.WIRETYPE_LENGTH_DELIMITED)
local_EncodeVarint = _EncodeVarint
@@ -752,7 +770,8 @@ def MessageSetItemEncoder(field_number):
}
}
"""
- start_bytes = "".join([
+ start_bytes = "".encode("latin1").join([ ##PY25
+##!PY25 start_bytes = b"".join([
TagBytes(1, wire_format.WIRETYPE_START_GROUP),
TagBytes(2, wire_format.WIRETYPE_VARINT),
_VarintBytes(field_number),
diff --git a/python/google/protobuf/internal/enum_type_wrapper.py b/python/google/protobuf/internal/enum_type_wrapper.py
new file mode 100644
index 00000000..1cffe352
--- /dev/null
+++ b/python/google/protobuf/internal/enum_type_wrapper.py
@@ -0,0 +1,89 @@
+# 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.
+
+"""A simple wrapper around enum types to expose utility functions.
+
+Instances are created as properties with the same name as the enum they wrap
+on proto classes. For usage, see:
+ reflection_test.py
+"""
+
+__author__ = 'rabsatt@google.com (Kevin Rabsatt)'
+
+
+class EnumTypeWrapper(object):
+ """A utility for finding the names of enum values."""
+
+ DESCRIPTOR = None
+
+ def __init__(self, enum_type):
+ """Inits EnumTypeWrapper with an EnumDescriptor."""
+ self._enum_type = enum_type
+ self.DESCRIPTOR = enum_type;
+
+ def Name(self, number):
+ """Returns a string containing the name of an enum value."""
+ if number in self._enum_type.values_by_number:
+ return self._enum_type.values_by_number[number].name
+ raise ValueError('Enum %s has no name defined for value %d' % (
+ self._enum_type.name, number))
+
+ def Value(self, name):
+ """Returns the value coresponding to the given enum name."""
+ if name in self._enum_type.values_by_name:
+ return self._enum_type.values_by_name[name].number
+ raise ValueError('Enum %s has no value defined for name %s' % (
+ self._enum_type.name, name))
+
+ def keys(self):
+ """Return a list of the string names in the enum.
+
+ These are returned in the order they were defined in the .proto file.
+ """
+
+ return [value_descriptor.name
+ for value_descriptor in self._enum_type.values]
+
+ def values(self):
+ """Return a list of the integer values in the enum.
+
+ These are returned in the order they were defined in the .proto file.
+ """
+
+ return [value_descriptor.number
+ for value_descriptor in self._enum_type.values]
+
+ def items(self):
+ """Return a list of the (name, value) pairs of the enum.
+
+ These are returned in the order they were defined in the .proto file.
+ """
+ return [(value_descriptor.name, value_descriptor.number)
+ for value_descriptor in self._enum_type.values]
diff --git a/python/google/protobuf/pyext/python_protobuf.cc b/python/google/protobuf/internal/factory_test1.proto
index 1b1ab5d1..d2fbbeec 100644
--- a/python/google/protobuf/pyext/python_protobuf.cc
+++ b/python/google/protobuf/internal/factory_test1.proto
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -28,36 +28,31 @@
// (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)
+// Author: matthewtoia@google.com (Matt Toia)
-#include <google/protobuf/pyext/python_protobuf.h>
+syntax = "proto2";
-namespace google {
-namespace protobuf {
-namespace python {
+package google.protobuf.python.internal;
-static const Message* GetCProtoInsidePyProtoStub(PyObject* msg) {
- return NULL;
-}
-static Message* MutableCProtoInsidePyProtoStub(PyObject* msg) {
- return NULL;
+
+enum Factory1Enum {
+ FACTORY_1_VALUE_0 = 0;
+ FACTORY_1_VALUE_1 = 1;
}
-// This is initialized with a default, stub implementation.
-// If python-google.protobuf.cc is loaded, the function pointer is overridden
-// with a full implementation.
-const Message* (*GetCProtoInsidePyProtoPtr)(PyObject* msg) =
- GetCProtoInsidePyProtoStub;
-Message* (*MutableCProtoInsidePyProtoPtr)(PyObject* msg) =
- MutableCProtoInsidePyProtoStub;
+message Factory1Message {
+ optional Factory1Enum factory_1_enum = 1;
+ enum NestedFactory1Enum {
+ NESTED_FACTORY_1_VALUE_0 = 0;
+ NESTED_FACTORY_1_VALUE_1 = 1;
+ }
+ optional NestedFactory1Enum nested_factory_1_enum = 2;
+ message NestedFactory1Message {
+ optional string value = 1;
+ }
+ optional NestedFactory1Message nested_factory_1_message = 3;
+ optional int32 scalar_value = 4;
+ repeated string list_value = 5;
-const Message* GetCProtoInsidePyProto(PyObject* msg) {
- return GetCProtoInsidePyProtoPtr(msg);
+ extensions 1000 to max;
}
-Message* MutableCProtoInsidePyProto(PyObject* msg) {
- return MutableCProtoInsidePyProtoPtr(msg);
-}
-
-} // namespace python
-} // namespace protobuf
-} // namespace google
diff --git a/python/google/protobuf/internal/factory_test2.proto b/python/google/protobuf/internal/factory_test2.proto
new file mode 100644
index 00000000..bb1b54ad
--- /dev/null
+++ b/python/google/protobuf/internal/factory_test2.proto
@@ -0,0 +1,99 @@
+// 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: matthewtoia@google.com (Matt Toia)
+
+syntax = "proto2";
+
+package google.protobuf.python.internal;
+
+import "google/protobuf/internal/factory_test1.proto";
+
+
+enum Factory2Enum {
+ FACTORY_2_VALUE_0 = 0;
+ FACTORY_2_VALUE_1 = 1;
+}
+
+message Factory2Message {
+ required int32 mandatory = 1;
+ optional Factory2Enum factory_2_enum = 2;
+ enum NestedFactory2Enum {
+ NESTED_FACTORY_2_VALUE_0 = 0;
+ NESTED_FACTORY_2_VALUE_1 = 1;
+ }
+ optional NestedFactory2Enum nested_factory_2_enum = 3;
+ message NestedFactory2Message {
+ optional string value = 1;
+ }
+ optional NestedFactory2Message nested_factory_2_message = 4;
+ optional Factory1Message factory_1_message = 5;
+ optional Factory1Enum factory_1_enum = 6;
+ optional Factory1Message.NestedFactory1Enum nested_factory_1_enum = 7;
+ optional Factory1Message.NestedFactory1Message nested_factory_1_message = 8;
+ optional Factory2Message circular_message = 9;
+ optional string scalar_value = 10;
+ repeated string list_value = 11;
+ repeated group Grouped = 12 {
+ optional string part_1 = 13;
+ optional string part_2 = 14;
+ }
+ optional LoopMessage loop = 15;
+ optional int32 int_with_default = 16 [default = 1776];
+ optional double double_with_default = 17 [default = 9.99];
+ optional string string_with_default = 18 [default = "hello world"];
+ optional bool bool_with_default = 19 [default = false];
+ optional Factory2Enum enum_with_default = 20 [default = FACTORY_2_VALUE_1];
+ optional bytes bytes_with_default = 21 [default = "a\373\000c"];
+
+
+ extend Factory1Message {
+ optional string one_more_field = 1001;
+ }
+
+ oneof oneof_field {
+ int32 oneof_int = 22;
+ string oneof_string = 23;
+ }
+}
+
+message LoopMessage {
+ optional Factory2Message loop = 1;
+}
+
+message MessageWithNestedEnumOnly {
+ enum NestedEnum {
+ NESTED_MESSAGE_ENUM_0 = 0;
+ }
+}
+
+extend Factory1Message {
+ optional string another_field = 1002;
+}
diff --git a/python/google/protobuf/internal/generator_test.py b/python/google/protobuf/internal/generator_test.py
index b3f7d9b1..03361e66 100755
--- a/python/google/protobuf/internal/generator_test.py
+++ b/python/google/protobuf/internal/generator_test.py
@@ -2,7 +2,7 @@
#
# Protocol Buffers - Google's data interchange format
# Copyright 2008 Google Inc. All rights reserved.
-# http://code.google.com/p/protobuf/
+# 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
@@ -35,24 +35,27 @@
# indirect testing of the protocol compiler output.
"""Unittest that directly tests the output of the pure-Python protocol
-compiler. See //google/protobuf/reflection_test.py for a test which
+compiler. See //google/protobuf/internal/reflection_test.py for a test which
further ensures that we can use Python protocol message objects as we expect.
"""
__author__ = 'robinson@google.com (Will Robinson)'
-import unittest
+from google.apputils import basetest
+from google.protobuf.internal import test_bad_identifiers_pb2
from google.protobuf import unittest_custom_options_pb2
from google.protobuf import unittest_import_pb2
+from google.protobuf import unittest_import_public_pb2
from google.protobuf import unittest_mset_pb2
-from google.protobuf import unittest_pb2
from google.protobuf import unittest_no_generic_services_pb2
+from google.protobuf import unittest_pb2
from google.protobuf import service
+from google.protobuf import symbol_database
MAX_EXTENSION = 536870912
-class GeneratorTest(unittest.TestCase):
+class GeneratorTest(basetest.TestCase):
def testNestedMessageDescriptor(self):
field_name = 'optional_nested_message'
@@ -146,7 +149,7 @@ class GeneratorTest(unittest.TestCase):
proto = unittest_custom_options_pb2.TestMessageWithCustomOptions()
enum_options = proto.DESCRIPTOR.enum_types_by_name['AnEnum'].GetOptions()
self.assertTrue(enum_options is not None)
- # TODO(gps): We really should test for the presense of the enum_opt1
+ # TODO(gps): We really should test for the presence of the enum_opt1
# extension and for its value to be set to -789.
def testNestedTypes(self):
@@ -215,6 +218,10 @@ class GeneratorTest(unittest.TestCase):
'google/protobuf/unittest.proto')
self.assertEqual(unittest_pb2.DESCRIPTOR.package, 'protobuf_unittest')
self.assertFalse(unittest_pb2.DESCRIPTOR.serialized_pb is None)
+ self.assertEqual(unittest_pb2.DESCRIPTOR.dependencies,
+ [unittest_import_pb2.DESCRIPTOR])
+ self.assertEqual(unittest_import_pb2.DESCRIPTOR.dependencies,
+ [unittest_import_public_pb2.DESCRIPTOR])
def testNoGenericServices(self):
self.assertTrue(hasattr(unittest_no_generic_services_pb2, "TestMessage"))
@@ -239,6 +246,98 @@ class GeneratorTest(unittest.TestCase):
unittest_pb2._TESTALLTYPES_NESTEDMESSAGE.name in
file_type.message_types_by_name)
+ def testEnumTypesByName(self):
+ file_type = unittest_pb2.DESCRIPTOR
+ self.assertEqual(
+ unittest_pb2._FOREIGNENUM,
+ file_type.enum_types_by_name[unittest_pb2._FOREIGNENUM.name])
+
+ def testExtensionsByName(self):
+ file_type = unittest_pb2.DESCRIPTOR
+ self.assertEqual(
+ unittest_pb2.my_extension_string,
+ file_type.extensions_by_name[unittest_pb2.my_extension_string.name])
+
+ def testPublicImports(self):
+ # Test public imports as embedded message.
+ all_type_proto = unittest_pb2.TestAllTypes()
+ self.assertEqual(0, all_type_proto.optional_public_import_message.e)
+
+ # PublicImportMessage is actually defined in unittest_import_public_pb2
+ # module, and is public imported by unittest_import_pb2 module.
+ public_import_proto = unittest_import_pb2.PublicImportMessage()
+ self.assertEqual(0, public_import_proto.e)
+ self.assertTrue(unittest_import_public_pb2.PublicImportMessage is
+ unittest_import_pb2.PublicImportMessage)
+
+ def testBadIdentifiers(self):
+ # We're just testing that the code was imported without problems.
+ message = test_bad_identifiers_pb2.TestBadIdentifiers()
+ self.assertEqual(message.Extensions[test_bad_identifiers_pb2.message],
+ "foo")
+ self.assertEqual(message.Extensions[test_bad_identifiers_pb2.descriptor],
+ "bar")
+ self.assertEqual(message.Extensions[test_bad_identifiers_pb2.reflection],
+ "baz")
+ self.assertEqual(message.Extensions[test_bad_identifiers_pb2.service],
+ "qux")
+
+ def testOneof(self):
+ desc = unittest_pb2.TestAllTypes.DESCRIPTOR
+ self.assertEqual(1, len(desc.oneofs))
+ self.assertEqual('oneof_field', desc.oneofs[0].name)
+ self.assertEqual(0, desc.oneofs[0].index)
+ self.assertIs(desc, desc.oneofs[0].containing_type)
+ self.assertIs(desc.oneofs[0], desc.oneofs_by_name['oneof_field'])
+ nested_names = set(['oneof_uint32', 'oneof_nested_message',
+ 'oneof_string', 'oneof_bytes'])
+ self.assertSameElements(
+ nested_names,
+ [field.name for field in desc.oneofs[0].fields])
+ for field_name, field_desc in desc.fields_by_name.iteritems():
+ if field_name in nested_names:
+ self.assertIs(desc.oneofs[0], field_desc.containing_oneof)
+ else:
+ self.assertIsNone(field_desc.containing_oneof)
+
+
+class SymbolDatabaseRegistrationTest(basetest.TestCase):
+ """Checks that messages, enums and files are correctly registered."""
+
+ def testGetSymbol(self):
+ self.assertEquals(
+ unittest_pb2.TestAllTypes, symbol_database.Default().GetSymbol(
+ 'protobuf_unittest.TestAllTypes'))
+ self.assertEquals(
+ unittest_pb2.TestAllTypes.NestedMessage,
+ symbol_database.Default().GetSymbol(
+ 'protobuf_unittest.TestAllTypes.NestedMessage'))
+ with self.assertRaises(KeyError):
+ symbol_database.Default().GetSymbol('protobuf_unittest.NestedMessage')
+ self.assertEquals(
+ unittest_pb2.TestAllTypes.OptionalGroup,
+ symbol_database.Default().GetSymbol(
+ 'protobuf_unittest.TestAllTypes.OptionalGroup'))
+ self.assertEquals(
+ unittest_pb2.TestAllTypes.RepeatedGroup,
+ symbol_database.Default().GetSymbol(
+ 'protobuf_unittest.TestAllTypes.RepeatedGroup'))
+
+ def testEnums(self):
+ self.assertEquals(
+ 'protobuf_unittest.ForeignEnum',
+ symbol_database.Default().pool.FindEnumTypeByName(
+ 'protobuf_unittest.ForeignEnum').full_name)
+ self.assertEquals(
+ 'protobuf_unittest.TestAllTypes.NestedEnum',
+ symbol_database.Default().pool.FindEnumTypeByName(
+ 'protobuf_unittest.TestAllTypes.NestedEnum').full_name)
+
+ def testFindFileByName(self):
+ self.assertEquals(
+ 'google/protobuf/unittest.proto',
+ symbol_database.Default().pool.FindFileByName(
+ 'google/protobuf/unittest.proto').name)
if __name__ == '__main__':
- unittest.main()
+ basetest.main()
diff --git a/python/google/protobuf/internal/import_test_package/__init__.py b/python/google/protobuf/internal/import_test_package/__init__.py
new file mode 100644
index 00000000..5121dd0e
--- /dev/null
+++ b/python/google/protobuf/internal/import_test_package/__init__.py
@@ -0,0 +1,33 @@
+# 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.
+
+"""Sample module importing a nested proto from itself."""
+
+from google.protobuf.internal.import_test_package import outer_pb2 as myproto
diff --git a/python/google/protobuf/internal/import_test_package/inner.proto b/python/google/protobuf/internal/import_test_package/inner.proto
new file mode 100644
index 00000000..2887c123
--- /dev/null
+++ b/python/google/protobuf/internal/import_test_package/inner.proto
@@ -0,0 +1,37 @@
+// 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";
+
+package google.protobuf.python.internal.import_test_package;
+
+message Inner {
+ optional int32 value = 1 [default = 57];
+}
diff --git a/python/google/protobuf/internal/import_test_package/outer.proto b/python/google/protobuf/internal/import_test_package/outer.proto
new file mode 100644
index 00000000..a27fb5c8
--- /dev/null
+++ b/python/google/protobuf/internal/import_test_package/outer.proto
@@ -0,0 +1,39 @@
+// 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";
+
+package google.protobuf.python.internal.import_test_package;
+
+import "google/protobuf/internal/import_test_package/inner.proto";
+
+message Outer {
+ optional Inner inner = 1;
+}
diff --git a/python/google/protobuf/internal/message_factory_python_test.py b/python/google/protobuf/internal/message_factory_python_test.py
new file mode 100644
index 00000000..85e02b25
--- /dev/null
+++ b/python/google/protobuf/internal/message_factory_python_test.py
@@ -0,0 +1,54 @@
+#! /usr/bin/python
+#
+# 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.
+
+"""Tests for ..public.message_factory for the pure Python implementation."""
+
+import os
+os.environ['PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION'] = 'python'
+
+# We must set the implementation version above before the google3 imports.
+# pylint: disable=g-import-not-at-top
+from google.apputils import basetest
+from google.protobuf.internal import api_implementation
+# Run all tests from the original module by putting them in our namespace.
+# pylint: disable=wildcard-import
+from google.protobuf.internal.message_factory_test import *
+
+
+class ConfirmPurePythonTest(basetest.TestCase):
+
+ def testImplementationSetting(self):
+ self.assertEqual('python', api_implementation.Type())
+
+
+if __name__ == '__main__':
+ basetest.main()
diff --git a/python/google/protobuf/internal/message_factory_test.py b/python/google/protobuf/internal/message_factory_test.py
new file mode 100644
index 00000000..fcf13410
--- /dev/null
+++ b/python/google/protobuf/internal/message_factory_test.py
@@ -0,0 +1,131 @@
+#! /usr/bin/python
+#
+# 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.
+
+"""Tests for google.protobuf.message_factory."""
+
+__author__ = 'matthewtoia@google.com (Matt Toia)'
+
+from google.apputils import basetest
+from google.protobuf import descriptor_pb2
+from google.protobuf.internal import factory_test1_pb2
+from google.protobuf.internal import factory_test2_pb2
+from google.protobuf import descriptor_database
+from google.protobuf import descriptor_pool
+from google.protobuf import message_factory
+
+
+class MessageFactoryTest(basetest.TestCase):
+
+ def setUp(self):
+ self.factory_test1_fd = descriptor_pb2.FileDescriptorProto.FromString(
+ factory_test1_pb2.DESCRIPTOR.serialized_pb)
+ self.factory_test2_fd = descriptor_pb2.FileDescriptorProto.FromString(
+ factory_test2_pb2.DESCRIPTOR.serialized_pb)
+
+ def _ExerciseDynamicClass(self, cls):
+ msg = cls()
+ msg.mandatory = 42
+ msg.nested_factory_2_enum = 0
+ msg.nested_factory_2_message.value = 'nested message value'
+ msg.factory_1_message.factory_1_enum = 1
+ msg.factory_1_message.nested_factory_1_enum = 0
+ msg.factory_1_message.nested_factory_1_message.value = (
+ 'nested message value')
+ msg.factory_1_message.scalar_value = 22
+ msg.factory_1_message.list_value.extend([u'one', u'two', u'three'])
+ msg.factory_1_message.list_value.append(u'four')
+ msg.factory_1_enum = 1
+ msg.nested_factory_1_enum = 0
+ msg.nested_factory_1_message.value = 'nested message value'
+ msg.circular_message.mandatory = 1
+ msg.circular_message.circular_message.mandatory = 2
+ msg.circular_message.scalar_value = 'one deep'
+ msg.scalar_value = 'zero deep'
+ msg.list_value.extend([u'four', u'three', u'two'])
+ msg.list_value.append(u'one')
+ msg.grouped.add()
+ msg.grouped[0].part_1 = 'hello'
+ msg.grouped[0].part_2 = 'world'
+ msg.grouped.add(part_1='testing', part_2='123')
+ msg.loop.loop.mandatory = 2
+ msg.loop.loop.loop.loop.mandatory = 4
+ serialized = msg.SerializeToString()
+ converted = factory_test2_pb2.Factory2Message.FromString(serialized)
+ reserialized = converted.SerializeToString()
+ self.assertEquals(serialized, reserialized)
+ result = cls.FromString(reserialized)
+ self.assertEquals(msg, result)
+
+ def testGetPrototype(self):
+ db = descriptor_database.DescriptorDatabase()
+ pool = descriptor_pool.DescriptorPool(db)
+ db.Add(self.factory_test1_fd)
+ db.Add(self.factory_test2_fd)
+ factory = message_factory.MessageFactory()
+ cls = factory.GetPrototype(pool.FindMessageTypeByName(
+ 'google.protobuf.python.internal.Factory2Message'))
+ self.assertIsNot(cls, factory_test2_pb2.Factory2Message)
+ self._ExerciseDynamicClass(cls)
+ cls2 = factory.GetPrototype(pool.FindMessageTypeByName(
+ 'google.protobuf.python.internal.Factory2Message'))
+ self.assertIs(cls, cls2)
+
+ def testGetMessages(self):
+ # performed twice because multiple calls with the same input must be allowed
+ for _ in range(2):
+ messages = message_factory.GetMessages([self.factory_test2_fd,
+ self.factory_test1_fd])
+ self.assertContainsSubset(
+ ['google.protobuf.python.internal.Factory2Message',
+ 'google.protobuf.python.internal.Factory1Message'],
+ messages.keys())
+ self._ExerciseDynamicClass(
+ messages['google.protobuf.python.internal.Factory2Message'])
+ self.assertContainsSubset(
+ ['google.protobuf.python.internal.Factory2Message.one_more_field',
+ 'google.protobuf.python.internal.another_field'],
+ (messages['google.protobuf.python.internal.Factory1Message']
+ ._extensions_by_name.keys()))
+ factory_msg1 = messages['google.protobuf.python.internal.Factory1Message']
+ msg1 = messages['google.protobuf.python.internal.Factory1Message']()
+ ext1 = factory_msg1._extensions_by_name[
+ 'google.protobuf.python.internal.Factory2Message.one_more_field']
+ ext2 = factory_msg1._extensions_by_name[
+ 'google.protobuf.python.internal.another_field']
+ msg1.Extensions[ext1] = 'test1'
+ msg1.Extensions[ext2] = 'test2'
+ self.assertEquals('test1', msg1.Extensions[ext1])
+ self.assertEquals('test2', msg1.Extensions[ext2])
+
+
+if __name__ == '__main__':
+ basetest.main()
diff --git a/python/google/protobuf/internal/message_listener.py b/python/google/protobuf/internal/message_listener.py
index 1080234d..0fc255a7 100755
--- a/python/google/protobuf/internal/message_listener.py
+++ b/python/google/protobuf/internal/message_listener.py
@@ -1,6 +1,6 @@
# Protocol Buffers - Google's data interchange format
# Copyright 2008 Google Inc. All rights reserved.
-# http://code.google.com/p/protobuf/
+# 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
diff --git a/python/google/protobuf/internal/message_python_test.py b/python/google/protobuf/internal/message_python_test.py
new file mode 100644
index 00000000..c40623a8
--- /dev/null
+++ b/python/google/protobuf/internal/message_python_test.py
@@ -0,0 +1,54 @@
+#! /usr/bin/python
+#
+# 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.
+
+"""Tests for ..public.message for the pure Python implementation."""
+
+import os
+os.environ['PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION'] = 'python'
+
+# We must set the implementation version above before the google3 imports.
+# pylint: disable=g-import-not-at-top
+from google.apputils import basetest
+from google.protobuf.internal import api_implementation
+# Run all tests from the original module by putting them in our namespace.
+# pylint: disable=wildcard-import
+from google.protobuf.internal.message_test import *
+
+
+class ConfirmPurePythonTest(basetest.TestCase):
+
+ def testImplementationSetting(self):
+ self.assertEqual('python', api_implementation.Type())
+
+
+if __name__ == '__main__':
+ basetest.main()
diff --git a/python/google/protobuf/internal/message_test.py b/python/google/protobuf/internal/message_test.py
index 65174373..ed1298af 100755
--- a/python/google/protobuf/internal/message_test.py
+++ b/python/google/protobuf/internal/message_test.py
@@ -2,7 +2,7 @@
#
# Protocol Buffers - Google's data interchange format
# Copyright 2008 Google Inc. All rights reserved.
-# http://code.google.com/p/protobuf/
+# 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
@@ -45,10 +45,18 @@ __author__ = 'gps@google.com (Gregory P. Smith)'
import copy
import math
+import operator
+import pickle
+import sys
import unittest
-from google.protobuf import unittest_import_pb2
+
+from google.apputils import basetest
+from google.protobuf.internal import _parameterized
from google.protobuf import unittest_pb2
+from google.protobuf import unittest_proto3_arena_pb2
+from google.protobuf.internal import api_implementation
from google.protobuf.internal import test_util
+from google.protobuf import message
# Python pre-2.6 does not have isinf() or isnan() functions, so we have
# to provide our own.
@@ -63,118 +71,144 @@ def IsPosInf(val):
def IsNegInf(val):
return isinf(val) and (val < 0)
-class MessageTest(unittest.TestCase):
- def testGoldenMessage(self):
- golden_data = test_util.GoldenFile('golden_message').read()
- golden_message = unittest_pb2.TestAllTypes()
- golden_message.ParseFromString(golden_data)
- test_util.ExpectAllFieldsSet(self, golden_message)
- self.assertTrue(golden_message.SerializeToString() == golden_data)
- golden_copy = copy.deepcopy(golden_message)
- self.assertTrue(golden_copy.SerializeToString() == golden_data)
+@_parameterized.Parameters(
+ (unittest_pb2),
+ (unittest_proto3_arena_pb2))
+class MessageTest(basetest.TestCase):
- def testGoldenExtensions(self):
- golden_data = test_util.GoldenFile('golden_message').read()
- golden_message = unittest_pb2.TestAllExtensions()
+ def testBadUtf8String(self, message_module):
+ if api_implementation.Type() != 'python':
+ self.skipTest("Skipping testBadUtf8String, currently only the python "
+ "api implementation raises UnicodeDecodeError when a "
+ "string field contains bad utf-8.")
+ bad_utf8_data = test_util.GoldenFileData('bad_utf8_string')
+ with self.assertRaises(UnicodeDecodeError) as context:
+ message_module.TestAllTypes.FromString(bad_utf8_data)
+ self.assertIn('TestAllTypes.optional_string', str(context.exception))
+
+ def testGoldenMessage(self, message_module):
+ # Proto3 doesn't have the "default_foo" members or foreign enums,
+ # and doesn't preserve unknown fields, so for proto3 we use a golden
+ # message that doesn't have these fields set.
+ if message_module is unittest_pb2:
+ golden_data = test_util.GoldenFileData(
+ 'golden_message_oneof_implemented')
+ else:
+ golden_data = test_util.GoldenFileData('golden_message_proto3')
+
+ golden_message = message_module.TestAllTypes()
golden_message.ParseFromString(golden_data)
- all_set = unittest_pb2.TestAllExtensions()
- test_util.SetAllExtensions(all_set)
- self.assertEquals(all_set, golden_message)
- self.assertTrue(golden_message.SerializeToString() == golden_data)
+ if message_module is unittest_pb2:
+ test_util.ExpectAllFieldsSet(self, golden_message)
+ self.assertEqual(golden_data, golden_message.SerializeToString())
golden_copy = copy.deepcopy(golden_message)
- self.assertTrue(golden_copy.SerializeToString() == golden_data)
+ self.assertEqual(golden_data, golden_copy.SerializeToString())
- def testGoldenPackedMessage(self):
- golden_data = test_util.GoldenFile('golden_packed_fields_message').read()
- golden_message = unittest_pb2.TestPackedTypes()
+ def testGoldenPackedMessage(self, message_module):
+ golden_data = test_util.GoldenFileData('golden_packed_fields_message')
+ golden_message = message_module.TestPackedTypes()
golden_message.ParseFromString(golden_data)
- all_set = unittest_pb2.TestPackedTypes()
+ all_set = message_module.TestPackedTypes()
test_util.SetAllPackedFields(all_set)
- self.assertEquals(all_set, golden_message)
- self.assertTrue(all_set.SerializeToString() == golden_data)
+ self.assertEqual(all_set, golden_message)
+ self.assertEqual(golden_data, all_set.SerializeToString())
golden_copy = copy.deepcopy(golden_message)
- self.assertTrue(golden_copy.SerializeToString() == golden_data)
+ self.assertEqual(golden_data, golden_copy.SerializeToString())
- def testGoldenPackedExtensions(self):
- golden_data = test_util.GoldenFile('golden_packed_fields_message').read()
- golden_message = unittest_pb2.TestPackedExtensions()
+ def testPickleSupport(self, message_module):
+ golden_data = test_util.GoldenFileData('golden_message')
+ golden_message = message_module.TestAllTypes()
golden_message.ParseFromString(golden_data)
- all_set = unittest_pb2.TestPackedExtensions()
- test_util.SetAllPackedExtensions(all_set)
- self.assertEquals(all_set, golden_message)
- self.assertTrue(all_set.SerializeToString() == golden_data)
- golden_copy = copy.deepcopy(golden_message)
- self.assertTrue(golden_copy.SerializeToString() == golden_data)
-
- def testPositiveInfinity(self):
- golden_data = ('\x5D\x00\x00\x80\x7F'
- '\x61\x00\x00\x00\x00\x00\x00\xF0\x7F'
- '\xCD\x02\x00\x00\x80\x7F'
- '\xD1\x02\x00\x00\x00\x00\x00\x00\xF0\x7F')
- golden_message = unittest_pb2.TestAllTypes()
+ pickled_message = pickle.dumps(golden_message)
+
+ unpickled_message = pickle.loads(pickled_message)
+ self.assertEqual(unpickled_message, golden_message)
+
+ def testPositiveInfinity(self, message_module):
+ golden_data = (b'\x5D\x00\x00\x80\x7F'
+ b'\x61\x00\x00\x00\x00\x00\x00\xF0\x7F'
+ b'\xCD\x02\x00\x00\x80\x7F'
+ b'\xD1\x02\x00\x00\x00\x00\x00\x00\xF0\x7F')
+ golden_message = message_module.TestAllTypes()
golden_message.ParseFromString(golden_data)
self.assertTrue(IsPosInf(golden_message.optional_float))
self.assertTrue(IsPosInf(golden_message.optional_double))
self.assertTrue(IsPosInf(golden_message.repeated_float[0]))
self.assertTrue(IsPosInf(golden_message.repeated_double[0]))
- self.assertTrue(golden_message.SerializeToString() == golden_data)
-
- def testNegativeInfinity(self):
- golden_data = ('\x5D\x00\x00\x80\xFF'
- '\x61\x00\x00\x00\x00\x00\x00\xF0\xFF'
- '\xCD\x02\x00\x00\x80\xFF'
- '\xD1\x02\x00\x00\x00\x00\x00\x00\xF0\xFF')
- golden_message = unittest_pb2.TestAllTypes()
+ self.assertEqual(golden_data, golden_message.SerializeToString())
+
+ def testNegativeInfinity(self, message_module):
+ golden_data = (b'\x5D\x00\x00\x80\xFF'
+ b'\x61\x00\x00\x00\x00\x00\x00\xF0\xFF'
+ b'\xCD\x02\x00\x00\x80\xFF'
+ b'\xD1\x02\x00\x00\x00\x00\x00\x00\xF0\xFF')
+ golden_message = message_module.TestAllTypes()
golden_message.ParseFromString(golden_data)
self.assertTrue(IsNegInf(golden_message.optional_float))
self.assertTrue(IsNegInf(golden_message.optional_double))
self.assertTrue(IsNegInf(golden_message.repeated_float[0]))
self.assertTrue(IsNegInf(golden_message.repeated_double[0]))
- self.assertTrue(golden_message.SerializeToString() == golden_data)
-
- def testNotANumber(self):
- golden_data = ('\x5D\x00\x00\xC0\x7F'
- '\x61\x00\x00\x00\x00\x00\x00\xF8\x7F'
- '\xCD\x02\x00\x00\xC0\x7F'
- '\xD1\x02\x00\x00\x00\x00\x00\x00\xF8\x7F')
- golden_message = unittest_pb2.TestAllTypes()
+ self.assertEqual(golden_data, golden_message.SerializeToString())
+
+ def testNotANumber(self, message_module):
+ golden_data = (b'\x5D\x00\x00\xC0\x7F'
+ b'\x61\x00\x00\x00\x00\x00\x00\xF8\x7F'
+ b'\xCD\x02\x00\x00\xC0\x7F'
+ b'\xD1\x02\x00\x00\x00\x00\x00\x00\xF8\x7F')
+ golden_message = message_module.TestAllTypes()
golden_message.ParseFromString(golden_data)
self.assertTrue(isnan(golden_message.optional_float))
self.assertTrue(isnan(golden_message.optional_double))
self.assertTrue(isnan(golden_message.repeated_float[0]))
self.assertTrue(isnan(golden_message.repeated_double[0]))
- self.assertTrue(golden_message.SerializeToString() == golden_data)
- def testPositiveInfinityPacked(self):
- golden_data = ('\xA2\x06\x04\x00\x00\x80\x7F'
- '\xAA\x06\x08\x00\x00\x00\x00\x00\x00\xF0\x7F')
- golden_message = unittest_pb2.TestPackedTypes()
+ # The protocol buffer may serialize to any one of multiple different
+ # representations of a NaN. Rather than verify a specific representation,
+ # verify the serialized string can be converted into a correctly
+ # behaving protocol buffer.
+ serialized = golden_message.SerializeToString()
+ message = message_module.TestAllTypes()
+ message.ParseFromString(serialized)
+ self.assertTrue(isnan(message.optional_float))
+ self.assertTrue(isnan(message.optional_double))
+ self.assertTrue(isnan(message.repeated_float[0]))
+ self.assertTrue(isnan(message.repeated_double[0]))
+
+ def testPositiveInfinityPacked(self, message_module):
+ golden_data = (b'\xA2\x06\x04\x00\x00\x80\x7F'
+ b'\xAA\x06\x08\x00\x00\x00\x00\x00\x00\xF0\x7F')
+ golden_message = message_module.TestPackedTypes()
golden_message.ParseFromString(golden_data)
self.assertTrue(IsPosInf(golden_message.packed_float[0]))
self.assertTrue(IsPosInf(golden_message.packed_double[0]))
- self.assertTrue(golden_message.SerializeToString() == golden_data)
+ self.assertEqual(golden_data, golden_message.SerializeToString())
- def testNegativeInfinityPacked(self):
- golden_data = ('\xA2\x06\x04\x00\x00\x80\xFF'
- '\xAA\x06\x08\x00\x00\x00\x00\x00\x00\xF0\xFF')
- golden_message = unittest_pb2.TestPackedTypes()
+ def testNegativeInfinityPacked(self, message_module):
+ golden_data = (b'\xA2\x06\x04\x00\x00\x80\xFF'
+ b'\xAA\x06\x08\x00\x00\x00\x00\x00\x00\xF0\xFF')
+ golden_message = message_module.TestPackedTypes()
golden_message.ParseFromString(golden_data)
self.assertTrue(IsNegInf(golden_message.packed_float[0]))
self.assertTrue(IsNegInf(golden_message.packed_double[0]))
- self.assertTrue(golden_message.SerializeToString() == golden_data)
+ self.assertEqual(golden_data, golden_message.SerializeToString())
- def testNotANumberPacked(self):
- golden_data = ('\xA2\x06\x04\x00\x00\xC0\x7F'
- '\xAA\x06\x08\x00\x00\x00\x00\x00\x00\xF8\x7F')
- golden_message = unittest_pb2.TestPackedTypes()
+ def testNotANumberPacked(self, message_module):
+ golden_data = (b'\xA2\x06\x04\x00\x00\xC0\x7F'
+ b'\xAA\x06\x08\x00\x00\x00\x00\x00\x00\xF8\x7F')
+ golden_message = message_module.TestPackedTypes()
golden_message.ParseFromString(golden_data)
self.assertTrue(isnan(golden_message.packed_float[0]))
self.assertTrue(isnan(golden_message.packed_double[0]))
- self.assertTrue(golden_message.SerializeToString() == golden_data)
- def testExtremeFloatValues(self):
- message = unittest_pb2.TestAllTypes()
+ serialized = golden_message.SerializeToString()
+ message = message_module.TestPackedTypes()
+ message.ParseFromString(serialized)
+ self.assertTrue(isnan(message.packed_float[0]))
+ self.assertTrue(isnan(message.packed_double[0]))
+
+ def testExtremeFloatValues(self, message_module):
+ message = message_module.TestAllTypes()
# Most positive exponent, no significand bits set.
kMostPosExponentNoSigBits = math.pow(2, 127)
@@ -218,8 +252,8 @@ class MessageTest(unittest.TestCase):
message.ParseFromString(message.SerializeToString())
self.assertTrue(message.optional_float == -kMostNegExponentOneSigBit)
- def testExtremeFloatValues(self):
- message = unittest_pb2.TestAllTypes()
+ def testExtremeDoubleValues(self, message_module):
+ message = message_module.TestAllTypes()
# Most positive exponent, no significand bits set.
kMostPosExponentNoSigBits = math.pow(2, 1023)
@@ -263,9 +297,43 @@ class MessageTest(unittest.TestCase):
message.ParseFromString(message.SerializeToString())
self.assertTrue(message.optional_double == -kMostNegExponentOneSigBit)
- def testSortingRepeatedScalarFieldsDefaultComparator(self):
+ def testFloatPrinting(self, message_module):
+ message = message_module.TestAllTypes()
+ message.optional_float = 2.0
+ self.assertEqual(str(message), 'optional_float: 2.0\n')
+
+ def testHighPrecisionFloatPrinting(self, message_module):
+ message = message_module.TestAllTypes()
+ message.optional_double = 0.12345678912345678
+ if sys.version_info.major >= 3:
+ self.assertEqual(str(message), 'optional_double: 0.12345678912345678\n')
+ else:
+ self.assertEqual(str(message), 'optional_double: 0.123456789123\n')
+
+ def testUnknownFieldPrinting(self, message_module):
+ populated = message_module.TestAllTypes()
+ test_util.SetAllNonLazyFields(populated)
+ empty = message_module.TestEmptyMessage()
+ empty.ParseFromString(populated.SerializeToString())
+ self.assertEqual(str(empty), '')
+
+ def testRepeatedNestedFieldIteration(self, message_module):
+ msg = message_module.TestAllTypes()
+ msg.repeated_nested_message.add(bb=1)
+ msg.repeated_nested_message.add(bb=2)
+ msg.repeated_nested_message.add(bb=3)
+ msg.repeated_nested_message.add(bb=4)
+
+ self.assertEqual([1, 2, 3, 4],
+ [m.bb for m in msg.repeated_nested_message])
+ self.assertEqual([4, 3, 2, 1],
+ [m.bb for m in reversed(msg.repeated_nested_message)])
+ self.assertEqual([4, 3, 2, 1],
+ [m.bb for m in msg.repeated_nested_message[::-1]])
+
+ def testSortingRepeatedScalarFieldsDefaultComparator(self, message_module):
"""Check some different types with the default comparator."""
- message = unittest_pb2.TestAllTypes()
+ message = message_module.TestAllTypes()
# TODO(mattp): would testing more scalar types strengthen test?
message.repeated_int32.append(1)
@@ -292,22 +360,22 @@ class MessageTest(unittest.TestCase):
self.assertEqual(message.repeated_string[1], 'b')
self.assertEqual(message.repeated_string[2], 'c')
- message.repeated_bytes.append('a')
- message.repeated_bytes.append('c')
- message.repeated_bytes.append('b')
+ message.repeated_bytes.append(b'a')
+ message.repeated_bytes.append(b'c')
+ message.repeated_bytes.append(b'b')
message.repeated_bytes.sort()
- self.assertEqual(message.repeated_bytes[0], 'a')
- self.assertEqual(message.repeated_bytes[1], 'b')
- self.assertEqual(message.repeated_bytes[2], 'c')
+ self.assertEqual(message.repeated_bytes[0], b'a')
+ self.assertEqual(message.repeated_bytes[1], b'b')
+ self.assertEqual(message.repeated_bytes[2], b'c')
- def testSortingRepeatedScalarFieldsCustomComparator(self):
+ def testSortingRepeatedScalarFieldsCustomComparator(self, message_module):
"""Check some different types with custom comparator."""
- message = unittest_pb2.TestAllTypes()
+ message = message_module.TestAllTypes()
message.repeated_int32.append(-3)
message.repeated_int32.append(-2)
message.repeated_int32.append(-1)
- message.repeated_int32.sort(lambda x,y: cmp(abs(x), abs(y)))
+ message.repeated_int32.sort(key=abs)
self.assertEqual(message.repeated_int32[0], -1)
self.assertEqual(message.repeated_int32[1], -2)
self.assertEqual(message.repeated_int32[2], -3)
@@ -315,14 +383,14 @@ class MessageTest(unittest.TestCase):
message.repeated_string.append('aaa')
message.repeated_string.append('bb')
message.repeated_string.append('c')
- message.repeated_string.sort(lambda x,y: cmp(len(x), len(y)))
+ message.repeated_string.sort(key=len)
self.assertEqual(message.repeated_string[0], 'c')
self.assertEqual(message.repeated_string[1], 'bb')
self.assertEqual(message.repeated_string[2], 'aaa')
- def testSortingRepeatedCompositeFieldsCustomComparator(self):
+ def testSortingRepeatedCompositeFieldsCustomComparator(self, message_module):
"""Check passing a custom comparator to sort a repeated composite field."""
- message = unittest_pb2.TestAllTypes()
+ message = message_module.TestAllTypes()
message.repeated_nested_message.add().bb = 1
message.repeated_nested_message.add().bb = 3
@@ -330,7 +398,7 @@ class MessageTest(unittest.TestCase):
message.repeated_nested_message.add().bb = 6
message.repeated_nested_message.add().bb = 5
message.repeated_nested_message.add().bb = 4
- message.repeated_nested_message.sort(lambda x,y: cmp(x.bb, y.bb))
+ message.repeated_nested_message.sort(key=operator.attrgetter('bb'))
self.assertEqual(message.repeated_nested_message[0].bb, 1)
self.assertEqual(message.repeated_nested_message[1].bb, 2)
self.assertEqual(message.repeated_nested_message[2].bb, 3)
@@ -338,6 +406,736 @@ class MessageTest(unittest.TestCase):
self.assertEqual(message.repeated_nested_message[4].bb, 5)
self.assertEqual(message.repeated_nested_message[5].bb, 6)
+ def testRepeatedCompositeFieldSortArguments(self, message_module):
+ """Check sorting a repeated composite field using list.sort() arguments."""
+ message = message_module.TestAllTypes()
+
+ get_bb = operator.attrgetter('bb')
+ cmp_bb = lambda a, b: cmp(a.bb, b.bb)
+ message.repeated_nested_message.add().bb = 1
+ message.repeated_nested_message.add().bb = 3
+ message.repeated_nested_message.add().bb = 2
+ message.repeated_nested_message.add().bb = 6
+ message.repeated_nested_message.add().bb = 5
+ message.repeated_nested_message.add().bb = 4
+ message.repeated_nested_message.sort(key=get_bb)
+ self.assertEqual([k.bb for k in message.repeated_nested_message],
+ [1, 2, 3, 4, 5, 6])
+ message.repeated_nested_message.sort(key=get_bb, reverse=True)
+ self.assertEqual([k.bb for k in message.repeated_nested_message],
+ [6, 5, 4, 3, 2, 1])
+ if sys.version_info.major >= 3: return # No cmp sorting in PY3.
+ message.repeated_nested_message.sort(sort_function=cmp_bb)
+ self.assertEqual([k.bb for k in message.repeated_nested_message],
+ [1, 2, 3, 4, 5, 6])
+ message.repeated_nested_message.sort(cmp=cmp_bb, reverse=True)
+ self.assertEqual([k.bb for k in message.repeated_nested_message],
+ [6, 5, 4, 3, 2, 1])
+
+ def testRepeatedScalarFieldSortArguments(self, message_module):
+ """Check sorting a scalar field using list.sort() arguments."""
+ message = message_module.TestAllTypes()
+
+ message.repeated_int32.append(-3)
+ message.repeated_int32.append(-2)
+ message.repeated_int32.append(-1)
+ message.repeated_int32.sort(key=abs)
+ self.assertEqual(list(message.repeated_int32), [-1, -2, -3])
+ message.repeated_int32.sort(key=abs, reverse=True)
+ self.assertEqual(list(message.repeated_int32), [-3, -2, -1])
+ if sys.version_info.major < 3: # No cmp sorting in PY3.
+ abs_cmp = lambda a, b: cmp(abs(a), abs(b))
+ message.repeated_int32.sort(sort_function=abs_cmp)
+ self.assertEqual(list(message.repeated_int32), [-1, -2, -3])
+ message.repeated_int32.sort(cmp=abs_cmp, reverse=True)
+ self.assertEqual(list(message.repeated_int32), [-3, -2, -1])
+
+ message.repeated_string.append('aaa')
+ message.repeated_string.append('bb')
+ message.repeated_string.append('c')
+ message.repeated_string.sort(key=len)
+ self.assertEqual(list(message.repeated_string), ['c', 'bb', 'aaa'])
+ message.repeated_string.sort(key=len, reverse=True)
+ self.assertEqual(list(message.repeated_string), ['aaa', 'bb', 'c'])
+ if sys.version_info.major < 3: # No cmp sorting in PY3.
+ len_cmp = lambda a, b: cmp(len(a), len(b))
+ message.repeated_string.sort(sort_function=len_cmp)
+ self.assertEqual(list(message.repeated_string), ['c', 'bb', 'aaa'])
+ message.repeated_string.sort(cmp=len_cmp, reverse=True)
+ self.assertEqual(list(message.repeated_string), ['aaa', 'bb', 'c'])
+
+ def testRepeatedFieldsComparable(self, message_module):
+ m1 = message_module.TestAllTypes()
+ m2 = message_module.TestAllTypes()
+ m1.repeated_int32.append(0)
+ m1.repeated_int32.append(1)
+ m1.repeated_int32.append(2)
+ m2.repeated_int32.append(0)
+ m2.repeated_int32.append(1)
+ m2.repeated_int32.append(2)
+ m1.repeated_nested_message.add().bb = 1
+ m1.repeated_nested_message.add().bb = 2
+ m1.repeated_nested_message.add().bb = 3
+ m2.repeated_nested_message.add().bb = 1
+ m2.repeated_nested_message.add().bb = 2
+ m2.repeated_nested_message.add().bb = 3
+
+ if sys.version_info.major >= 3: return # No cmp() in PY3.
+
+ # These comparisons should not raise errors.
+ _ = m1 < m2
+ _ = m1.repeated_nested_message < m2.repeated_nested_message
+
+ # Make sure cmp always works. If it wasn't defined, these would be
+ # id() comparisons and would all fail.
+ self.assertEqual(cmp(m1, m2), 0)
+ self.assertEqual(cmp(m1.repeated_int32, m2.repeated_int32), 0)
+ self.assertEqual(cmp(m1.repeated_int32, [0, 1, 2]), 0)
+ self.assertEqual(cmp(m1.repeated_nested_message,
+ m2.repeated_nested_message), 0)
+ with self.assertRaises(TypeError):
+ # Can't compare repeated composite containers to lists.
+ cmp(m1.repeated_nested_message, m2.repeated_nested_message[:])
+
+ # TODO(anuraag): Implement extensiondict comparison in C++ and then add test
+
+ def ensureNestedMessageExists(self, msg, attribute):
+ """Make sure that a nested message object exists.
+
+ As soon as a nested message attribute is accessed, it will be present in the
+ _fields dict, without being marked as actually being set.
+ """
+ getattr(msg, attribute)
+ self.assertFalse(msg.HasField(attribute))
+
+ def testOneofGetCaseNonexistingField(self, message_module):
+ m = message_module.TestAllTypes()
+ self.assertRaises(ValueError, m.WhichOneof, 'no_such_oneof_field')
+
+ def testOneofDefaultValues(self, message_module):
+ m = message_module.TestAllTypes()
+ self.assertIs(None, m.WhichOneof('oneof_field'))
+ self.assertFalse(m.HasField('oneof_uint32'))
+
+ # Oneof is set even when setting it to a default value.
+ m.oneof_uint32 = 0
+ self.assertEqual('oneof_uint32', m.WhichOneof('oneof_field'))
+ self.assertTrue(m.HasField('oneof_uint32'))
+ self.assertFalse(m.HasField('oneof_string'))
+
+ m.oneof_string = ""
+ self.assertEqual('oneof_string', m.WhichOneof('oneof_field'))
+ self.assertTrue(m.HasField('oneof_string'))
+ self.assertFalse(m.HasField('oneof_uint32'))
+
+ def testOneofSemantics(self, message_module):
+ m = message_module.TestAllTypes()
+ self.assertIs(None, m.WhichOneof('oneof_field'))
+
+ m.oneof_uint32 = 11
+ self.assertEqual('oneof_uint32', m.WhichOneof('oneof_field'))
+ self.assertTrue(m.HasField('oneof_uint32'))
+
+ m.oneof_string = u'foo'
+ self.assertEqual('oneof_string', m.WhichOneof('oneof_field'))
+ self.assertFalse(m.HasField('oneof_uint32'))
+ self.assertTrue(m.HasField('oneof_string'))
+
+ m.oneof_nested_message.bb = 11
+ self.assertEqual('oneof_nested_message', m.WhichOneof('oneof_field'))
+ self.assertFalse(m.HasField('oneof_string'))
+ self.assertTrue(m.HasField('oneof_nested_message'))
+
+ m.oneof_bytes = b'bb'
+ self.assertEqual('oneof_bytes', m.WhichOneof('oneof_field'))
+ self.assertFalse(m.HasField('oneof_nested_message'))
+ self.assertTrue(m.HasField('oneof_bytes'))
+
+ def testOneofCompositeFieldReadAccess(self, message_module):
+ m = message_module.TestAllTypes()
+ m.oneof_uint32 = 11
+
+ self.ensureNestedMessageExists(m, 'oneof_nested_message')
+ self.assertEqual('oneof_uint32', m.WhichOneof('oneof_field'))
+ self.assertEqual(11, m.oneof_uint32)
+
+ def testOneofWhichOneof(self, message_module):
+ m = message_module.TestAllTypes()
+ self.assertIs(None, m.WhichOneof('oneof_field'))
+ if message_module is unittest_pb2:
+ self.assertFalse(m.HasField('oneof_field'))
+
+ m.oneof_uint32 = 11
+ self.assertEqual('oneof_uint32', m.WhichOneof('oneof_field'))
+ if message_module is unittest_pb2:
+ self.assertTrue(m.HasField('oneof_field'))
+
+ m.oneof_bytes = b'bb'
+ self.assertEqual('oneof_bytes', m.WhichOneof('oneof_field'))
+
+ m.ClearField('oneof_bytes')
+ self.assertIs(None, m.WhichOneof('oneof_field'))
+ if message_module is unittest_pb2:
+ self.assertFalse(m.HasField('oneof_field'))
+
+ def testOneofClearField(self, message_module):
+ m = message_module.TestAllTypes()
+ m.oneof_uint32 = 11
+ m.ClearField('oneof_field')
+ if message_module is unittest_pb2:
+ self.assertFalse(m.HasField('oneof_field'))
+ self.assertFalse(m.HasField('oneof_uint32'))
+ self.assertIs(None, m.WhichOneof('oneof_field'))
+
+ def testOneofClearSetField(self, message_module):
+ m = message_module.TestAllTypes()
+ m.oneof_uint32 = 11
+ m.ClearField('oneof_uint32')
+ if message_module is unittest_pb2:
+ self.assertFalse(m.HasField('oneof_field'))
+ self.assertFalse(m.HasField('oneof_uint32'))
+ self.assertIs(None, m.WhichOneof('oneof_field'))
+
+ def testOneofClearUnsetField(self, message_module):
+ m = message_module.TestAllTypes()
+ m.oneof_uint32 = 11
+ self.ensureNestedMessageExists(m, 'oneof_nested_message')
+ m.ClearField('oneof_nested_message')
+ self.assertEqual(11, m.oneof_uint32)
+ if message_module is unittest_pb2:
+ self.assertTrue(m.HasField('oneof_field'))
+ self.assertTrue(m.HasField('oneof_uint32'))
+ self.assertEqual('oneof_uint32', m.WhichOneof('oneof_field'))
+
+ def testOneofDeserialize(self, message_module):
+ m = message_module.TestAllTypes()
+ m.oneof_uint32 = 11
+ m2 = message_module.TestAllTypes()
+ m2.ParseFromString(m.SerializeToString())
+ self.assertEqual('oneof_uint32', m2.WhichOneof('oneof_field'))
+
+ def testOneofCopyFrom(self, message_module):
+ m = message_module.TestAllTypes()
+ m.oneof_uint32 = 11
+ m2 = message_module.TestAllTypes()
+ m2.CopyFrom(m)
+ self.assertEqual('oneof_uint32', m2.WhichOneof('oneof_field'))
+
+ def testOneofNestedMergeFrom(self, message_module):
+ m = message_module.NestedTestAllTypes()
+ m.payload.oneof_uint32 = 11
+ m2 = message_module.NestedTestAllTypes()
+ m2.payload.oneof_bytes = b'bb'
+ m2.child.payload.oneof_bytes = b'bb'
+ m2.MergeFrom(m)
+ self.assertEqual('oneof_uint32', m2.payload.WhichOneof('oneof_field'))
+ self.assertEqual('oneof_bytes', m2.child.payload.WhichOneof('oneof_field'))
+
+ def testOneofMessageMergeFrom(self, message_module):
+ m = message_module.NestedTestAllTypes()
+ m.payload.oneof_nested_message.bb = 11
+ m.child.payload.oneof_nested_message.bb = 12
+ m2 = message_module.NestedTestAllTypes()
+ m2.payload.oneof_uint32 = 13
+ m2.MergeFrom(m)
+ self.assertEqual('oneof_nested_message',
+ m2.payload.WhichOneof('oneof_field'))
+ self.assertEqual('oneof_nested_message',
+ m2.child.payload.WhichOneof('oneof_field'))
+
+ def testOneofNestedMessageInit(self, message_module):
+ m = message_module.TestAllTypes(
+ oneof_nested_message=message_module.TestAllTypes.NestedMessage())
+ self.assertEqual('oneof_nested_message', m.WhichOneof('oneof_field'))
+
+ def testOneofClear(self, message_module):
+ m = message_module.TestAllTypes()
+ m.oneof_uint32 = 11
+ m.Clear()
+ self.assertIsNone(m.WhichOneof('oneof_field'))
+ m.oneof_bytes = b'bb'
+ self.assertEqual('oneof_bytes', m.WhichOneof('oneof_field'))
+
+ def testAssignByteStringToUnicodeField(self, message_module):
+ """Assigning a byte string to a string field should result
+ in the value being converted to a Unicode string."""
+ m = message_module.TestAllTypes()
+ m.optional_string = str('')
+ self.assertTrue(isinstance(m.optional_string, unicode))
+
+# TODO(haberman): why are these tests Google-internal only?
+
+ def testLongValuedSlice(self, message_module):
+ """It should be possible to use long-valued indicies in slices
+
+ This didn't used to work in the v2 C++ implementation.
+ """
+ m = message_module.TestAllTypes()
+
+ # Repeated scalar
+ m.repeated_int32.append(1)
+ sl = m.repeated_int32[long(0):long(len(m.repeated_int32))]
+ self.assertEqual(len(m.repeated_int32), len(sl))
+
+ # Repeated composite
+ m.repeated_nested_message.add().bb = 3
+ sl = m.repeated_nested_message[long(0):long(len(m.repeated_nested_message))]
+ self.assertEqual(len(m.repeated_nested_message), len(sl))
+
+ def testExtendShouldNotSwallowExceptions(self, message_module):
+ """This didn't use to work in the v2 C++ implementation."""
+ m = message_module.TestAllTypes()
+ with self.assertRaises(NameError) as _:
+ m.repeated_int32.extend(a for i in range(10)) # pylint: disable=undefined-variable
+ with self.assertRaises(NameError) as _:
+ m.repeated_nested_enum.extend(
+ a for i in range(10)) # pylint: disable=undefined-variable
+
+ FALSY_VALUES = [None, False, 0, 0.0, b'', u'', bytearray(), [], {}, set()]
+
+ def testExtendInt32WithNothing(self, message_module):
+ """Test no-ops extending repeated int32 fields."""
+ m = message_module.TestAllTypes()
+ self.assertSequenceEqual([], m.repeated_int32)
+
+ # TODO(ptucker): Deprecate this behavior. b/18413862
+ for falsy_value in MessageTest.FALSY_VALUES:
+ m.repeated_int32.extend(falsy_value)
+ self.assertSequenceEqual([], m.repeated_int32)
+
+ m.repeated_int32.extend([])
+ self.assertSequenceEqual([], m.repeated_int32)
+
+ def testExtendFloatWithNothing(self, message_module):
+ """Test no-ops extending repeated float fields."""
+ m = message_module.TestAllTypes()
+ self.assertSequenceEqual([], m.repeated_float)
+
+ # TODO(ptucker): Deprecate this behavior. b/18413862
+ for falsy_value in MessageTest.FALSY_VALUES:
+ m.repeated_float.extend(falsy_value)
+ self.assertSequenceEqual([], m.repeated_float)
+
+ m.repeated_float.extend([])
+ self.assertSequenceEqual([], m.repeated_float)
+
+ def testExtendStringWithNothing(self, message_module):
+ """Test no-ops extending repeated string fields."""
+ m = message_module.TestAllTypes()
+ self.assertSequenceEqual([], m.repeated_string)
+
+ # TODO(ptucker): Deprecate this behavior. b/18413862
+ for falsy_value in MessageTest.FALSY_VALUES:
+ m.repeated_string.extend(falsy_value)
+ self.assertSequenceEqual([], m.repeated_string)
+
+ m.repeated_string.extend([])
+ self.assertSequenceEqual([], m.repeated_string)
+
+ def testExtendInt32WithPythonList(self, message_module):
+ """Test extending repeated int32 fields with python lists."""
+ m = message_module.TestAllTypes()
+ self.assertSequenceEqual([], m.repeated_int32)
+ m.repeated_int32.extend([0])
+ self.assertSequenceEqual([0], m.repeated_int32)
+ m.repeated_int32.extend([1, 2])
+ self.assertSequenceEqual([0, 1, 2], m.repeated_int32)
+ m.repeated_int32.extend([3, 4])
+ self.assertSequenceEqual([0, 1, 2, 3, 4], m.repeated_int32)
+
+ def testExtendFloatWithPythonList(self, message_module):
+ """Test extending repeated float fields with python lists."""
+ m = message_module.TestAllTypes()
+ self.assertSequenceEqual([], m.repeated_float)
+ m.repeated_float.extend([0.0])
+ self.assertSequenceEqual([0.0], m.repeated_float)
+ m.repeated_float.extend([1.0, 2.0])
+ self.assertSequenceEqual([0.0, 1.0, 2.0], m.repeated_float)
+ m.repeated_float.extend([3.0, 4.0])
+ self.assertSequenceEqual([0.0, 1.0, 2.0, 3.0, 4.0], m.repeated_float)
+
+ def testExtendStringWithPythonList(self, message_module):
+ """Test extending repeated string fields with python lists."""
+ m = message_module.TestAllTypes()
+ self.assertSequenceEqual([], m.repeated_string)
+ m.repeated_string.extend([''])
+ self.assertSequenceEqual([''], m.repeated_string)
+ m.repeated_string.extend(['11', '22'])
+ self.assertSequenceEqual(['', '11', '22'], m.repeated_string)
+ m.repeated_string.extend(['33', '44'])
+ self.assertSequenceEqual(['', '11', '22', '33', '44'], m.repeated_string)
+
+ def testExtendStringWithString(self, message_module):
+ """Test extending repeated string fields with characters from a string."""
+ m = message_module.TestAllTypes()
+ self.assertSequenceEqual([], m.repeated_string)
+ m.repeated_string.extend('abc')
+ self.assertSequenceEqual(['a', 'b', 'c'], m.repeated_string)
+
+ class TestIterable(object):
+ """This iterable object mimics the behavior of numpy.array.
+
+ __nonzero__ fails for length > 1, and returns bool(item[0]) for length == 1.
+
+ """
+
+ def __init__(self, values=None):
+ self._list = values or []
+
+ def __nonzero__(self):
+ size = len(self._list)
+ if size == 0:
+ return False
+ if size == 1:
+ return bool(self._list[0])
+ raise ValueError('Truth value is ambiguous.')
+
+ def __len__(self):
+ return len(self._list)
+
+ def __iter__(self):
+ return self._list.__iter__()
+
+ def testExtendInt32WithIterable(self, message_module):
+ """Test extending repeated int32 fields with iterable."""
+ m = message_module.TestAllTypes()
+ self.assertSequenceEqual([], m.repeated_int32)
+ m.repeated_int32.extend(MessageTest.TestIterable([]))
+ self.assertSequenceEqual([], m.repeated_int32)
+ m.repeated_int32.extend(MessageTest.TestIterable([0]))
+ self.assertSequenceEqual([0], m.repeated_int32)
+ m.repeated_int32.extend(MessageTest.TestIterable([1, 2]))
+ self.assertSequenceEqual([0, 1, 2], m.repeated_int32)
+ m.repeated_int32.extend(MessageTest.TestIterable([3, 4]))
+ self.assertSequenceEqual([0, 1, 2, 3, 4], m.repeated_int32)
+
+ def testExtendFloatWithIterable(self, message_module):
+ """Test extending repeated float fields with iterable."""
+ m = message_module.TestAllTypes()
+ self.assertSequenceEqual([], m.repeated_float)
+ m.repeated_float.extend(MessageTest.TestIterable([]))
+ self.assertSequenceEqual([], m.repeated_float)
+ m.repeated_float.extend(MessageTest.TestIterable([0.0]))
+ self.assertSequenceEqual([0.0], m.repeated_float)
+ m.repeated_float.extend(MessageTest.TestIterable([1.0, 2.0]))
+ self.assertSequenceEqual([0.0, 1.0, 2.0], m.repeated_float)
+ m.repeated_float.extend(MessageTest.TestIterable([3.0, 4.0]))
+ self.assertSequenceEqual([0.0, 1.0, 2.0, 3.0, 4.0], m.repeated_float)
+
+ def testExtendStringWithIterable(self, message_module):
+ """Test extending repeated string fields with iterable."""
+ m = message_module.TestAllTypes()
+ self.assertSequenceEqual([], m.repeated_string)
+ m.repeated_string.extend(MessageTest.TestIterable([]))
+ self.assertSequenceEqual([], m.repeated_string)
+ m.repeated_string.extend(MessageTest.TestIterable(['']))
+ self.assertSequenceEqual([''], m.repeated_string)
+ m.repeated_string.extend(MessageTest.TestIterable(['1', '2']))
+ self.assertSequenceEqual(['', '1', '2'], m.repeated_string)
+ m.repeated_string.extend(MessageTest.TestIterable(['3', '4']))
+ self.assertSequenceEqual(['', '1', '2', '3', '4'], m.repeated_string)
+
+ def testPickleRepeatedScalarContainer(self, message_module):
+ # TODO(tibell): The pure-Python implementation support pickling of
+ # scalar containers in *some* cases. For now the cpp2 version
+ # throws an exception to avoid a segfault. Investigate if we
+ # want to support pickling of these fields.
+ #
+ # For more information see: https://b2.corp.google.com/u/0/issues/18677897
+ if (api_implementation.Type() != 'cpp' or
+ api_implementation.Version() == 2):
+ return
+ m = message_module.TestAllTypes()
+ with self.assertRaises(pickle.PickleError) as _:
+ pickle.dumps(m.repeated_int32, pickle.HIGHEST_PROTOCOL)
+
+
+ def testSortEmptyRepeatedCompositeContainer(self, message_module):
+ """Exercise a scenario that has led to segfaults in the past.
+ """
+ m = message_module.TestAllTypes()
+ m.repeated_nested_message.sort()
+
+ def testHasFieldOnRepeatedField(self, message_module):
+ """Using HasField on a repeated field should raise an exception.
+ """
+ m = message_module.TestAllTypes()
+ with self.assertRaises(ValueError) as _:
+ m.HasField('repeated_int32')
+
+ def testRepeatedScalarFieldPop(self, message_module):
+ m = message_module.TestAllTypes()
+ with self.assertRaises(IndexError) as _:
+ m.repeated_int32.pop()
+ m.repeated_int32.extend(range(5))
+ self.assertEqual(4, m.repeated_int32.pop())
+ self.assertEqual(0, m.repeated_int32.pop(0))
+ self.assertEqual(2, m.repeated_int32.pop(1))
+ self.assertEqual([1, 3], m.repeated_int32)
+
+ def testRepeatedCompositeFieldPop(self, message_module):
+ m = message_module.TestAllTypes()
+ with self.assertRaises(IndexError) as _:
+ m.repeated_nested_message.pop()
+ for i in range(5):
+ n = m.repeated_nested_message.add()
+ n.bb = i
+ self.assertEqual(4, m.repeated_nested_message.pop().bb)
+ self.assertEqual(0, m.repeated_nested_message.pop(0).bb)
+ self.assertEqual(2, m.repeated_nested_message.pop(1).bb)
+ self.assertEqual([1, 3], [n.bb for n in m.repeated_nested_message])
+
+
+# Class to test proto2-only features (required, extensions, etc.)
+class Proto2Test(basetest.TestCase):
+
+ def testFieldPresence(self):
+ message = unittest_pb2.TestAllTypes()
+
+ self.assertFalse(message.HasField("optional_int32"))
+ self.assertFalse(message.HasField("optional_bool"))
+ self.assertFalse(message.HasField("optional_nested_message"))
+
+ with self.assertRaises(ValueError):
+ message.HasField("field_doesnt_exist")
+
+ with self.assertRaises(ValueError):
+ message.HasField("repeated_int32")
+ with self.assertRaises(ValueError):
+ message.HasField("repeated_nested_message")
+
+ self.assertEqual(0, message.optional_int32)
+ self.assertEqual(False, message.optional_bool)
+ self.assertEqual(0, message.optional_nested_message.bb)
+
+ # Fields are set even when setting the values to default values.
+ message.optional_int32 = 0
+ message.optional_bool = False
+ message.optional_nested_message.bb = 0
+ self.assertTrue(message.HasField("optional_int32"))
+ self.assertTrue(message.HasField("optional_bool"))
+ self.assertTrue(message.HasField("optional_nested_message"))
+
+ # Set the fields to non-default values.
+ message.optional_int32 = 5
+ message.optional_bool = True
+ message.optional_nested_message.bb = 15
+
+ self.assertTrue(message.HasField("optional_int32"))
+ self.assertTrue(message.HasField("optional_bool"))
+ self.assertTrue(message.HasField("optional_nested_message"))
+
+ # Clearing the fields unsets them and resets their value to default.
+ message.ClearField("optional_int32")
+ message.ClearField("optional_bool")
+ message.ClearField("optional_nested_message")
+
+ self.assertFalse(message.HasField("optional_int32"))
+ self.assertFalse(message.HasField("optional_bool"))
+ self.assertFalse(message.HasField("optional_nested_message"))
+ self.assertEqual(0, message.optional_int32)
+ self.assertEqual(False, message.optional_bool)
+ self.assertEqual(0, message.optional_nested_message.bb)
+
+ # TODO(tibell): The C++ implementations actually allows assignment
+ # of unknown enum values to *scalar* fields (but not repeated
+ # fields). Once checked enum fields becomes the default in the
+ # Python implementation, the C++ implementation should follow suit.
+ def testAssignInvalidEnum(self):
+ """It should not be possible to assign an invalid enum number to an
+ enum field."""
+ m = unittest_pb2.TestAllTypes()
+
+ with self.assertRaises(ValueError) as _:
+ m.optional_nested_enum = 1234567
+ self.assertRaises(ValueError, m.repeated_nested_enum.append, 1234567)
+
+ def testGoldenExtensions(self):
+ golden_data = test_util.GoldenFileData('golden_message')
+ golden_message = unittest_pb2.TestAllExtensions()
+ golden_message.ParseFromString(golden_data)
+ all_set = unittest_pb2.TestAllExtensions()
+ test_util.SetAllExtensions(all_set)
+ self.assertEqual(all_set, golden_message)
+ self.assertEqual(golden_data, golden_message.SerializeToString())
+ golden_copy = copy.deepcopy(golden_message)
+ self.assertEqual(golden_data, golden_copy.SerializeToString())
+
+ def testGoldenPackedExtensions(self):
+ golden_data = test_util.GoldenFileData('golden_packed_fields_message')
+ golden_message = unittest_pb2.TestPackedExtensions()
+ golden_message.ParseFromString(golden_data)
+ all_set = unittest_pb2.TestPackedExtensions()
+ test_util.SetAllPackedExtensions(all_set)
+ self.assertEqual(all_set, golden_message)
+ self.assertEqual(golden_data, all_set.SerializeToString())
+ golden_copy = copy.deepcopy(golden_message)
+ self.assertEqual(golden_data, golden_copy.SerializeToString())
+
+ def testPickleIncompleteProto(self):
+ golden_message = unittest_pb2.TestRequired(a=1)
+ pickled_message = pickle.dumps(golden_message)
+
+ unpickled_message = pickle.loads(pickled_message)
+ self.assertEqual(unpickled_message, golden_message)
+ self.assertEqual(unpickled_message.a, 1)
+ # This is still an incomplete proto - so serializing should fail
+ self.assertRaises(message.EncodeError, unpickled_message.SerializeToString)
+
+
+ # TODO(haberman): this isn't really a proto2-specific test except that this
+ # message has a required field in it. Should probably be factored out so
+ # that we can test the other parts with proto3.
+ def testParsingMerge(self):
+ """Check the merge behavior when a required or optional field appears
+ multiple times in the input."""
+ messages = [
+ unittest_pb2.TestAllTypes(),
+ unittest_pb2.TestAllTypes(),
+ unittest_pb2.TestAllTypes() ]
+ messages[0].optional_int32 = 1
+ messages[1].optional_int64 = 2
+ messages[2].optional_int32 = 3
+ messages[2].optional_string = 'hello'
+
+ merged_message = unittest_pb2.TestAllTypes()
+ merged_message.optional_int32 = 3
+ merged_message.optional_int64 = 2
+ merged_message.optional_string = 'hello'
+
+ generator = unittest_pb2.TestParsingMerge.RepeatedFieldsGenerator()
+ generator.field1.extend(messages)
+ generator.field2.extend(messages)
+ generator.field3.extend(messages)
+ generator.ext1.extend(messages)
+ generator.ext2.extend(messages)
+ generator.group1.add().field1.MergeFrom(messages[0])
+ generator.group1.add().field1.MergeFrom(messages[1])
+ generator.group1.add().field1.MergeFrom(messages[2])
+ generator.group2.add().field1.MergeFrom(messages[0])
+ generator.group2.add().field1.MergeFrom(messages[1])
+ generator.group2.add().field1.MergeFrom(messages[2])
+
+ data = generator.SerializeToString()
+ parsing_merge = unittest_pb2.TestParsingMerge()
+ parsing_merge.ParseFromString(data)
+
+ # Required and optional fields should be merged.
+ self.assertEqual(parsing_merge.required_all_types, merged_message)
+ self.assertEqual(parsing_merge.optional_all_types, merged_message)
+ self.assertEqual(parsing_merge.optionalgroup.optional_group_all_types,
+ merged_message)
+ self.assertEqual(parsing_merge.Extensions[
+ unittest_pb2.TestParsingMerge.optional_ext],
+ merged_message)
+
+ # Repeated fields should not be merged.
+ self.assertEqual(len(parsing_merge.repeated_all_types), 3)
+ self.assertEqual(len(parsing_merge.repeatedgroup), 3)
+ self.assertEqual(len(parsing_merge.Extensions[
+ unittest_pb2.TestParsingMerge.repeated_ext]), 3)
+
+
+# Class to test proto3-only features/behavior (updated field presence & enums)
+class Proto3Test(basetest.TestCase):
+
+ def testFieldPresence(self):
+ message = unittest_proto3_arena_pb2.TestAllTypes()
+
+ # We can't test presence of non-repeated, non-submessage fields.
+ with self.assertRaises(ValueError):
+ message.HasField("optional_int32")
+ with self.assertRaises(ValueError):
+ message.HasField("optional_float")
+ with self.assertRaises(ValueError):
+ message.HasField("optional_string")
+ with self.assertRaises(ValueError):
+ message.HasField("optional_bool")
+
+ # But we can still test presence of submessage fields.
+ self.assertFalse(message.HasField("optional_nested_message"))
+
+ # As with proto2, we can't test presence of fields that don't exist, or
+ # repeated fields.
+ with self.assertRaises(ValueError):
+ message.HasField("field_doesnt_exist")
+
+ with self.assertRaises(ValueError):
+ message.HasField("repeated_int32")
+ with self.assertRaises(ValueError):
+ message.HasField("repeated_nested_message")
+
+ # Fields should default to their type-specific default.
+ self.assertEqual(0, message.optional_int32)
+ self.assertEqual(0, message.optional_float)
+ self.assertEqual("", message.optional_string)
+ self.assertEqual(False, message.optional_bool)
+ self.assertEqual(0, message.optional_nested_message.bb)
+
+ # Setting a submessage should still return proper presence information.
+ message.optional_nested_message.bb = 0
+ self.assertTrue(message.HasField("optional_nested_message"))
+
+ # Set the fields to non-default values.
+ message.optional_int32 = 5
+ message.optional_float = 1.1
+ message.optional_string = "abc"
+ message.optional_bool = True
+ message.optional_nested_message.bb = 15
+
+ # Clearing the fields unsets them and resets their value to default.
+ message.ClearField("optional_int32")
+ message.ClearField("optional_float")
+ message.ClearField("optional_string")
+ message.ClearField("optional_bool")
+ message.ClearField("optional_nested_message")
+
+ self.assertEqual(0, message.optional_int32)
+ self.assertEqual(0, message.optional_float)
+ self.assertEqual("", message.optional_string)
+ self.assertEqual(False, message.optional_bool)
+ self.assertEqual(0, message.optional_nested_message.bb)
+
+ def testAssignUnknownEnum(self):
+ """Assigning an unknown enum value is allowed and preserves the value."""
+ m = unittest_proto3_arena_pb2.TestAllTypes()
+
+ m.optional_nested_enum = 1234567
+ self.assertEqual(1234567, m.optional_nested_enum)
+ m.repeated_nested_enum.append(22334455)
+ self.assertEqual(22334455, m.repeated_nested_enum[0])
+ # Assignment is a different code path than append for the C++ impl.
+ m.repeated_nested_enum[0] = 7654321
+ self.assertEqual(7654321, m.repeated_nested_enum[0])
+ serialized = m.SerializeToString()
+
+ m2 = unittest_proto3_arena_pb2.TestAllTypes()
+ m2.ParseFromString(serialized)
+ self.assertEqual(1234567, m2.optional_nested_enum)
+ self.assertEqual(7654321, m2.repeated_nested_enum[0])
+
+
+class ValidTypeNamesTest(basetest.TestCase):
+
+ def assertImportFromName(self, msg, base_name):
+ # Parse <type 'module.class_name'> to extra 'some.name' as a string.
+ tp_name = str(type(msg)).split("'")[1]
+ valid_names = ('Repeated%sContainer' % base_name,
+ 'Repeated%sFieldContainer' % base_name)
+ self.assertTrue(any(tp_name.endswith(v) for v in valid_names),
+ '%r does end with any of %r' % (tp_name, valid_names))
+
+ parts = tp_name.split('.')
+ class_name = parts[-1]
+ module_name = '.'.join(parts[:-1])
+ __import__(module_name, fromlist=[class_name])
+
+ def testTypeNamesCanBeImported(self):
+ # If import doesn't work, pickling won't work either.
+ pb = unittest_pb2.TestAllTypes()
+ self.assertImportFromName(pb.repeated_int32, 'Scalar')
+ self.assertImportFromName(pb.repeated_nested_message, 'Composite')
+
if __name__ == '__main__':
- unittest.main()
+ basetest.main()
diff --git a/python/google/protobuf/internal/missing_enum_values.proto b/python/google/protobuf/internal/missing_enum_values.proto
new file mode 100644
index 00000000..161fc5e1
--- /dev/null
+++ b/python/google/protobuf/internal/missing_enum_values.proto
@@ -0,0 +1,52 @@
+// 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";
+
+package google.protobuf.python.internal;
+
+message TestEnumValues {
+ enum NestedEnum {
+ ZERO = 0;
+ ONE = 1;
+ }
+ optional NestedEnum optional_nested_enum = 1;
+ repeated NestedEnum repeated_nested_enum = 2;
+ repeated NestedEnum packed_nested_enum = 3 [packed = true];
+}
+
+message TestMissingEnumValues {
+ enum NestedEnum {
+ TWO = 2;
+ }
+ optional NestedEnum optional_nested_enum = 1;
+ repeated NestedEnum repeated_nested_enum = 2;
+ repeated NestedEnum packed_nested_enum = 3 [packed = true];
+}
diff --git a/python/google/protobuf/internal/more_extensions.proto b/python/google/protobuf/internal/more_extensions.proto
index e2d97010..78f14673 100644
--- a/python/google/protobuf/internal/more_extensions.proto
+++ b/python/google/protobuf/internal/more_extensions.proto
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -30,6 +30,7 @@
// Author: robinson@google.com (Will Robinson)
+syntax = "proto2";
package google.protobuf.internal;
diff --git a/python/google/protobuf/internal/more_extensions_dynamic.proto b/python/google/protobuf/internal/more_extensions_dynamic.proto
new file mode 100644
index 00000000..11f85ef6
--- /dev/null
+++ b/python/google/protobuf/internal/more_extensions_dynamic.proto
@@ -0,0 +1,50 @@
+// 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: jasonh@google.com (Jason Hsueh)
+//
+// This file is used to test a corner case in the CPP implementation where the
+// generated C++ type is available for the extendee, but the extension is
+// defined in a file whose C++ type is not in the binary.
+
+syntax = "proto2";
+
+import "google/protobuf/internal/more_extensions.proto";
+
+package google.protobuf.internal;
+
+message DynamicMessageType {
+ optional int32 a = 1;
+}
+
+extend ExtendedMessage {
+ optional int32 dynamic_int32_extension = 100;
+ optional DynamicMessageType dynamic_message_extension = 101;
+}
diff --git a/python/google/protobuf/internal/more_messages.proto b/python/google/protobuf/internal/more_messages.proto
index c701b446..2c6ab9ef 100644
--- a/python/google/protobuf/internal/more_messages.proto
+++ b/python/google/protobuf/internal/more_messages.proto
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
@@ -30,6 +30,7 @@
// Author: robinson@google.com (Will Robinson)
+syntax = "proto2";
package google.protobuf.internal;
diff --git a/python/google/protobuf/internal/proto_builder_test.py b/python/google/protobuf/internal/proto_builder_test.py
new file mode 100644
index 00000000..c74db7e7
--- /dev/null
+++ b/python/google/protobuf/internal/proto_builder_test.py
@@ -0,0 +1,77 @@
+#! /usr/bin/python
+#
+# 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.
+
+"""Tests for google.protobuf.proto_builder."""
+
+from google.apputils import basetest
+
+from google.protobuf import descriptor_pb2
+from google.protobuf import descriptor_pool
+from google.protobuf import proto_builder
+from google.protobuf import text_format
+
+
+class ProtoBuilderTest(basetest.TestCase):
+
+ def setUp(self):
+ self._fields = {
+ 'foo': descriptor_pb2.FieldDescriptorProto.TYPE_INT64,
+ 'bar': descriptor_pb2.FieldDescriptorProto.TYPE_STRING,
+ }
+
+ def testMakeSimpleProtoClass(self):
+ """Test that we can create a proto class."""
+ proto_cls = proto_builder.MakeSimpleProtoClass(
+ self._fields,
+ full_name='net.proto2.python.public.proto_builder_test.Test')
+ proto = proto_cls()
+ proto.foo = 12345
+ proto.bar = 'asdf'
+ self.assertMultiLineEqual(
+ 'bar: "asdf"\nfoo: 12345\n', text_format.MessageToString(proto))
+
+ def testMakeSameProtoClassTwice(self):
+ """Test that the DescriptorPool is used."""
+ pool = descriptor_pool.DescriptorPool()
+ proto_cls1 = proto_builder.MakeSimpleProtoClass(
+ self._fields,
+ full_name='net.proto2.python.public.proto_builder_test.Test',
+ pool=pool)
+ proto_cls2 = proto_builder.MakeSimpleProtoClass(
+ self._fields,
+ full_name='net.proto2.python.public.proto_builder_test.Test',
+ pool=pool)
+ self.assertIs(proto_cls1.DESCRIPTOR, proto_cls2.DESCRIPTOR)
+
+
+if __name__ == '__main__':
+ basetest.main()
diff --git a/python/google/protobuf/internal/python_message.py b/python/google/protobuf/internal/python_message.py
index 66fca918..6ad0f90d 100755
--- a/python/google/protobuf/internal/python_message.py
+++ b/python/google/protobuf/internal/python_message.py
@@ -1,6 +1,6 @@
# Protocol Buffers - Google's data interchange format
# Copyright 2008 Google Inc. All rights reserved.
-# http://code.google.com/p/protobuf/
+# 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
@@ -28,6 +28,10 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+# Keep it Python2.5 compatible for GAE.
+#
+# Copyright 2007 Google Inc. All Rights Reserved.
+#
# This code is meant to work on Python 2.4 and above only.
#
# TODO(robinson): Helpers for verbose, common checks like seeing if a
@@ -50,10 +54,16 @@ this file*.
__author__ = 'robinson@google.com (Will Robinson)'
-try:
- from cStringIO import StringIO
-except ImportError:
- from StringIO import StringIO
+import sys
+if sys.version_info[0] < 3:
+ try:
+ from cStringIO import StringIO as BytesIO
+ except ImportError:
+ from StringIO import StringIO as BytesIO
+ import copy_reg as copyreg
+else:
+ from io import BytesIO
+ import copyreg
import struct
import weakref
@@ -61,6 +71,7 @@ import weakref
from google.protobuf.internal import containers
from google.protobuf.internal import decoder
from google.protobuf.internal import encoder
+from google.protobuf.internal import enum_type_wrapper
from google.protobuf.internal import message_listener as message_listener_mod
from google.protobuf.internal import type_checkers
from google.protobuf.internal import wire_format
@@ -71,9 +82,10 @@ from google.protobuf import text_format
_FieldDescriptor = descriptor_mod.FieldDescriptor
-def NewMessage(descriptor, dictionary):
+def NewMessage(bases, descriptor, dictionary):
_AddClassAttributesForNestedExtensions(descriptor, dictionary)
_AddSlots(descriptor, dictionary)
+ return bases
def InitMessage(descriptor, cls):
@@ -83,7 +95,7 @@ def InitMessage(descriptor, cls):
if (descriptor.has_options and
descriptor.GetOptions().message_set_wire_format):
cls._decoders_by_tag[decoder.MESSAGE_SET_ITEM_TAG] = (
- decoder.MessageSetItemDecoder(cls._extensions_by_number))
+ decoder.MessageSetItemDecoder(cls._extensions_by_number), None)
# Attach stuff to each FieldDescriptor for quick lookup later on.
for field in descriptor.fields:
@@ -95,7 +107,8 @@ def InitMessage(descriptor, cls):
_AddPropertiesForExtensions(descriptor, cls)
_AddStaticMethods(cls)
_AddMessageMethods(descriptor, cls)
- _AddPrivateHelperMethods(cls)
+ _AddPrivateHelperMethods(descriptor, cls)
+ copyreg.pickle(cls, lambda obj: (cls, (), obj.__getstate__()))
# Stateless helpers for GeneratedProtocolMessageType below.
@@ -145,6 +158,10 @@ def _VerifyExtensionHandle(message, extension_handle):
if not extension_handle.is_extension:
raise KeyError('"%s" is not an extension.' % extension_handle.full_name)
+ if not extension_handle.containing_type:
+ raise KeyError('"%s" is missing a containing_type.'
+ % extension_handle.full_name)
+
if extension_handle.containing_type is not message.DESCRIPTOR:
raise KeyError('Extension "%s" extends message type "%s", but this '
'message is of type "%s".' %
@@ -164,10 +181,12 @@ def _AddSlots(message_descriptor, dictionary):
dictionary['__slots__'] = ['_cached_byte_size',
'_cached_byte_size_dirty',
'_fields',
+ '_unknown_fields',
'_is_present_in_parent',
'_listener',
'_listener_for_children',
- '__weakref__']
+ '__weakref__',
+ '_oneofs']
def _IsMessageSetExtension(field):
@@ -200,10 +219,20 @@ def _AttachFieldHelpers(cls, field_descriptor):
def AddDecoder(wiretype, is_packed):
tag_bytes = encoder.TagBytes(field_descriptor.number, wiretype)
- cls._decoders_by_tag[tag_bytes] = (
- type_checkers.TYPE_TO_DECODER[field_descriptor.type](
- field_descriptor.number, is_repeated, is_packed,
- field_descriptor, field_descriptor._default_constructor))
+ decode_type = field_descriptor.type
+ if (decode_type == _FieldDescriptor.TYPE_ENUM and
+ type_checkers.SupportsOpenEnums(field_descriptor)):
+ decode_type = _FieldDescriptor.TYPE_INT32
+
+ oneof_descriptor = None
+ if field_descriptor.containing_oneof is not None:
+ oneof_descriptor = field_descriptor
+
+ field_decoder = type_checkers.TYPE_TO_DECODER[decode_type](
+ field_descriptor.number, is_repeated, is_packed,
+ field_descriptor, field_descriptor._default_constructor)
+
+ cls._decoders_by_tag[tag_bytes] = (field_decoder, oneof_descriptor)
AddDecoder(type_checkers.FIELD_TYPE_TO_WIRE_TYPE[field_descriptor.type],
False)
@@ -224,11 +253,14 @@ def _AddClassAttributesForNestedExtensions(descriptor, dictionary):
def _AddEnumValues(descriptor, cls):
"""Sets class-level attributes for all enum fields defined in this message.
+ Also exporting a class-level object that can name enum values.
+
Args:
descriptor: Descriptor object for this message type.
cls: Class we're constructing for this message type.
"""
for enum_type in descriptor.enum_types:
+ setattr(cls, enum_type.name, enum_type_wrapper.EnumTypeWrapper(enum_type))
for enum_value in enum_type.values:
setattr(cls, enum_value.name, enum_value.number)
@@ -248,7 +280,7 @@ def _DefaultValueConstructorForField(field):
"""
if field.label == _FieldDescriptor.LABEL_REPEATED:
- if field.default_value != []:
+ if field.has_default_value and field.default_value != []:
raise ValueError('Repeated field default value not empty list: %s' % (
field.default_value))
if field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE:
@@ -260,7 +292,7 @@ def _DefaultValueConstructorForField(field):
message._listener_for_children, field.message_type)
return MakeRepeatedMessageDefault
else:
- type_checker = type_checkers.GetTypeChecker(field.cpp_type, field.type)
+ type_checker = type_checkers.GetTypeChecker(field)
def MakeRepeatedScalarDefault(message):
return containers.RepeatedScalarFieldContainer(
message._listener_for_children, type_checker)
@@ -272,14 +304,29 @@ def _DefaultValueConstructorForField(field):
def MakeSubMessageDefault(message):
result = message_type._concrete_class()
result._SetListener(message._listener_for_children)
+ if field.containing_oneof:
+ message._UpdateOneofState(field)
return result
return MakeSubMessageDefault
def MakeScalarDefault(message):
+ # TODO(protobuf-team): This may be broken since there may not be
+ # default_value. Combine with has_default_value somehow.
return field.default_value
return MakeScalarDefault
+def _ReraiseTypeErrorWithFieldName(message_name, field_name):
+ """Re-raise the currently-handled TypeError with the field name added."""
+ exc = sys.exc_info()[1]
+ if len(exc.args) == 1 and type(exc) is TypeError:
+ # simple TypeError; add field name to exception message
+ exc = TypeError('%s for field %s.%s' % (str(exc), message_name, field_name))
+
+ # re-raise possibly-amended exception with original traceback:
+ raise type(exc), exc, sys.exc_info()[2]
+
+
def _AddInitMethod(message_descriptor, cls):
"""Adds an __init__ method to cls."""
fields = message_descriptor.fields
@@ -287,6 +334,13 @@ def _AddInitMethod(message_descriptor, cls):
self._cached_byte_size = 0
self._cached_byte_size_dirty = len(kwargs) > 0
self._fields = {}
+ # Contains a mapping from oneof field descriptors to the descriptor
+ # of the currently set field in that oneof field.
+ self._oneofs = {}
+
+ # _unknown_fields is () when empty for efficiency, and will be turned into
+ # a list if fields are added.
+ self._unknown_fields = ()
self._is_present_in_parent = False
self._listener = message_listener_mod.NullMessageListener()
self._listener_for_children = _Listener(self)
@@ -305,10 +359,16 @@ def _AddInitMethod(message_descriptor, cls):
self._fields[field] = copy
elif field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE:
copy = field._default_constructor(self)
- copy.MergeFrom(field_value)
+ try:
+ copy.MergeFrom(field_value)
+ except TypeError:
+ _ReraiseTypeErrorWithFieldName(message_descriptor.name, field_name)
self._fields[field] = copy
else:
- setattr(self, field_name, field_value)
+ try:
+ setattr(self, field_name, field_value)
+ except TypeError:
+ _ReraiseTypeErrorWithFieldName(message_descriptor.name, field_name)
init.__module__ = None
init.__doc__ = None
@@ -423,22 +483,41 @@ def _AddPropertiesForNonRepeatedScalarField(field, cls):
"""
proto_field_name = field.name
property_name = _PropertyName(proto_field_name)
- type_checker = type_checkers.GetTypeChecker(field.cpp_type, field.type)
+ type_checker = type_checkers.GetTypeChecker(field)
default_value = field.default_value
valid_values = set()
+ is_proto3 = field.containing_type.syntax == "proto3"
def getter(self):
+ # TODO(protobuf-team): This may be broken since there may not be
+ # default_value. Combine with has_default_value somehow.
return self._fields.get(field, default_value)
getter.__module__ = None
getter.__doc__ = 'Getter for %s.' % proto_field_name
- def setter(self, new_value):
- type_checker.CheckValue(new_value)
- self._fields[field] = new_value
+
+ clear_when_set_to_default = is_proto3 and not field.containing_oneof
+
+ def field_setter(self, new_value):
+ # pylint: disable=protected-access
+ # Testing the value for truthiness captures all of the proto3 defaults
+ # (0, 0.0, enum 0, and False).
+ new_value = type_checker.CheckValue(new_value)
+ if clear_when_set_to_default and not new_value:
+ self._fields.pop(field, None)
+ else:
+ self._fields[field] = new_value
# Check _cached_byte_size_dirty inline to improve performance, since scalar
# setters are called frequently.
if not self._cached_byte_size_dirty:
self._Modified()
+ if field.containing_oneof:
+ def setter(self, new_value):
+ field_setter(self, new_value)
+ self._UpdateOneofState(field)
+ else:
+ setter = field_setter
+
setter.__module__ = None
setter.__doc__ = 'Setter for %s.' % proto_field_name
@@ -462,14 +541,22 @@ def _AddPropertiesForNonRepeatedCompositeField(field, cls):
# for non-repeated scalars.
proto_field_name = field.name
property_name = _PropertyName(proto_field_name)
+
+ # TODO(komarek): Can anyone explain to me why we cache the message_type this
+ # way, instead of referring to field.message_type inside of getter(self)?
+ # What if someone sets message_type later on (which makes for simpler
+ # dyanmic proto descriptor and class creation code).
message_type = field.message_type
def getter(self):
field_value = self._fields.get(field)
if field_value is None:
# Construct a new object to represent this field.
- field_value = message_type._concrete_class()
- field_value._SetListener(self._listener_for_children)
+ field_value = message_type._concrete_class() # use field.message_type?
+ field_value._SetListener(
+ _OneofListener(self, field)
+ if field.containing_oneof is not None
+ else self._listener_for_children)
# Atomically check if another thread has preempted us and, if not, swap
# in the new object we just created. If someone has preempted us, we
@@ -557,27 +644,48 @@ def _AddListFieldsMethod(message_descriptor, cls):
cls.ListFields = ListFields
+_Proto3HasError = 'Protocol message has no non-repeated submessage field "%s"'
+_Proto2HasError = 'Protocol message has no non-repeated field "%s"'
def _AddHasFieldMethod(message_descriptor, cls):
"""Helper for _AddMessageMethods()."""
- singular_fields = {}
+ is_proto3 = (message_descriptor.syntax == "proto3")
+ error_msg = _Proto3HasError if is_proto3 else _Proto2HasError
+
+ hassable_fields = {}
for field in message_descriptor.fields:
- if field.label != _FieldDescriptor.LABEL_REPEATED:
- singular_fields[field.name] = field
+ if field.label == _FieldDescriptor.LABEL_REPEATED:
+ continue
+ # For proto3, only submessages and fields inside a oneof have presence.
+ if (is_proto3 and field.cpp_type != _FieldDescriptor.CPPTYPE_MESSAGE and
+ not field.containing_oneof):
+ continue
+ hassable_fields[field.name] = field
+
+ if not is_proto3:
+ # Fields inside oneofs are never repeated (enforced by the compiler).
+ for oneof in message_descriptor.oneofs:
+ hassable_fields[oneof.name] = oneof
def HasField(self, field_name):
try:
- field = singular_fields[field_name]
+ field = hassable_fields[field_name]
except KeyError:
- raise ValueError(
- 'Protocol message has no singular "%s" field.' % field_name)
+ raise ValueError(error_msg % field_name)
- if field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE:
- value = self._fields.get(field)
- return value is not None and value._is_present_in_parent
+ if isinstance(field, descriptor_mod.OneofDescriptor):
+ try:
+ return HasField(self, self._oneofs[field].name)
+ except KeyError:
+ return False
else:
- return field in self._fields
+ if field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE:
+ value = self._fields.get(field)
+ return value is not None and value._is_present_in_parent
+ else:
+ return field in self._fields
+
cls.HasField = HasField
@@ -587,7 +695,14 @@ def _AddClearFieldMethod(message_descriptor, cls):
try:
field = message_descriptor.fields_by_name[field_name]
except KeyError:
- raise ValueError('Protocol message has no "%s" field.' % field_name)
+ try:
+ field = message_descriptor.oneofs_by_name[field_name]
+ if field in self._oneofs:
+ field = self._oneofs[field]
+ else:
+ return
+ except KeyError:
+ raise ValueError('Protocol message has no "%s" field.' % field_name)
if field in self._fields:
# Note: If the field is a sub-message, its listener will still point
@@ -595,6 +710,9 @@ def _AddClearFieldMethod(message_descriptor, cls):
# will call _Modified() and invalidate our byte size. Big deal.
del self._fields[field]
+ if self._oneofs.get(field.containing_oneof, None) is field:
+ del self._oneofs[field.containing_oneof]
+
# Always call _Modified() -- even if nothing was changed, this is
# a mutating method, and thus calling it should cause the field to become
# present in the parent message.
@@ -620,6 +738,8 @@ def _AddClearMethod(message_descriptor, cls):
def Clear(self):
# Clear fields.
self._fields = {}
+ self._unknown_fields = ()
+ self._oneofs = {}
self._Modified()
cls.Clear = Clear
@@ -649,7 +769,16 @@ def _AddEqualsMethod(message_descriptor, cls):
if self is other:
return True
- return self.ListFields() == other.ListFields()
+ if not self.ListFields() == other.ListFields():
+ return False
+
+ # Sort unknown fields because their order shouldn't affect equality test.
+ unknown_fields = list(self._unknown_fields)
+ unknown_fields.sort()
+ other_unknown_fields = list(other._unknown_fields)
+ other_unknown_fields.sort()
+
+ return unknown_fields == other_unknown_fields
cls.__eq__ = __eq__
@@ -710,6 +839,9 @@ def _AddByteSizeMethod(message_descriptor, cls):
for field_descriptor, field_value in self.ListFields():
size += field_descriptor._sizer(field_value)
+ for tag_bytes, value_bytes in self._unknown_fields:
+ size += len(tag_bytes) + len(value_bytes)
+
self._cached_byte_size = size
self._cached_byte_size_dirty = False
self._listener_for_children.dirty = False
@@ -726,8 +858,8 @@ def _AddSerializeToStringMethod(message_descriptor, cls):
errors = []
if not self.IsInitialized():
raise message_mod.EncodeError(
- 'Message is missing required fields: ' +
- ','.join(self.FindInitializationErrors()))
+ 'Message %s is missing required fields: %s' % (
+ self.DESCRIPTOR.full_name, ','.join(self.FindInitializationErrors())))
return self.SerializePartialToString()
cls.SerializeToString = SerializeToString
@@ -736,7 +868,7 @@ def _AddSerializePartialToStringMethod(message_descriptor, cls):
"""Helper for _AddMessageMethods()."""
def SerializePartialToString(self):
- out = StringIO()
+ out = BytesIO()
self._InternalSerialize(out.write)
return out.getvalue()
cls.SerializePartialToString = SerializePartialToString
@@ -744,6 +876,9 @@ def _AddSerializePartialToStringMethod(message_descriptor, cls):
def InternalSerialize(self, write_bytes):
for field_descriptor, field_value in self.ListFields():
field_descriptor._encoder(write_bytes, field_value)
+ for tag_bytes, value_bytes in self._unknown_fields:
+ write_bytes(tag_bytes)
+ write_bytes(value_bytes)
cls._InternalSerialize = InternalSerialize
@@ -756,7 +891,8 @@ def _AddMergeFromStringMethod(message_descriptor, cls):
# The only reason _InternalParse would return early is if it
# encountered an end-group tag.
raise message_mod.DecodeError('Unexpected end-group tag.')
- except IndexError:
+ except (IndexError, TypeError):
+ # Now ord(buf[p:p+1]) == ord('') gets TypeError.
raise message_mod.DecodeError('Truncated message.')
except struct.error, e:
raise message_mod.DecodeError(e)
@@ -766,20 +902,30 @@ def _AddMergeFromStringMethod(message_descriptor, cls):
local_ReadTag = decoder.ReadTag
local_SkipField = decoder.SkipField
decoders_by_tag = cls._decoders_by_tag
+ is_proto3 = message_descriptor.syntax == "proto3"
def InternalParse(self, buffer, pos, end):
self._Modified()
field_dict = self._fields
+ unknown_field_list = self._unknown_fields
while pos != end:
(tag_bytes, new_pos) = local_ReadTag(buffer, pos)
- field_decoder = decoders_by_tag.get(tag_bytes)
+ field_decoder, field_desc = decoders_by_tag.get(tag_bytes, (None, None))
if field_decoder is None:
+ value_start_pos = new_pos
new_pos = local_SkipField(buffer, new_pos, end, tag_bytes)
if new_pos == -1:
return pos
+ if not is_proto3:
+ if not unknown_field_list:
+ unknown_field_list = self._unknown_fields = []
+ unknown_field_list.append(
+ (tag_bytes, buffer[value_start_pos:new_pos]))
pos = new_pos
else:
pos = field_decoder(buffer, new_pos, end, self, field_dict)
+ if field_desc:
+ self._UpdateOneofState(field_desc)
return pos
cls._InternalParse = InternalParse
@@ -812,7 +958,7 @@ def _AddIsInitializedMethod(message_descriptor, cls):
errors.extend(self.FindInitializationErrors())
return False
- for field, value in self._fields.iteritems():
+ for field, value in list(self._fields.items()): # dict can change size!
if field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE:
if field.label == _FieldDescriptor.LABEL_REPEATED:
for element in value:
@@ -873,7 +1019,8 @@ def _AddMergeFromMethod(cls):
def MergeFrom(self, msg):
if not isinstance(msg, cls):
raise TypeError(
- "Parameter to MergeFrom() must be instance of same class.")
+ "Parameter to MergeFrom() must be instance of same class: "
+ "expected %s got %s." % (cls.__name__, type(msg).__name__))
assert msg is not self
self._Modified()
@@ -895,12 +1042,40 @@ def _AddMergeFromMethod(cls):
# Construct a new object to represent this field.
field_value = field._default_constructor(self)
fields[field] = field_value
+ if field.containing_oneof:
+ self._UpdateOneofState(field)
field_value.MergeFrom(value)
else:
self._fields[field] = value
+ if field.containing_oneof:
+ self._UpdateOneofState(field)
+
+ if msg._unknown_fields:
+ if not self._unknown_fields:
+ self._unknown_fields = []
+ self._unknown_fields.extend(msg._unknown_fields)
+
cls.MergeFrom = MergeFrom
+def _AddWhichOneofMethod(message_descriptor, cls):
+ def WhichOneof(self, oneof_name):
+ """Returns the name of the currently set field inside a oneof, or None."""
+ try:
+ field = message_descriptor.oneofs_by_name[oneof_name]
+ except KeyError:
+ raise ValueError(
+ 'Protocol message has no oneof "%s" field.' % oneof_name)
+
+ nested_field = self._oneofs.get(field, None)
+ if nested_field is not None and self.HasField(nested_field.name):
+ return nested_field.name
+ else:
+ return None
+
+ cls.WhichOneof = WhichOneof
+
+
def _AddMessageMethods(message_descriptor, cls):
"""Adds implementations of all Message methods to cls."""
_AddListFieldsMethod(message_descriptor, cls)
@@ -920,9 +1095,9 @@ def _AddMessageMethods(message_descriptor, cls):
_AddMergeFromStringMethod(message_descriptor, cls)
_AddIsInitializedMethod(message_descriptor, cls)
_AddMergeFromMethod(cls)
+ _AddWhichOneofMethod(message_descriptor, cls)
-
-def _AddPrivateHelperMethods(cls):
+def _AddPrivateHelperMethods(message_descriptor, cls):
"""Adds implementation of private helper methods to cls."""
def Modified(self):
@@ -940,8 +1115,20 @@ def _AddPrivateHelperMethods(cls):
self._is_present_in_parent = True
self._listener.Modified()
+ def _UpdateOneofState(self, field):
+ """Sets field as the active field in its containing oneof.
+
+ Will also delete currently active field in the oneof, if it is different
+ from the argument. Does not mark the message as modified.
+ """
+ other_field = self._oneofs.setdefault(field.containing_oneof, field)
+ if other_field is not field:
+ del self._fields[other_field]
+ self._oneofs[field.containing_oneof] = field
+
cls._Modified = Modified
cls.SetInParent = Modified
+ cls._UpdateOneofState = _UpdateOneofState
class _Listener(object):
@@ -990,6 +1177,27 @@ class _Listener(object):
pass
+class _OneofListener(_Listener):
+ """Special listener implementation for setting composite oneof fields."""
+
+ def __init__(self, parent_message, field):
+ """Args:
+ parent_message: The message whose _Modified() method we should call when
+ we receive Modified() messages.
+ field: The descriptor of the field being set in the parent message.
+ """
+ super(_OneofListener, self).__init__(parent_message)
+ self._field = field
+
+ def Modified(self):
+ """Also updates the state of the containing oneof in the parent message."""
+ try:
+ self._parent_message_weakref._UpdateOneofState(self._field)
+ super(_OneofListener, self).Modified()
+ except ReferenceError:
+ pass
+
+
# TODO(robinson): Move elsewhere? This file is getting pretty ridiculous...
# TODO(robinson): Unify error handling of "unknown extension" crap.
# TODO(robinson): Support iteritems()-style iteration over all
@@ -1080,10 +1288,10 @@ class _ExtensionDict(object):
# It's slightly wasteful to lookup the type checker each time,
# but we expect this to be a vanishingly uncommon case anyway.
- type_checker = type_checkers.GetTypeChecker(
- extension_handle.cpp_type, extension_handle.type)
- type_checker.CheckValue(value)
- self._extended_message._fields[extension_handle] = value
+ type_checker = type_checkers.GetTypeChecker(extension_handle)
+ # pylint: disable=protected-access
+ self._extended_message._fields[extension_handle] = (
+ type_checker.CheckValue(value))
self._extended_message._Modified()
def _FindExtensionByName(self, name):
diff --git a/python/google/protobuf/internal/reflection_test.py b/python/google/protobuf/internal/reflection_test.py
index 7b9d3398..a62d9845 100755
--- a/python/google/protobuf/internal/reflection_test.py
+++ b/python/google/protobuf/internal/reflection_test.py
@@ -3,7 +3,7 @@
#
# Protocol Buffers - Google's data interchange format
# Copyright 2008 Google Inc. All rights reserved.
-# http://code.google.com/p/protobuf/
+# 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
@@ -35,12 +35,12 @@
pure-Python protocol compiler.
"""
-__author__ = 'robinson@google.com (Will Robinson)'
-
+import copy
+import gc
import operator
import struct
-import unittest
+from google.apputils import basetest
from google.protobuf import unittest_import_pb2
from google.protobuf import unittest_mset_pb2
from google.protobuf import unittest_pb2
@@ -48,6 +48,7 @@ from google.protobuf import descriptor_pb2
from google.protobuf import descriptor
from google.protobuf import message
from google.protobuf import reflection
+from google.protobuf import text_format
from google.protobuf.internal import api_implementation
from google.protobuf.internal import more_extensions_pb2
from google.protobuf.internal import more_messages_pb2
@@ -101,7 +102,7 @@ class _MiniDecoder(object):
return self._pos == len(self._bytes)
-class ReflectionTest(unittest.TestCase):
+class ReflectionTest(basetest.TestCase):
def assertListsEqual(self, values, others):
self.assertEqual(len(values), len(others))
@@ -318,15 +319,6 @@ class ReflectionTest(unittest.TestCase):
# ...and ensure that the scalar field has returned to its default.
self.assertEqual(0, getattr(composite_field, scalar_field_name))
- # Finally, ensure that modifications to the old composite field object
- # don't have any effect on the parent. Possible only with the pure-python
- # implementation of the API.
- #
- # (NOTE that when we clear the composite field in the parent, we actually
- # don't recursively clear down the tree. Instead, we just disconnect the
- # cleared composite from the tree.)
- if api_implementation.Type() != 'python':
- return
self.assertTrue(old_composite_field is not composite_field)
setattr(old_composite_field, scalar_field_name, new_val)
self.assertTrue(not composite_field.HasField(scalar_field_name))
@@ -348,8 +340,6 @@ class ReflectionTest(unittest.TestCase):
nested.bb = 23
def testDisconnectingNestedMessageBeforeSettingField(self):
- if api_implementation.Type() != 'python':
- return
proto = unittest_pb2.TestAllTypes()
nested = proto.optional_nested_message
proto.ClearField('optional_nested_message') # Should disconnect from parent
@@ -358,6 +348,64 @@ class ReflectionTest(unittest.TestCase):
self.assertTrue(not proto.HasField('optional_nested_message'))
self.assertEqual(0, proto.optional_nested_message.bb)
+ def testGetDefaultMessageAfterDisconnectingDefaultMessage(self):
+ proto = unittest_pb2.TestAllTypes()
+ nested = proto.optional_nested_message
+ proto.ClearField('optional_nested_message')
+ del proto
+ del nested
+ # Force a garbage collect so that the underlying CMessages are freed along
+ # with the Messages they point to. This is to make sure we're not deleting
+ # default message instances.
+ gc.collect()
+ proto = unittest_pb2.TestAllTypes()
+ nested = proto.optional_nested_message
+
+ def testDisconnectingNestedMessageAfterSettingField(self):
+ proto = unittest_pb2.TestAllTypes()
+ nested = proto.optional_nested_message
+ nested.bb = 5
+ self.assertTrue(proto.HasField('optional_nested_message'))
+ proto.ClearField('optional_nested_message') # Should disconnect from parent
+ self.assertEqual(5, nested.bb)
+ self.assertEqual(0, proto.optional_nested_message.bb)
+ self.assertTrue(nested is not proto.optional_nested_message)
+ nested.bb = 23
+ self.assertTrue(not proto.HasField('optional_nested_message'))
+ self.assertEqual(0, proto.optional_nested_message.bb)
+
+ def testDisconnectingNestedMessageBeforeGettingField(self):
+ proto = unittest_pb2.TestAllTypes()
+ self.assertTrue(not proto.HasField('optional_nested_message'))
+ proto.ClearField('optional_nested_message')
+ self.assertTrue(not proto.HasField('optional_nested_message'))
+
+ def testDisconnectingNestedMessageAfterMerge(self):
+ # This test exercises the code path that does not use ReleaseMessage().
+ # The underlying fear is that if we use ReleaseMessage() incorrectly,
+ # we will have memory leaks. It's hard to check that that doesn't happen,
+ # but at least we can exercise that code path to make sure it works.
+ proto1 = unittest_pb2.TestAllTypes()
+ proto2 = unittest_pb2.TestAllTypes()
+ proto2.optional_nested_message.bb = 5
+ proto1.MergeFrom(proto2)
+ self.assertTrue(proto1.HasField('optional_nested_message'))
+ proto1.ClearField('optional_nested_message')
+ self.assertTrue(not proto1.HasField('optional_nested_message'))
+
+ def testDisconnectingLazyNestedMessage(self):
+ # This test exercises releasing a nested message that is lazy. This test
+ # only exercises real code in the C++ implementation as Python does not
+ # support lazy parsing, but the current C++ implementation results in
+ # memory corruption and a crash.
+ if api_implementation.Type() != 'python':
+ return
+ proto = unittest_pb2.TestAllTypes()
+ proto.optional_lazy_message.bb = 5
+ proto.ClearField('optional_lazy_message')
+ del proto
+ gc.collect()
+
def testHasBitsWhenModifyingRepeatedFields(self):
# Test nesting when we add an element to a repeated field in a submessage.
proto = unittest_pb2.TestNestedMessageHasBits()
@@ -485,7 +533,7 @@ class ReflectionTest(unittest.TestCase):
self.assertEqual(0.0, proto.optional_double)
self.assertEqual(False, proto.optional_bool)
self.assertEqual('', proto.optional_string)
- self.assertEqual('', proto.optional_bytes)
+ self.assertEqual(b'', proto.optional_bytes)
self.assertEqual(41, proto.default_int32)
self.assertEqual(42, proto.default_int64)
@@ -501,7 +549,7 @@ class ReflectionTest(unittest.TestCase):
self.assertEqual(52e3, proto.default_double)
self.assertEqual(True, proto.default_bool)
self.assertEqual('hello', proto.default_string)
- self.assertEqual('world', proto.default_bytes)
+ self.assertEqual(b'world', proto.default_bytes)
self.assertEqual(unittest_pb2.TestAllTypes.BAR, proto.default_nested_enum)
self.assertEqual(unittest_pb2.FOREIGN_BAR, proto.default_foreign_enum)
self.assertEqual(unittest_import_pb2.IMPORT_BAR,
@@ -518,6 +566,17 @@ class ReflectionTest(unittest.TestCase):
proto = unittest_pb2.TestAllTypes()
self.assertRaises(ValueError, proto.ClearField, 'nonexistent_field')
+ def testClearRemovesChildren(self):
+ # Make sure there aren't any implementation bugs that are only partially
+ # clearing the message (which can happen in the more complex C++
+ # implementation which has parallel message lists).
+ proto = unittest_pb2.TestRequiredForeign()
+ for i in range(10):
+ proto.repeated_message.add()
+ proto2 = unittest_pb2.TestRequiredForeign()
+ proto.CopyFrom(proto2)
+ self.assertRaises(IndexError, lambda: proto.repeated_message[5])
+
def testDisallowedAssignments(self):
# It's illegal to assign values directly to repeated fields
# or to nonrepeated composite fields. Ensure that this fails.
@@ -546,6 +605,30 @@ class ReflectionTest(unittest.TestCase):
self.assertRaises(TypeError, setattr, proto, 'optional_string', 10)
self.assertRaises(TypeError, setattr, proto, 'optional_bytes', 10)
+ def testIntegerTypes(self):
+ def TestGetAndDeserialize(field_name, value, expected_type):
+ proto = unittest_pb2.TestAllTypes()
+ setattr(proto, field_name, value)
+ self.assertTrue(isinstance(getattr(proto, field_name), expected_type))
+ proto2 = unittest_pb2.TestAllTypes()
+ proto2.ParseFromString(proto.SerializeToString())
+ self.assertTrue(isinstance(getattr(proto2, field_name), expected_type))
+
+ TestGetAndDeserialize('optional_int32', 1, int)
+ TestGetAndDeserialize('optional_int32', 1 << 30, int)
+ TestGetAndDeserialize('optional_uint32', 1 << 30, int)
+ if struct.calcsize('L') == 4:
+ # Python only has signed ints, so 32-bit python can't fit an uint32
+ # in an int.
+ TestGetAndDeserialize('optional_uint32', 1 << 31, long)
+ else:
+ # 64-bit python can fit uint32 inside an int
+ TestGetAndDeserialize('optional_uint32', 1 << 31, int)
+ TestGetAndDeserialize('optional_int64', 1 << 30, long)
+ TestGetAndDeserialize('optional_int64', 1 << 60, long)
+ TestGetAndDeserialize('optional_uint64', 1 << 30, long)
+ TestGetAndDeserialize('optional_uint64', 1 << 60, long)
+
def testSingleScalarBoundsChecking(self):
def TestMinAndMaxIntegers(field_name, expected_min, expected_max):
pb = unittest_pb2.TestAllTypes()
@@ -565,29 +648,6 @@ class ReflectionTest(unittest.TestCase):
pb.optional_nested_enum = 1
self.assertEqual(1, pb.optional_nested_enum)
- # Invalid enum values.
- pb.optional_nested_enum = 0
- self.assertEqual(0, pb.optional_nested_enum)
-
- bytes_size_before = pb.ByteSize()
-
- pb.optional_nested_enum = 4
- self.assertEqual(4, pb.optional_nested_enum)
-
- pb.optional_nested_enum = 0
- self.assertEqual(0, pb.optional_nested_enum)
-
- # Make sure that setting the same enum field doesn't just add unknown
- # fields (but overwrites them).
- self.assertEqual(bytes_size_before, pb.ByteSize())
-
- # Is the invalid value preserved after serialization?
- serialized = pb.SerializeToString()
- pb2 = unittest_pb2.TestAllTypes()
- pb2.ParseFromString(serialized)
- self.assertEqual(0, pb2.optional_nested_enum)
- self.assertEqual(pb, pb2)
-
def testRepeatedScalarTypeSafety(self):
proto = unittest_pb2.TestAllTypes()
self.assertRaises(TypeError, proto.repeated_int32.append, 1.1)
@@ -635,6 +695,77 @@ class ReflectionTest(unittest.TestCase):
self.assertEqual(3, proto.BAZ)
self.assertEqual(3, unittest_pb2.TestAllTypes.BAZ)
+ def testEnum_Name(self):
+ self.assertEqual('FOREIGN_FOO',
+ unittest_pb2.ForeignEnum.Name(unittest_pb2.FOREIGN_FOO))
+ self.assertEqual('FOREIGN_BAR',
+ unittest_pb2.ForeignEnum.Name(unittest_pb2.FOREIGN_BAR))
+ self.assertEqual('FOREIGN_BAZ',
+ unittest_pb2.ForeignEnum.Name(unittest_pb2.FOREIGN_BAZ))
+ self.assertRaises(ValueError,
+ unittest_pb2.ForeignEnum.Name, 11312)
+
+ proto = unittest_pb2.TestAllTypes()
+ self.assertEqual('FOO',
+ proto.NestedEnum.Name(proto.FOO))
+ self.assertEqual('FOO',
+ unittest_pb2.TestAllTypes.NestedEnum.Name(proto.FOO))
+ self.assertEqual('BAR',
+ proto.NestedEnum.Name(proto.BAR))
+ self.assertEqual('BAR',
+ unittest_pb2.TestAllTypes.NestedEnum.Name(proto.BAR))
+ self.assertEqual('BAZ',
+ proto.NestedEnum.Name(proto.BAZ))
+ self.assertEqual('BAZ',
+ unittest_pb2.TestAllTypes.NestedEnum.Name(proto.BAZ))
+ self.assertRaises(ValueError,
+ proto.NestedEnum.Name, 11312)
+ self.assertRaises(ValueError,
+ unittest_pb2.TestAllTypes.NestedEnum.Name, 11312)
+
+ def testEnum_Value(self):
+ self.assertEqual(unittest_pb2.FOREIGN_FOO,
+ unittest_pb2.ForeignEnum.Value('FOREIGN_FOO'))
+ self.assertEqual(unittest_pb2.FOREIGN_BAR,
+ unittest_pb2.ForeignEnum.Value('FOREIGN_BAR'))
+ self.assertEqual(unittest_pb2.FOREIGN_BAZ,
+ unittest_pb2.ForeignEnum.Value('FOREIGN_BAZ'))
+ self.assertRaises(ValueError,
+ unittest_pb2.ForeignEnum.Value, 'FO')
+
+ proto = unittest_pb2.TestAllTypes()
+ self.assertEqual(proto.FOO,
+ proto.NestedEnum.Value('FOO'))
+ self.assertEqual(proto.FOO,
+ unittest_pb2.TestAllTypes.NestedEnum.Value('FOO'))
+ self.assertEqual(proto.BAR,
+ proto.NestedEnum.Value('BAR'))
+ self.assertEqual(proto.BAR,
+ unittest_pb2.TestAllTypes.NestedEnum.Value('BAR'))
+ self.assertEqual(proto.BAZ,
+ proto.NestedEnum.Value('BAZ'))
+ self.assertEqual(proto.BAZ,
+ unittest_pb2.TestAllTypes.NestedEnum.Value('BAZ'))
+ self.assertRaises(ValueError,
+ proto.NestedEnum.Value, 'Foo')
+ self.assertRaises(ValueError,
+ unittest_pb2.TestAllTypes.NestedEnum.Value, 'Foo')
+
+ def testEnum_KeysAndValues(self):
+ self.assertEqual(['FOREIGN_FOO', 'FOREIGN_BAR', 'FOREIGN_BAZ'],
+ unittest_pb2.ForeignEnum.keys())
+ self.assertEqual([4, 5, 6],
+ unittest_pb2.ForeignEnum.values())
+ self.assertEqual([('FOREIGN_FOO', 4), ('FOREIGN_BAR', 5),
+ ('FOREIGN_BAZ', 6)],
+ unittest_pb2.ForeignEnum.items())
+
+ proto = unittest_pb2.TestAllTypes()
+ self.assertEqual(['FOO', 'BAR', 'BAZ', 'NEG'], proto.NestedEnum.keys())
+ self.assertEqual([1, 2, 3, -1], proto.NestedEnum.values())
+ self.assertEqual([('FOO', 1), ('BAR', 2), ('BAZ', 3), ('NEG', -1)],
+ proto.NestedEnum.items())
+
def testRepeatedScalars(self):
proto = unittest_pb2.TestAllTypes()
@@ -826,6 +957,35 @@ class ReflectionTest(unittest.TestCase):
self.assertEqual(1, len(proto.repeated_nested_message))
self.assertEqual(23, proto.repeated_nested_message[0].bb)
+ def testRepeatedCompositeRemove(self):
+ proto = unittest_pb2.TestAllTypes()
+
+ self.assertEqual(0, len(proto.repeated_nested_message))
+ m0 = proto.repeated_nested_message.add()
+ # Need to set some differentiating variable so m0 != m1 != m2:
+ m0.bb = len(proto.repeated_nested_message)
+ m1 = proto.repeated_nested_message.add()
+ m1.bb = len(proto.repeated_nested_message)
+ self.assertTrue(m0 != m1)
+ m2 = proto.repeated_nested_message.add()
+ m2.bb = len(proto.repeated_nested_message)
+ self.assertListsEqual([m0, m1, m2], proto.repeated_nested_message)
+
+ self.assertEqual(3, len(proto.repeated_nested_message))
+ proto.repeated_nested_message.remove(m0)
+ self.assertEqual(2, len(proto.repeated_nested_message))
+ self.assertEqual(m1, proto.repeated_nested_message[0])
+ self.assertEqual(m2, proto.repeated_nested_message[1])
+
+ # Removing m0 again or removing None should raise error
+ self.assertRaises(ValueError, proto.repeated_nested_message.remove, m0)
+ self.assertRaises(ValueError, proto.repeated_nested_message.remove, None)
+ self.assertEqual(2, len(proto.repeated_nested_message))
+
+ proto.repeated_nested_message.remove(m2)
+ self.assertEqual(1, len(proto.repeated_nested_message))
+ self.assertEqual(m1, proto.repeated_nested_message[0])
+
def testHandWrittenReflection(self):
# Hand written extensions are only supported by the pure-Python
# implementation of the API.
@@ -856,6 +1016,68 @@ class ReflectionTest(unittest.TestCase):
self.assertEqual(23, myproto_instance.foo_field)
self.assertTrue(myproto_instance.HasField('foo_field'))
+ def testDescriptorProtoSupport(self):
+ # Hand written descriptors/reflection are only supported by the pure-Python
+ # implementation of the API.
+ if api_implementation.Type() != 'python':
+ return
+
+ def AddDescriptorField(proto, field_name, field_type):
+ AddDescriptorField.field_index += 1
+ new_field = proto.field.add()
+ new_field.name = field_name
+ new_field.type = field_type
+ new_field.number = AddDescriptorField.field_index
+ new_field.label = descriptor_pb2.FieldDescriptorProto.LABEL_OPTIONAL
+
+ AddDescriptorField.field_index = 0
+
+ desc_proto = descriptor_pb2.DescriptorProto()
+ desc_proto.name = 'Car'
+ fdp = descriptor_pb2.FieldDescriptorProto
+ AddDescriptorField(desc_proto, 'name', fdp.TYPE_STRING)
+ AddDescriptorField(desc_proto, 'year', fdp.TYPE_INT64)
+ AddDescriptorField(desc_proto, 'automatic', fdp.TYPE_BOOL)
+ AddDescriptorField(desc_proto, 'price', fdp.TYPE_DOUBLE)
+ # Add a repeated field
+ AddDescriptorField.field_index += 1
+ new_field = desc_proto.field.add()
+ new_field.name = 'owners'
+ new_field.type = fdp.TYPE_STRING
+ new_field.number = AddDescriptorField.field_index
+ new_field.label = descriptor_pb2.FieldDescriptorProto.LABEL_REPEATED
+
+ desc = descriptor.MakeDescriptor(desc_proto)
+ self.assertTrue(desc.fields_by_name.has_key('name'))
+ self.assertTrue(desc.fields_by_name.has_key('year'))
+ self.assertTrue(desc.fields_by_name.has_key('automatic'))
+ self.assertTrue(desc.fields_by_name.has_key('price'))
+ self.assertTrue(desc.fields_by_name.has_key('owners'))
+
+ class CarMessage(message.Message):
+ __metaclass__ = reflection.GeneratedProtocolMessageType
+ DESCRIPTOR = desc
+
+ prius = CarMessage()
+ prius.name = 'prius'
+ prius.year = 2010
+ prius.automatic = True
+ prius.price = 25134.75
+ prius.owners.extend(['bob', 'susan'])
+
+ serialized_prius = prius.SerializeToString()
+ new_prius = reflection.ParseMessage(desc, serialized_prius)
+ self.assertTrue(new_prius is not prius)
+ self.assertEqual(prius, new_prius)
+
+ # these are unnecessary assuming message equality works as advertised but
+ # explicitly check to be safe since we're mucking about in metaclass foo
+ self.assertEqual(prius.name, new_prius.name)
+ self.assertEqual(prius.year, new_prius.year)
+ self.assertEqual(prius.automatic, new_prius.automatic)
+ self.assertEqual(prius.price, new_prius.price)
+ self.assertEqual(prius.owners, new_prius.owners)
+
def testTopLevelExtensionsForOptionalScalar(self):
extendee_proto = unittest_pb2.TestAllExtensions()
extension = unittest_pb2.optional_int32_extension
@@ -945,6 +1167,14 @@ class ReflectionTest(unittest.TestCase):
self.assertTrue(required is not extendee_proto.Extensions[extension])
self.assertTrue(not extendee_proto.HasExtension(extension))
+ def testRegisteredExtensions(self):
+ self.assertTrue('protobuf_unittest.optional_int32_extension' in
+ unittest_pb2.TestAllExtensions._extensions_by_name)
+ self.assertTrue(1 in unittest_pb2.TestAllExtensions._extensions_by_number)
+ # Make sure extensions haven't been registered into types that shouldn't
+ # have any.
+ self.assertEquals(0, len(unittest_pb2.TestAllTypes._extensions_by_name))
+
# If message A directly contains message B, and
# a.HasField('b') is currently False, then mutating any
# extension in B should change a.HasField('b') to True
@@ -1020,15 +1250,18 @@ class ReflectionTest(unittest.TestCase):
# Try something that *is* an extension handle, just not for
# this message...
- unknown_handle = more_extensions_pb2.optional_int_extension
- self.assertRaises(KeyError, extendee_proto.HasExtension,
- unknown_handle)
- self.assertRaises(KeyError, extendee_proto.ClearExtension,
- unknown_handle)
- self.assertRaises(KeyError, extendee_proto.Extensions.__getitem__,
- unknown_handle)
- self.assertRaises(KeyError, extendee_proto.Extensions.__setitem__,
- unknown_handle, 5)
+ for unknown_handle in (more_extensions_pb2.optional_int_extension,
+ more_extensions_pb2.optional_message_extension,
+ more_extensions_pb2.repeated_int_extension,
+ more_extensions_pb2.repeated_message_extension):
+ self.assertRaises(KeyError, extendee_proto.HasExtension,
+ unknown_handle)
+ self.assertRaises(KeyError, extendee_proto.ClearExtension,
+ unknown_handle)
+ self.assertRaises(KeyError, extendee_proto.Extensions.__getitem__,
+ unknown_handle)
+ self.assertRaises(KeyError, extendee_proto.Extensions.__setitem__,
+ unknown_handle, 5)
# Try call HasExtension() with a valid handle, but for a
# *repeated* field. (Just as with non-extension repeated
@@ -1241,9 +1474,27 @@ class ReflectionTest(unittest.TestCase):
proto2 = unittest_pb2.TestAllExtensions()
self.assertRaises(TypeError, proto1.CopyFrom, proto2)
+ def testDeepCopy(self):
+ proto1 = unittest_pb2.TestAllTypes()
+ proto1.optional_int32 = 1
+ proto2 = copy.deepcopy(proto1)
+ self.assertEqual(1, proto2.optional_int32)
+
+ proto1.repeated_int32.append(2)
+ proto1.repeated_int32.append(3)
+ container = copy.deepcopy(proto1.repeated_int32)
+ self.assertEqual([2, 3], container)
+
+ # TODO(anuraag): Implement deepcopy for repeated composite / extension dict
+
def testClear(self):
proto = unittest_pb2.TestAllTypes()
- test_util.SetAllFields(proto)
+ # C++ implementation does not support lazy fields right now so leave it
+ # out for now.
+ if api_implementation.Type() == 'python':
+ test_util.SetAllFields(proto)
+ else:
+ test_util.SetAllNonLazyFields(proto)
# Clear the message.
proto.Clear()
self.assertEquals(proto.ByteSize(), 0)
@@ -1259,6 +1510,45 @@ class ReflectionTest(unittest.TestCase):
empty_proto = unittest_pb2.TestAllExtensions()
self.assertEquals(proto, empty_proto)
+ def testDisconnectingBeforeClear(self):
+ proto = unittest_pb2.TestAllTypes()
+ nested = proto.optional_nested_message
+ proto.Clear()
+ self.assertTrue(nested is not proto.optional_nested_message)
+ nested.bb = 23
+ self.assertTrue(not proto.HasField('optional_nested_message'))
+ self.assertEqual(0, proto.optional_nested_message.bb)
+
+ proto = unittest_pb2.TestAllTypes()
+ nested = proto.optional_nested_message
+ nested.bb = 5
+ foreign = proto.optional_foreign_message
+ foreign.c = 6
+
+ proto.Clear()
+ self.assertTrue(nested is not proto.optional_nested_message)
+ self.assertTrue(foreign is not proto.optional_foreign_message)
+ self.assertEqual(5, nested.bb)
+ self.assertEqual(6, foreign.c)
+ nested.bb = 15
+ foreign.c = 16
+ self.assertFalse(proto.HasField('optional_nested_message'))
+ self.assertEqual(0, proto.optional_nested_message.bb)
+ self.assertFalse(proto.HasField('optional_foreign_message'))
+ self.assertEqual(0, proto.optional_foreign_message.c)
+
+ def testOneOf(self):
+ proto = unittest_pb2.TestAllTypes()
+ proto.oneof_uint32 = 10
+ proto.oneof_nested_message.bb = 11
+ self.assertEqual(11, proto.oneof_nested_message.bb)
+ self.assertFalse(proto.HasField('oneof_uint32'))
+ nested = proto.oneof_nested_message
+ proto.oneof_string = 'abc'
+ self.assertEqual('abc', proto.oneof_string)
+ self.assertEqual(11, nested.bb)
+ self.assertFalse(proto.HasField('oneof_nested_message'))
+
def assertInitialized(self, proto):
self.assertTrue(proto.IsInitialized())
# Neither method should raise an exception.
@@ -1329,6 +1619,40 @@ class ReflectionTest(unittest.TestCase):
self.assertFalse(proto.IsInitialized(errors))
self.assertEqual(errors, ['a', 'b', 'c'])
+ @basetest.unittest.skipIf(
+ api_implementation.Type() != 'cpp' or api_implementation.Version() != 2,
+ 'Errors are only available from the most recent C++ implementation.')
+ def testFileDescriptorErrors(self):
+ file_name = 'test_file_descriptor_errors.proto'
+ package_name = 'test_file_descriptor_errors.proto'
+ file_descriptor_proto = descriptor_pb2.FileDescriptorProto()
+ file_descriptor_proto.name = file_name
+ file_descriptor_proto.package = package_name
+ m1 = file_descriptor_proto.message_type.add()
+ m1.name = 'msg1'
+ # Compiles the proto into the C++ descriptor pool
+ descriptor.FileDescriptor(
+ file_name,
+ package_name,
+ serialized_pb=file_descriptor_proto.SerializeToString())
+ # Add a FileDescriptorProto that has duplicate symbols
+ another_file_name = 'another_test_file_descriptor_errors.proto'
+ file_descriptor_proto.name = another_file_name
+ m2 = file_descriptor_proto.message_type.add()
+ m2.name = 'msg2'
+ with self.assertRaises(TypeError) as cm:
+ descriptor.FileDescriptor(
+ another_file_name,
+ package_name,
+ serialized_pb=file_descriptor_proto.SerializeToString())
+ self.assertTrue(hasattr(cm, 'exception'), '%s not raised' %
+ getattr(cm.expected, '__name__', cm.expected))
+ self.assertIn('test_file_descriptor_errors.proto', str(cm.exception))
+ # Error message will say something about this definition being a
+ # duplicate, though we don't check the message exactly to avoid a
+ # dependency on the C++ logging code.
+ self.assertIn('test_file_descriptor_errors.msg1', str(cm.exception))
+
def testStringUTF8Encoding(self):
proto = unittest_pb2.TestAllTypes()
@@ -1346,18 +1670,15 @@ class ReflectionTest(unittest.TestCase):
proto.optional_string = str('Testing')
self.assertEqual(proto.optional_string, unicode('Testing'))
- if api_implementation.Type() == 'python':
- # Values of type 'str' are also accepted as long as they can be
- # encoded in UTF-8.
- self.assertEqual(type(proto.optional_string), str)
-
- # Try to assign a 'str' value which contains bytes that aren't 7-bit ASCII.
- self.assertRaises(ValueError,
- setattr, proto, 'optional_string', str('a\x80a'))
- # Assign a 'str' object which contains a UTF-8 encoded string.
+ # Try to assign a 'bytes' object which contains non-UTF-8.
self.assertRaises(ValueError,
- setattr, proto, 'optional_string', 'Тест')
- # No exception thrown.
+ setattr, proto, 'optional_string', b'a\x80a')
+ # No exception: Assign already encoded UTF-8 bytes to a string field.
+ utf8_bytes = u'Тест'.encode('utf-8')
+ proto.optional_string = utf8_bytes
+ # No exception: Assign the a non-ascii unicode object.
+ proto.optional_string = u'Тест'
+ # No exception thrown (normal str assignment containing ASCII).
proto.optional_string = 'abc'
def testStringUTF8Serialization(self):
@@ -1379,7 +1700,8 @@ class ReflectionTest(unittest.TestCase):
self.assertEqual(proto.ByteSize(), len(serialized))
raw = unittest_mset_pb2.RawMessageSet()
- raw.MergeFromString(serialized)
+ bytes_read = raw.MergeFromString(serialized)
+ self.assertEqual(len(serialized), bytes_read)
message2 = unittest_mset_pb2.TestMessageSetExtension2()
@@ -1390,7 +1712,8 @@ class ReflectionTest(unittest.TestCase):
# Check the actual bytes on the wire.
self.assertTrue(
raw.item[0].message.endswith(test_utf8_bytes))
- message2.MergeFromString(raw.item[0].message)
+ bytes_read = message2.MergeFromString(raw.item[0].message)
+ self.assertEqual(len(raw.item[0].message), bytes_read)
self.assertEqual(type(message2.str), unicode)
self.assertEqual(message2.str, test_utf8)
@@ -1401,17 +1724,22 @@ class ReflectionTest(unittest.TestCase):
# MergeFromString and thus has no way to throw the exception.
#
# The pure Python API always returns objects of type 'unicode' (UTF-8
- # encoded), or 'str' (in 7 bit ASCII).
- bytes = raw.item[0].message.replace(
- test_utf8_bytes, len(test_utf8_bytes) * '\xff')
+ # encoded), or 'bytes' (in 7 bit ASCII).
+ badbytes = raw.item[0].message.replace(
+ test_utf8_bytes, len(test_utf8_bytes) * b'\xff')
unicode_decode_failed = False
try:
- message2.MergeFromString(bytes)
- except UnicodeDecodeError, e:
+ message2.MergeFromString(badbytes)
+ except UnicodeDecodeError:
unicode_decode_failed = True
string_field = message2.str
- self.assertTrue(unicode_decode_failed or type(string_field) == str)
+ self.assertTrue(unicode_decode_failed or type(string_field) is bytes)
+
+ def testBytesInTextFormat(self):
+ proto = unittest_pb2.TestAllTypes(optional_bytes=b'\x00\x7f\x80\xff')
+ self.assertEqual(u'optional_bytes: "\\000\\177\\200\\377"\n',
+ unicode(proto))
def testEmptyNestedMessage(self):
proto = unittest_pb2.TestAllTypes()
@@ -1425,16 +1753,19 @@ class ReflectionTest(unittest.TestCase):
self.assertTrue(proto.HasField('optional_nested_message'))
proto = unittest_pb2.TestAllTypes()
- proto.optional_nested_message.MergeFromString('')
+ bytes_read = proto.optional_nested_message.MergeFromString(b'')
+ self.assertEqual(0, bytes_read)
self.assertTrue(proto.HasField('optional_nested_message'))
proto = unittest_pb2.TestAllTypes()
- proto.optional_nested_message.ParseFromString('')
+ proto.optional_nested_message.ParseFromString(b'')
self.assertTrue(proto.HasField('optional_nested_message'))
serialized = proto.SerializeToString()
proto2 = unittest_pb2.TestAllTypes()
- proto2.MergeFromString(serialized)
+ self.assertEqual(
+ len(serialized),
+ proto2.MergeFromString(serialized))
self.assertTrue(proto2.HasField('optional_nested_message'))
def testSetInParent(self):
@@ -1443,12 +1774,41 @@ class ReflectionTest(unittest.TestCase):
proto.optionalgroup.SetInParent()
self.assertTrue(proto.HasField('optionalgroup'))
+ def testPackageInitializationImport(self):
+ """Test that we can import nested messages from their __init__.py.
+
+ Such setup is not trivial since at the time of processing of __init__.py one
+ can't refer to its submodules by name in code, so expressions like
+ google.protobuf.internal.import_test_package.inner_pb2
+ don't work. They do work in imports, so we have assign an alias at import
+ and then use that alias in generated code.
+ """
+ # We import here since it's the import that used to fail, and we want
+ # the failure to have the right context.
+ # pylint: disable=g-import-not-at-top
+ from google.protobuf.internal import import_test_package
+ # pylint: enable=g-import-not-at-top
+ msg = import_test_package.myproto.Outer()
+ # Just check the default value.
+ self.assertEqual(57, msg.inner.value)
+
+ @basetest.unittest.skipIf(
+ api_implementation.Type() != 'cpp' or api_implementation.Version() != 2,
+ 'CPPv2-specific test')
+ def testBadArguments(self):
+ # Some of these assertions used to segfault.
+ from google.protobuf.pyext import _message
+ self.assertRaises(TypeError, _message.Message._GetFieldDescriptor, 3)
+ self.assertRaises(TypeError, _message.Message._GetExtensionDescriptor, 42)
+ self.assertRaises(TypeError,
+ unittest_pb2.TestAllTypes().__getattribute__, 42)
+
# Since we had so many tests for protocol buffer equality, we broke these out
# into separate TestCase classes.
-class TestAllTypesEqualityTest(unittest.TestCase):
+class TestAllTypesEqualityTest(basetest.TestCase):
def setUp(self):
self.first_proto = unittest_pb2.TestAllTypes()
@@ -1464,7 +1824,7 @@ class TestAllTypesEqualityTest(unittest.TestCase):
self.assertEqual(self.first_proto, self.second_proto)
-class FullProtosEqualityTest(unittest.TestCase):
+class FullProtosEqualityTest(basetest.TestCase):
"""Equality tests using completely-full protos as a starting point."""
@@ -1550,7 +1910,7 @@ class FullProtosEqualityTest(unittest.TestCase):
self.assertEqual(self.first_proto, self.second_proto)
-class ExtensionEqualityTest(unittest.TestCase):
+class ExtensionEqualityTest(basetest.TestCase):
def testExtensionEquality(self):
first_proto = unittest_pb2.TestAllExtensions()
@@ -1583,7 +1943,7 @@ class ExtensionEqualityTest(unittest.TestCase):
self.assertEqual(first_proto, second_proto)
-class MutualRecursionEqualityTest(unittest.TestCase):
+class MutualRecursionEqualityTest(basetest.TestCase):
def testEqualityWithMutualRecursion(self):
first_proto = unittest_pb2.TestMutualRecursionA()
@@ -1595,7 +1955,7 @@ class MutualRecursionEqualityTest(unittest.TestCase):
self.assertEqual(first_proto, second_proto)
-class ByteSizeTest(unittest.TestCase):
+class ByteSizeTest(basetest.TestCase):
def setUp(self):
self.proto = unittest_pb2.TestAllTypes()
@@ -1891,14 +2251,16 @@ class ByteSizeTest(unittest.TestCase):
# * Handling of empty submessages (with and without "has"
# bits set).
-class SerializationTest(unittest.TestCase):
+class SerializationTest(basetest.TestCase):
def testSerializeEmtpyMessage(self):
first_proto = unittest_pb2.TestAllTypes()
second_proto = unittest_pb2.TestAllTypes()
serialized = first_proto.SerializeToString()
self.assertEqual(first_proto.ByteSize(), len(serialized))
- second_proto.MergeFromString(serialized)
+ self.assertEqual(
+ len(serialized),
+ second_proto.MergeFromString(serialized))
self.assertEqual(first_proto, second_proto)
def testSerializeAllFields(self):
@@ -1907,7 +2269,9 @@ class SerializationTest(unittest.TestCase):
test_util.SetAllFields(first_proto)
serialized = first_proto.SerializeToString()
self.assertEqual(first_proto.ByteSize(), len(serialized))
- second_proto.MergeFromString(serialized)
+ self.assertEqual(
+ len(serialized),
+ second_proto.MergeFromString(serialized))
self.assertEqual(first_proto, second_proto)
def testSerializeAllExtensions(self):
@@ -1915,7 +2279,19 @@ class SerializationTest(unittest.TestCase):
second_proto = unittest_pb2.TestAllExtensions()
test_util.SetAllExtensions(first_proto)
serialized = first_proto.SerializeToString()
- second_proto.MergeFromString(serialized)
+ self.assertEqual(
+ len(serialized),
+ second_proto.MergeFromString(serialized))
+ self.assertEqual(first_proto, second_proto)
+
+ def testSerializeWithOptionalGroup(self):
+ first_proto = unittest_pb2.TestAllTypes()
+ second_proto = unittest_pb2.TestAllTypes()
+ first_proto.optionalgroup.a = 242
+ serialized = first_proto.SerializeToString()
+ self.assertEqual(
+ len(serialized),
+ second_proto.MergeFromString(serialized))
self.assertEqual(first_proto, second_proto)
def testSerializeNegativeValues(self):
@@ -2007,7 +2383,9 @@ class SerializationTest(unittest.TestCase):
second_proto.optional_int32 = 100
second_proto.optional_nested_message.bb = 999
- second_proto.MergeFromString(serialized)
+ bytes_parsed = second_proto.MergeFromString(serialized)
+ self.assertEqual(len(serialized), bytes_parsed)
+
# Ensure that we append to repeated fields.
self.assertEqual(['baz', 'foobar'], list(second_proto.repeated_string))
# Ensure that we overwrite nonrepeatd scalars.
@@ -2032,20 +2410,28 @@ class SerializationTest(unittest.TestCase):
raw = unittest_mset_pb2.RawMessageSet()
self.assertEqual(False,
raw.DESCRIPTOR.GetOptions().message_set_wire_format)
- raw.MergeFromString(serialized)
+ self.assertEqual(
+ len(serialized),
+ raw.MergeFromString(serialized))
self.assertEqual(2, len(raw.item))
message1 = unittest_mset_pb2.TestMessageSetExtension1()
- message1.MergeFromString(raw.item[0].message)
+ self.assertEqual(
+ len(raw.item[0].message),
+ message1.MergeFromString(raw.item[0].message))
self.assertEqual(123, message1.i)
message2 = unittest_mset_pb2.TestMessageSetExtension2()
- message2.MergeFromString(raw.item[1].message)
+ self.assertEqual(
+ len(raw.item[1].message),
+ message2.MergeFromString(raw.item[1].message))
self.assertEqual('foo', message2.str)
# Deserialize using the MessageSet wire format.
proto2 = unittest_mset_pb2.TestMessageSet()
- proto2.MergeFromString(serialized)
+ self.assertEqual(
+ len(serialized),
+ proto2.MergeFromString(serialized))
self.assertEqual(123, proto2.Extensions[extension1].i)
self.assertEqual('foo', proto2.Extensions[extension2].str)
@@ -2085,7 +2471,9 @@ class SerializationTest(unittest.TestCase):
# Parse message using the message set wire format.
proto = unittest_mset_pb2.TestMessageSet()
- proto.MergeFromString(serialized)
+ self.assertEqual(
+ len(serialized),
+ proto.MergeFromString(serialized))
# Check that the message parsed well.
extension_message1 = unittest_mset_pb2.TestMessageSetExtension1
@@ -2103,7 +2491,9 @@ class SerializationTest(unittest.TestCase):
proto2 = unittest_pb2.TestEmptyMessage()
# Parsing this message should succeed.
- proto2.MergeFromString(serialized)
+ self.assertEqual(
+ len(serialized),
+ proto2.MergeFromString(serialized))
# Now test with a int64 field set.
proto = unittest_pb2.TestAllTypes()
@@ -2113,13 +2503,15 @@ class SerializationTest(unittest.TestCase):
# unknown.
proto2 = unittest_pb2.TestEmptyMessage()
# Parsing this message should succeed.
- proto2.MergeFromString(serialized)
+ self.assertEqual(
+ len(serialized),
+ proto2.MergeFromString(serialized))
def _CheckRaises(self, exc_class, callable_obj, exception):
"""This method checks if the excpetion type and message are as expected."""
try:
callable_obj()
- except exc_class, ex:
+ except exc_class as ex:
# Check if the exception message is the right one.
self.assertEqual(exception, str(ex))
return
@@ -2131,15 +2523,22 @@ class SerializationTest(unittest.TestCase):
self._CheckRaises(
message.EncodeError,
proto.SerializeToString,
- 'Message is missing required fields: a,b,c')
+ 'Message protobuf_unittest.TestRequired is missing required fields: '
+ 'a,b,c')
# Shouldn't raise exceptions.
partial = proto.SerializePartialToString()
+ proto2 = unittest_pb2.TestRequired()
+ self.assertFalse(proto2.HasField('a'))
+ # proto2 ParseFromString does not check that required fields are set.
+ proto2.ParseFromString(partial)
+ self.assertFalse(proto2.HasField('a'))
+
proto.a = 1
self._CheckRaises(
message.EncodeError,
proto.SerializeToString,
- 'Message is missing required fields: b,c')
+ 'Message protobuf_unittest.TestRequired is missing required fields: b,c')
# Shouldn't raise exceptions.
partial = proto.SerializePartialToString()
@@ -2147,7 +2546,7 @@ class SerializationTest(unittest.TestCase):
self._CheckRaises(
message.EncodeError,
proto.SerializeToString,
- 'Message is missing required fields: c')
+ 'Message protobuf_unittest.TestRequired is missing required fields: c')
# Shouldn't raise exceptions.
partial = proto.SerializePartialToString()
@@ -2157,11 +2556,15 @@ class SerializationTest(unittest.TestCase):
partial = proto.SerializePartialToString()
proto2 = unittest_pb2.TestRequired()
- proto2.MergeFromString(serialized)
+ self.assertEqual(
+ len(serialized),
+ proto2.MergeFromString(serialized))
self.assertEqual(1, proto2.a)
self.assertEqual(2, proto2.b)
self.assertEqual(3, proto2.c)
- proto2.ParseFromString(partial)
+ self.assertEqual(
+ len(partial),
+ proto2.MergeFromString(partial))
self.assertEqual(1, proto2.a)
self.assertEqual(2, proto2.b)
self.assertEqual(3, proto2.c)
@@ -2176,7 +2579,8 @@ class SerializationTest(unittest.TestCase):
self._CheckRaises(
message.EncodeError,
proto.SerializeToString,
- 'Message is missing required fields: '
+ 'Message protobuf_unittest.TestRequiredForeign '
+ 'is missing required fields: '
'optional_message.b,optional_message.c')
proto.optional_message.b = 2
@@ -2188,7 +2592,7 @@ class SerializationTest(unittest.TestCase):
self._CheckRaises(
message.EncodeError,
proto.SerializeToString,
- 'Message is missing required fields: '
+ 'Message protobuf_unittest.TestRequiredForeign is missing required fields: '
'repeated_message[0].b,repeated_message[0].c,'
'repeated_message[1].a,repeated_message[1].c')
@@ -2228,7 +2632,9 @@ class SerializationTest(unittest.TestCase):
second_proto.packed_double.extend([1.0, 2.0])
second_proto.packed_sint32.append(4)
- second_proto.MergeFromString(serialized)
+ self.assertEqual(
+ len(serialized),
+ second_proto.MergeFromString(serialized))
self.assertEqual([3, 1, 2], second_proto.packed_int32)
self.assertEqual([1.0, 2.0, 3.0], second_proto.packed_double)
self.assertEqual([4], second_proto.packed_sint32)
@@ -2261,7 +2667,10 @@ class SerializationTest(unittest.TestCase):
unpacked = unittest_pb2.TestUnpackedTypes()
test_util.SetAllUnpackedFields(unpacked)
packed = unittest_pb2.TestPackedTypes()
- packed.MergeFromString(unpacked.SerializeToString())
+ serialized = unpacked.SerializeToString()
+ self.assertEqual(
+ len(serialized),
+ packed.MergeFromString(serialized))
expected = unittest_pb2.TestPackedTypes()
test_util.SetAllPackedFields(expected)
self.assertEqual(expected, packed)
@@ -2270,7 +2679,10 @@ class SerializationTest(unittest.TestCase):
packed = unittest_pb2.TestPackedTypes()
test_util.SetAllPackedFields(packed)
unpacked = unittest_pb2.TestUnpackedTypes()
- unpacked.MergeFromString(packed.SerializeToString())
+ serialized = packed.SerializeToString()
+ self.assertEqual(
+ len(serialized),
+ unpacked.MergeFromString(serialized))
expected = unittest_pb2.TestUnpackedTypes()
test_util.SetAllUnpackedFields(expected)
self.assertEqual(expected, unpacked)
@@ -2322,7 +2734,7 @@ class SerializationTest(unittest.TestCase):
optional_int32=1,
optional_string='foo',
optional_bool=True,
- optional_bytes='bar',
+ optional_bytes=b'bar',
optional_nested_message=unittest_pb2.TestAllTypes.NestedMessage(bb=1),
optional_foreign_message=unittest_pb2.ForeignMessage(c=1),
optional_nested_enum=unittest_pb2.TestAllTypes.FOO,
@@ -2340,7 +2752,7 @@ class SerializationTest(unittest.TestCase):
self.assertEqual(1, proto.optional_int32)
self.assertEqual('foo', proto.optional_string)
self.assertEqual(True, proto.optional_bool)
- self.assertEqual('bar', proto.optional_bytes)
+ self.assertEqual(b'bar', proto.optional_bytes)
self.assertEqual(1, proto.optional_nested_message.bb)
self.assertEqual(1, proto.optional_foreign_message.c)
self.assertEqual(unittest_pb2.TestAllTypes.FOO,
@@ -2390,7 +2802,7 @@ class SerializationTest(unittest.TestCase):
self.assertEqual(3, proto.repeated_int32[2])
-class OptionsTest(unittest.TestCase):
+class OptionsTest(basetest.TestCase):
def testMessageOptions(self):
proto = unittest_mset_pb2.TestMessageSet()
@@ -2417,5 +2829,138 @@ class OptionsTest(unittest.TestCase):
+class ClassAPITest(basetest.TestCase):
+
+ @basetest.unittest.skipIf(
+ api_implementation.Type() == 'cpp' and api_implementation.Version() == 2,
+ 'C++ implementation requires a call to MakeDescriptor()')
+ def testMakeClassWithNestedDescriptor(self):
+ leaf_desc = descriptor.Descriptor('leaf', 'package.parent.child.leaf', '',
+ containing_type=None, fields=[],
+ nested_types=[], enum_types=[],
+ extensions=[])
+ child_desc = descriptor.Descriptor('child', 'package.parent.child', '',
+ containing_type=None, fields=[],
+ nested_types=[leaf_desc], enum_types=[],
+ extensions=[])
+ sibling_desc = descriptor.Descriptor('sibling', 'package.parent.sibling',
+ '', containing_type=None, fields=[],
+ nested_types=[], enum_types=[],
+ extensions=[])
+ parent_desc = descriptor.Descriptor('parent', 'package.parent', '',
+ containing_type=None, fields=[],
+ nested_types=[child_desc, sibling_desc],
+ enum_types=[], extensions=[])
+ message_class = reflection.MakeClass(parent_desc)
+ self.assertIn('child', message_class.__dict__)
+ self.assertIn('sibling', message_class.__dict__)
+ self.assertIn('leaf', message_class.child.__dict__)
+
+ def _GetSerializedFileDescriptor(self, name):
+ """Get a serialized representation of a test FileDescriptorProto.
+
+ Args:
+ name: All calls to this must use a unique message name, to avoid
+ collisions in the cpp descriptor pool.
+ Returns:
+ A string containing the serialized form of a test FileDescriptorProto.
+ """
+ file_descriptor_str = (
+ 'message_type {'
+ ' name: "' + name + '"'
+ ' field {'
+ ' name: "flat"'
+ ' number: 1'
+ ' label: LABEL_REPEATED'
+ ' type: TYPE_UINT32'
+ ' }'
+ ' field {'
+ ' name: "bar"'
+ ' number: 2'
+ ' label: LABEL_OPTIONAL'
+ ' type: TYPE_MESSAGE'
+ ' type_name: "Bar"'
+ ' }'
+ ' nested_type {'
+ ' name: "Bar"'
+ ' field {'
+ ' name: "baz"'
+ ' number: 3'
+ ' label: LABEL_OPTIONAL'
+ ' type: TYPE_MESSAGE'
+ ' type_name: "Baz"'
+ ' }'
+ ' nested_type {'
+ ' name: "Baz"'
+ ' enum_type {'
+ ' name: "deep_enum"'
+ ' value {'
+ ' name: "VALUE_A"'
+ ' number: 0'
+ ' }'
+ ' }'
+ ' field {'
+ ' name: "deep"'
+ ' number: 4'
+ ' label: LABEL_OPTIONAL'
+ ' type: TYPE_UINT32'
+ ' }'
+ ' }'
+ ' }'
+ '}')
+ file_descriptor = descriptor_pb2.FileDescriptorProto()
+ text_format.Merge(file_descriptor_str, file_descriptor)
+ return file_descriptor.SerializeToString()
+
+ def testParsingFlatClassWithExplicitClassDeclaration(self):
+ """Test that the generated class can parse a flat message."""
+ file_descriptor = descriptor_pb2.FileDescriptorProto()
+ file_descriptor.ParseFromString(self._GetSerializedFileDescriptor('A'))
+ msg_descriptor = descriptor.MakeDescriptor(
+ file_descriptor.message_type[0])
+
+ class MessageClass(message.Message):
+ __metaclass__ = reflection.GeneratedProtocolMessageType
+ DESCRIPTOR = msg_descriptor
+ msg = MessageClass()
+ msg_str = (
+ 'flat: 0 '
+ 'flat: 1 '
+ 'flat: 2 ')
+ text_format.Merge(msg_str, msg)
+ self.assertEqual(msg.flat, [0, 1, 2])
+
+ def testParsingFlatClass(self):
+ """Test that the generated class can parse a flat message."""
+ file_descriptor = descriptor_pb2.FileDescriptorProto()
+ file_descriptor.ParseFromString(self._GetSerializedFileDescriptor('B'))
+ msg_descriptor = descriptor.MakeDescriptor(
+ file_descriptor.message_type[0])
+ msg_class = reflection.MakeClass(msg_descriptor)
+ msg = msg_class()
+ msg_str = (
+ 'flat: 0 '
+ 'flat: 1 '
+ 'flat: 2 ')
+ text_format.Merge(msg_str, msg)
+ self.assertEqual(msg.flat, [0, 1, 2])
+
+ def testParsingNestedClass(self):
+ """Test that the generated class can parse a nested message."""
+ file_descriptor = descriptor_pb2.FileDescriptorProto()
+ file_descriptor.ParseFromString(self._GetSerializedFileDescriptor('C'))
+ msg_descriptor = descriptor.MakeDescriptor(
+ file_descriptor.message_type[0])
+ msg_class = reflection.MakeClass(msg_descriptor)
+ msg = msg_class()
+ msg_str = (
+ 'bar {'
+ ' baz {'
+ ' deep: 4'
+ ' }'
+ '}')
+ text_format.Merge(msg_str, msg)
+ self.assertEqual(msg.bar.baz.deep, 4)
+
if __name__ == '__main__':
- unittest.main()
+ basetest.main()
diff --git a/python/google/protobuf/internal/service_reflection_test.py b/python/google/protobuf/internal/service_reflection_test.py
index e04f8252..d066ae70 100755
--- a/python/google/protobuf/internal/service_reflection_test.py
+++ b/python/google/protobuf/internal/service_reflection_test.py
@@ -2,7 +2,7 @@
#
# Protocol Buffers - Google's data interchange format
# Copyright 2008 Google Inc. All rights reserved.
-# http://code.google.com/p/protobuf/
+# 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
@@ -34,13 +34,13 @@
__author__ = 'petar@google.com (Petar Petrov)'
-import unittest
+from google.apputils import basetest
from google.protobuf import unittest_pb2
from google.protobuf import service_reflection
from google.protobuf import service
-class FooUnitTest(unittest.TestCase):
+class FooUnitTest(basetest.TestCase):
def testService(self):
class MockRpcChannel(service.RpcChannel):
@@ -118,7 +118,7 @@ class FooUnitTest(unittest.TestCase):
rpc_controller = 'controller'
request = 'request'
- # GetDescriptor now static, still works as instance method for compatability
+ # GetDescriptor now static, still works as instance method for compatibility
self.assertEqual(unittest_pb2.TestService_Stub.GetDescriptor(),
stub.GetDescriptor())
@@ -133,4 +133,4 @@ class FooUnitTest(unittest.TestCase):
if __name__ == '__main__':
- unittest.main()
+ basetest.main()
diff --git a/python/google/protobuf/internal/symbol_database_test.py b/python/google/protobuf/internal/symbol_database_test.py
new file mode 100644
index 00000000..47572d58
--- /dev/null
+++ b/python/google/protobuf/internal/symbol_database_test.py
@@ -0,0 +1,120 @@
+#! /usr/bin/python
+#
+# 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.
+
+"""Tests for google.protobuf.symbol_database."""
+
+from google.apputils import basetest
+from google.protobuf import unittest_pb2
+from google.protobuf import symbol_database
+
+
+class SymbolDatabaseTest(basetest.TestCase):
+
+ def _Database(self):
+ db = symbol_database.SymbolDatabase()
+ # Register representative types from unittest_pb2.
+ db.RegisterFileDescriptor(unittest_pb2.DESCRIPTOR)
+ db.RegisterMessage(unittest_pb2.TestAllTypes)
+ db.RegisterMessage(unittest_pb2.TestAllTypes.NestedMessage)
+ db.RegisterMessage(unittest_pb2.TestAllTypes.OptionalGroup)
+ db.RegisterMessage(unittest_pb2.TestAllTypes.RepeatedGroup)
+ db.RegisterEnumDescriptor(unittest_pb2.ForeignEnum.DESCRIPTOR)
+ db.RegisterEnumDescriptor(unittest_pb2.TestAllTypes.NestedEnum.DESCRIPTOR)
+ return db
+
+ def testGetPrototype(self):
+ instance = self._Database().GetPrototype(
+ unittest_pb2.TestAllTypes.DESCRIPTOR)
+ self.assertTrue(instance is unittest_pb2.TestAllTypes)
+
+ def testGetMessages(self):
+ messages = self._Database().GetMessages(
+ ['google/protobuf/unittest.proto'])
+ self.assertTrue(
+ unittest_pb2.TestAllTypes is
+ messages['protobuf_unittest.TestAllTypes'])
+
+ def testGetSymbol(self):
+ self.assertEquals(
+ unittest_pb2.TestAllTypes, self._Database().GetSymbol(
+ 'protobuf_unittest.TestAllTypes'))
+ self.assertEquals(
+ unittest_pb2.TestAllTypes.NestedMessage, self._Database().GetSymbol(
+ 'protobuf_unittest.TestAllTypes.NestedMessage'))
+ self.assertEquals(
+ unittest_pb2.TestAllTypes.OptionalGroup, self._Database().GetSymbol(
+ 'protobuf_unittest.TestAllTypes.OptionalGroup'))
+ self.assertEquals(
+ unittest_pb2.TestAllTypes.RepeatedGroup, self._Database().GetSymbol(
+ 'protobuf_unittest.TestAllTypes.RepeatedGroup'))
+
+ def testEnums(self):
+ # Check registration of types in the pool.
+ self.assertEquals(
+ 'protobuf_unittest.ForeignEnum',
+ self._Database().pool.FindEnumTypeByName(
+ 'protobuf_unittest.ForeignEnum').full_name)
+ self.assertEquals(
+ 'protobuf_unittest.TestAllTypes.NestedEnum',
+ self._Database().pool.FindEnumTypeByName(
+ 'protobuf_unittest.TestAllTypes.NestedEnum').full_name)
+
+ def testFindMessageTypeByName(self):
+ self.assertEquals(
+ 'protobuf_unittest.TestAllTypes',
+ self._Database().pool.FindMessageTypeByName(
+ 'protobuf_unittest.TestAllTypes').full_name)
+ self.assertEquals(
+ 'protobuf_unittest.TestAllTypes.NestedMessage',
+ self._Database().pool.FindMessageTypeByName(
+ 'protobuf_unittest.TestAllTypes.NestedMessage').full_name)
+
+ def testFindFindContainingSymbol(self):
+ # Lookup based on either enum or message.
+ self.assertEquals(
+ 'google/protobuf/unittest.proto',
+ self._Database().pool.FindFileContainingSymbol(
+ 'protobuf_unittest.TestAllTypes.NestedEnum').name)
+ self.assertEquals(
+ 'google/protobuf/unittest.proto',
+ self._Database().pool.FindFileContainingSymbol(
+ 'protobuf_unittest.TestAllTypes').name)
+
+ def testFindFileByName(self):
+ self.assertEquals(
+ 'google/protobuf/unittest.proto',
+ self._Database().pool.FindFileByName(
+ 'google/protobuf/unittest.proto').name)
+
+
+if __name__ == '__main__':
+ basetest.main()
diff --git a/python/google/protobuf/internal/test_bad_identifiers.proto b/python/google/protobuf/internal/test_bad_identifiers.proto
new file mode 100644
index 00000000..c4860ea8
--- /dev/null
+++ b/python/google/protobuf/internal/test_bad_identifiers.proto
@@ -0,0 +1,53 @@
+// 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: kenton@google.com (Kenton Varda)
+
+syntax = "proto2";
+
+package protobuf_unittest;
+
+option py_generic_services = true;
+
+message TestBadIdentifiers {
+ extensions 100 to max;
+}
+
+// Make sure these reasonable extension names don't conflict with internal
+// variables.
+extend TestBadIdentifiers {
+ optional string message = 100 [default="foo"];
+ optional string descriptor = 101 [default="bar"];
+ optional string reflection = 102 [default="baz"];
+ optional string service = 103 [default="qux"];
+}
+
+message AnotherMessage {}
+service AnotherService {}
diff --git a/python/google/protobuf/internal/test_util.py b/python/google/protobuf/internal/test_util.py
index 1df16194..d84e3836 100755
--- a/python/google/protobuf/internal/test_util.py
+++ b/python/google/protobuf/internal/test_util.py
@@ -1,6 +1,6 @@
# Protocol Buffers - Google's data interchange format
# Copyright 2008 Google Inc. All rights reserved.
-# http://code.google.com/p/protobuf/
+# 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
@@ -40,13 +40,19 @@ import os.path
from google.protobuf import unittest_import_pb2
from google.protobuf import unittest_pb2
+from google.protobuf import descriptor_pb2
+# Tests whether the given TestAllTypes message is proto2 or not.
+# This is used to gate several fields/features that only exist
+# for the proto2 version of the message.
+def IsProto2(message):
+ return message.DESCRIPTOR.syntax == "proto2"
-def SetAllFields(message):
- """Sets every field in the message to a unique value.
+def SetAllNonLazyFields(message):
+ """Sets every non-lazy field in the message to a unique value.
Args:
- message: A unittest_pb2.TestAllTypes instance.
+ message: A TestAllTypes instance.
"""
#
@@ -66,26 +72,22 @@ def SetAllFields(message):
message.optional_float = 111
message.optional_double = 112
message.optional_bool = True
- # TODO(robinson): Firmly spec out and test how
- # protos interact with unicode. One specific example:
- # what happens if we change the literal below to
- # u'115'? What *should* happen? Still some discussion
- # to finish with Kenton about bytes vs. strings
- # and forcing everything to be utf8. :-/
- message.optional_string = '115'
- message.optional_bytes = '116'
+ message.optional_string = u'115'
+ message.optional_bytes = b'116'
message.optionalgroup.a = 117
message.optional_nested_message.bb = 118
message.optional_foreign_message.c = 119
message.optional_import_message.d = 120
+ message.optional_public_import_message.e = 126
message.optional_nested_enum = unittest_pb2.TestAllTypes.BAZ
message.optional_foreign_enum = unittest_pb2.FOREIGN_BAZ
- message.optional_import_enum = unittest_import_pb2.IMPORT_BAZ
+ if IsProto2(message):
+ message.optional_import_enum = unittest_import_pb2.IMPORT_BAZ
- message.optional_string_piece = '124'
- message.optional_cord = '125'
+ message.optional_string_piece = u'124'
+ message.optional_cord = u'125'
#
# Repeated fields.
@@ -104,20 +106,22 @@ def SetAllFields(message):
message.repeated_float.append(211)
message.repeated_double.append(212)
message.repeated_bool.append(True)
- message.repeated_string.append('215')
- message.repeated_bytes.append('216')
+ message.repeated_string.append(u'215')
+ message.repeated_bytes.append(b'216')
message.repeatedgroup.add().a = 217
message.repeated_nested_message.add().bb = 218
message.repeated_foreign_message.add().c = 219
message.repeated_import_message.add().d = 220
+ message.repeated_lazy_message.add().bb = 227
message.repeated_nested_enum.append(unittest_pb2.TestAllTypes.BAR)
message.repeated_foreign_enum.append(unittest_pb2.FOREIGN_BAR)
- message.repeated_import_enum.append(unittest_import_pb2.IMPORT_BAR)
+ if IsProto2(message):
+ message.repeated_import_enum.append(unittest_import_pb2.IMPORT_BAR)
- message.repeated_string_piece.append('224')
- message.repeated_cord.append('225')
+ message.repeated_string_piece.append(u'224')
+ message.repeated_cord.append(u'225')
# Add a second one of each field.
message.repeated_int32.append(301)
@@ -133,47 +137,60 @@ def SetAllFields(message):
message.repeated_float.append(311)
message.repeated_double.append(312)
message.repeated_bool.append(False)
- message.repeated_string.append('315')
- message.repeated_bytes.append('316')
+ message.repeated_string.append(u'315')
+ message.repeated_bytes.append(b'316')
message.repeatedgroup.add().a = 317
message.repeated_nested_message.add().bb = 318
message.repeated_foreign_message.add().c = 319
message.repeated_import_message.add().d = 320
+ message.repeated_lazy_message.add().bb = 327
message.repeated_nested_enum.append(unittest_pb2.TestAllTypes.BAZ)
message.repeated_foreign_enum.append(unittest_pb2.FOREIGN_BAZ)
- message.repeated_import_enum.append(unittest_import_pb2.IMPORT_BAZ)
+ if IsProto2(message):
+ message.repeated_import_enum.append(unittest_import_pb2.IMPORT_BAZ)
- message.repeated_string_piece.append('324')
- message.repeated_cord.append('325')
+ message.repeated_string_piece.append(u'324')
+ message.repeated_cord.append(u'325')
#
# Fields that have defaults.
#
- message.default_int32 = 401
- message.default_int64 = 402
- message.default_uint32 = 403
- message.default_uint64 = 404
- message.default_sint32 = 405
- message.default_sint64 = 406
- message.default_fixed32 = 407
- message.default_fixed64 = 408
- message.default_sfixed32 = 409
- message.default_sfixed64 = 410
- message.default_float = 411
- message.default_double = 412
- message.default_bool = False
- message.default_string = '415'
- message.default_bytes = '416'
-
- message.default_nested_enum = unittest_pb2.TestAllTypes.FOO
- message.default_foreign_enum = unittest_pb2.FOREIGN_FOO
- message.default_import_enum = unittest_import_pb2.IMPORT_FOO
-
- message.default_string_piece = '424'
- message.default_cord = '425'
+ if IsProto2(message):
+ message.default_int32 = 401
+ message.default_int64 = 402
+ message.default_uint32 = 403
+ message.default_uint64 = 404
+ message.default_sint32 = 405
+ message.default_sint64 = 406
+ message.default_fixed32 = 407
+ message.default_fixed64 = 408
+ message.default_sfixed32 = 409
+ message.default_sfixed64 = 410
+ message.default_float = 411
+ message.default_double = 412
+ message.default_bool = False
+ message.default_string = '415'
+ message.default_bytes = b'416'
+
+ message.default_nested_enum = unittest_pb2.TestAllTypes.FOO
+ message.default_foreign_enum = unittest_pb2.FOREIGN_FOO
+ message.default_import_enum = unittest_import_pb2.IMPORT_FOO
+
+ message.default_string_piece = '424'
+ message.default_cord = '425'
+
+ message.oneof_uint32 = 601
+ message.oneof_nested_message.bb = 602
+ message.oneof_string = '603'
+ message.oneof_bytes = b'604'
+
+
+def SetAllFields(message):
+ SetAllNonLazyFields(message)
+ message.optional_lazy_message.bb = 127
def SetAllExtensions(message):
@@ -204,21 +221,23 @@ def SetAllExtensions(message):
extensions[pb2.optional_float_extension] = 111
extensions[pb2.optional_double_extension] = 112
extensions[pb2.optional_bool_extension] = True
- extensions[pb2.optional_string_extension] = '115'
- extensions[pb2.optional_bytes_extension] = '116'
+ extensions[pb2.optional_string_extension] = u'115'
+ extensions[pb2.optional_bytes_extension] = b'116'
extensions[pb2.optionalgroup_extension].a = 117
extensions[pb2.optional_nested_message_extension].bb = 118
extensions[pb2.optional_foreign_message_extension].c = 119
extensions[pb2.optional_import_message_extension].d = 120
+ extensions[pb2.optional_public_import_message_extension].e = 126
+ extensions[pb2.optional_lazy_message_extension].bb = 127
extensions[pb2.optional_nested_enum_extension] = pb2.TestAllTypes.BAZ
extensions[pb2.optional_nested_enum_extension] = pb2.TestAllTypes.BAZ
extensions[pb2.optional_foreign_enum_extension] = pb2.FOREIGN_BAZ
extensions[pb2.optional_import_enum_extension] = import_pb2.IMPORT_BAZ
- extensions[pb2.optional_string_piece_extension] = '124'
- extensions[pb2.optional_cord_extension] = '125'
+ extensions[pb2.optional_string_piece_extension] = u'124'
+ extensions[pb2.optional_cord_extension] = u'125'
#
# Repeated fields.
@@ -237,20 +256,21 @@ def SetAllExtensions(message):
extensions[pb2.repeated_float_extension].append(211)
extensions[pb2.repeated_double_extension].append(212)
extensions[pb2.repeated_bool_extension].append(True)
- extensions[pb2.repeated_string_extension].append('215')
- extensions[pb2.repeated_bytes_extension].append('216')
+ extensions[pb2.repeated_string_extension].append(u'215')
+ extensions[pb2.repeated_bytes_extension].append(b'216')
extensions[pb2.repeatedgroup_extension].add().a = 217
extensions[pb2.repeated_nested_message_extension].add().bb = 218
extensions[pb2.repeated_foreign_message_extension].add().c = 219
extensions[pb2.repeated_import_message_extension].add().d = 220
+ extensions[pb2.repeated_lazy_message_extension].add().bb = 227
extensions[pb2.repeated_nested_enum_extension].append(pb2.TestAllTypes.BAR)
extensions[pb2.repeated_foreign_enum_extension].append(pb2.FOREIGN_BAR)
extensions[pb2.repeated_import_enum_extension].append(import_pb2.IMPORT_BAR)
- extensions[pb2.repeated_string_piece_extension].append('224')
- extensions[pb2.repeated_cord_extension].append('225')
+ extensions[pb2.repeated_string_piece_extension].append(u'224')
+ extensions[pb2.repeated_cord_extension].append(u'225')
# Append a second one of each field.
extensions[pb2.repeated_int32_extension].append(301)
@@ -266,20 +286,21 @@ def SetAllExtensions(message):
extensions[pb2.repeated_float_extension].append(311)
extensions[pb2.repeated_double_extension].append(312)
extensions[pb2.repeated_bool_extension].append(False)
- extensions[pb2.repeated_string_extension].append('315')
- extensions[pb2.repeated_bytes_extension].append('316')
+ extensions[pb2.repeated_string_extension].append(u'315')
+ extensions[pb2.repeated_bytes_extension].append(b'316')
extensions[pb2.repeatedgroup_extension].add().a = 317
extensions[pb2.repeated_nested_message_extension].add().bb = 318
extensions[pb2.repeated_foreign_message_extension].add().c = 319
extensions[pb2.repeated_import_message_extension].add().d = 320
+ extensions[pb2.repeated_lazy_message_extension].add().bb = 327
extensions[pb2.repeated_nested_enum_extension].append(pb2.TestAllTypes.BAZ)
extensions[pb2.repeated_foreign_enum_extension].append(pb2.FOREIGN_BAZ)
extensions[pb2.repeated_import_enum_extension].append(import_pb2.IMPORT_BAZ)
- extensions[pb2.repeated_string_piece_extension].append('324')
- extensions[pb2.repeated_cord_extension].append('325')
+ extensions[pb2.repeated_string_piece_extension].append(u'324')
+ extensions[pb2.repeated_cord_extension].append(u'325')
#
# Fields with defaults.
@@ -298,16 +319,21 @@ def SetAllExtensions(message):
extensions[pb2.default_float_extension] = 411
extensions[pb2.default_double_extension] = 412
extensions[pb2.default_bool_extension] = False
- extensions[pb2.default_string_extension] = '415'
- extensions[pb2.default_bytes_extension] = '416'
+ extensions[pb2.default_string_extension] = u'415'
+ extensions[pb2.default_bytes_extension] = b'416'
extensions[pb2.default_nested_enum_extension] = pb2.TestAllTypes.FOO
extensions[pb2.default_foreign_enum_extension] = pb2.FOREIGN_FOO
extensions[pb2.default_import_enum_extension] = import_pb2.IMPORT_FOO
- extensions[pb2.default_string_piece_extension] = '424'
+ extensions[pb2.default_string_piece_extension] = u'424'
extensions[pb2.default_cord_extension] = '425'
+ extensions[pb2.oneof_uint32_extension] = 601
+ extensions[pb2.oneof_nested_message_extension].bb = 602
+ extensions[pb2.oneof_string_extension] = u'603'
+ extensions[pb2.oneof_bytes_extension] = b'604'
+
def SetAllFieldsAndExtensions(message):
"""Sets every field and extension in the message to a unique value.
@@ -346,7 +372,7 @@ def ExpectAllFieldsAndExtensionsInOrder(serialized):
message.my_float = 1.0
expected_strings.append(message.SerializeToString())
message.Clear()
- expected = ''.join(expected_strings)
+ expected = b''.join(expected_strings)
if expected != serialized:
raise ValueError('Expected %r, found %r' % (expected, serialized))
@@ -382,7 +408,8 @@ def ExpectAllFieldsSet(test_case, message):
test_case.assertTrue(message.HasField('optional_nested_enum'))
test_case.assertTrue(message.HasField('optional_foreign_enum'))
- test_case.assertTrue(message.HasField('optional_import_enum'))
+ if IsProto2(message):
+ test_case.assertTrue(message.HasField('optional_import_enum'))
test_case.assertTrue(message.HasField('optional_string_piece'))
test_case.assertTrue(message.HasField('optional_cord'))
@@ -401,19 +428,22 @@ def ExpectAllFieldsSet(test_case, message):
test_case.assertEqual(112, message.optional_double)
test_case.assertEqual(True, message.optional_bool)
test_case.assertEqual('115', message.optional_string)
- test_case.assertEqual('116', message.optional_bytes)
+ test_case.assertEqual(b'116', message.optional_bytes)
test_case.assertEqual(117, message.optionalgroup.a)
test_case.assertEqual(118, message.optional_nested_message.bb)
test_case.assertEqual(119, message.optional_foreign_message.c)
test_case.assertEqual(120, message.optional_import_message.d)
+ test_case.assertEqual(126, message.optional_public_import_message.e)
+ test_case.assertEqual(127, message.optional_lazy_message.bb)
test_case.assertEqual(unittest_pb2.TestAllTypes.BAZ,
message.optional_nested_enum)
test_case.assertEqual(unittest_pb2.FOREIGN_BAZ,
message.optional_foreign_enum)
- test_case.assertEqual(unittest_import_pb2.IMPORT_BAZ,
- message.optional_import_enum)
+ if IsProto2(message):
+ test_case.assertEqual(unittest_import_pb2.IMPORT_BAZ,
+ message.optional_import_enum)
# -----------------------------------------------------------------
@@ -439,7 +469,8 @@ def ExpectAllFieldsSet(test_case, message):
test_case.assertEqual(2, len(message.repeated_import_message))
test_case.assertEqual(2, len(message.repeated_nested_enum))
test_case.assertEqual(2, len(message.repeated_foreign_enum))
- test_case.assertEqual(2, len(message.repeated_import_enum))
+ if IsProto2(message):
+ test_case.assertEqual(2, len(message.repeated_import_enum))
test_case.assertEqual(2, len(message.repeated_string_piece))
test_case.assertEqual(2, len(message.repeated_cord))
@@ -458,19 +489,21 @@ def ExpectAllFieldsSet(test_case, message):
test_case.assertEqual(212, message.repeated_double[0])
test_case.assertEqual(True, message.repeated_bool[0])
test_case.assertEqual('215', message.repeated_string[0])
- test_case.assertEqual('216', message.repeated_bytes[0])
+ test_case.assertEqual(b'216', message.repeated_bytes[0])
test_case.assertEqual(217, message.repeatedgroup[0].a)
test_case.assertEqual(218, message.repeated_nested_message[0].bb)
test_case.assertEqual(219, message.repeated_foreign_message[0].c)
test_case.assertEqual(220, message.repeated_import_message[0].d)
+ test_case.assertEqual(227, message.repeated_lazy_message[0].bb)
test_case.assertEqual(unittest_pb2.TestAllTypes.BAR,
message.repeated_nested_enum[0])
test_case.assertEqual(unittest_pb2.FOREIGN_BAR,
message.repeated_foreign_enum[0])
- test_case.assertEqual(unittest_import_pb2.IMPORT_BAR,
- message.repeated_import_enum[0])
+ if IsProto2(message):
+ test_case.assertEqual(unittest_import_pb2.IMPORT_BAR,
+ message.repeated_import_enum[0])
test_case.assertEqual(301, message.repeated_int32[1])
test_case.assertEqual(302, message.repeated_int64[1])
@@ -486,64 +519,68 @@ def ExpectAllFieldsSet(test_case, message):
test_case.assertEqual(312, message.repeated_double[1])
test_case.assertEqual(False, message.repeated_bool[1])
test_case.assertEqual('315', message.repeated_string[1])
- test_case.assertEqual('316', message.repeated_bytes[1])
+ test_case.assertEqual(b'316', message.repeated_bytes[1])
test_case.assertEqual(317, message.repeatedgroup[1].a)
test_case.assertEqual(318, message.repeated_nested_message[1].bb)
test_case.assertEqual(319, message.repeated_foreign_message[1].c)
test_case.assertEqual(320, message.repeated_import_message[1].d)
+ test_case.assertEqual(327, message.repeated_lazy_message[1].bb)
test_case.assertEqual(unittest_pb2.TestAllTypes.BAZ,
message.repeated_nested_enum[1])
test_case.assertEqual(unittest_pb2.FOREIGN_BAZ,
message.repeated_foreign_enum[1])
- test_case.assertEqual(unittest_import_pb2.IMPORT_BAZ,
- message.repeated_import_enum[1])
+ if IsProto2(message):
+ test_case.assertEqual(unittest_import_pb2.IMPORT_BAZ,
+ message.repeated_import_enum[1])
# -----------------------------------------------------------------
- test_case.assertTrue(message.HasField('default_int32'))
- test_case.assertTrue(message.HasField('default_int64'))
- test_case.assertTrue(message.HasField('default_uint32'))
- test_case.assertTrue(message.HasField('default_uint64'))
- test_case.assertTrue(message.HasField('default_sint32'))
- test_case.assertTrue(message.HasField('default_sint64'))
- test_case.assertTrue(message.HasField('default_fixed32'))
- test_case.assertTrue(message.HasField('default_fixed64'))
- test_case.assertTrue(message.HasField('default_sfixed32'))
- test_case.assertTrue(message.HasField('default_sfixed64'))
- test_case.assertTrue(message.HasField('default_float'))
- test_case.assertTrue(message.HasField('default_double'))
- test_case.assertTrue(message.HasField('default_bool'))
- test_case.assertTrue(message.HasField('default_string'))
- test_case.assertTrue(message.HasField('default_bytes'))
-
- test_case.assertTrue(message.HasField('default_nested_enum'))
- test_case.assertTrue(message.HasField('default_foreign_enum'))
- test_case.assertTrue(message.HasField('default_import_enum'))
-
- test_case.assertEqual(401, message.default_int32)
- test_case.assertEqual(402, message.default_int64)
- test_case.assertEqual(403, message.default_uint32)
- test_case.assertEqual(404, message.default_uint64)
- test_case.assertEqual(405, message.default_sint32)
- test_case.assertEqual(406, message.default_sint64)
- test_case.assertEqual(407, message.default_fixed32)
- test_case.assertEqual(408, message.default_fixed64)
- test_case.assertEqual(409, message.default_sfixed32)
- test_case.assertEqual(410, message.default_sfixed64)
- test_case.assertEqual(411, message.default_float)
- test_case.assertEqual(412, message.default_double)
- test_case.assertEqual(False, message.default_bool)
- test_case.assertEqual('415', message.default_string)
- test_case.assertEqual('416', message.default_bytes)
-
- test_case.assertEqual(unittest_pb2.TestAllTypes.FOO,
- message.default_nested_enum)
- test_case.assertEqual(unittest_pb2.FOREIGN_FOO,
- message.default_foreign_enum)
- test_case.assertEqual(unittest_import_pb2.IMPORT_FOO,
- message.default_import_enum)
+ if IsProto2(message):
+ test_case.assertTrue(message.HasField('default_int32'))
+ test_case.assertTrue(message.HasField('default_int64'))
+ test_case.assertTrue(message.HasField('default_uint32'))
+ test_case.assertTrue(message.HasField('default_uint64'))
+ test_case.assertTrue(message.HasField('default_sint32'))
+ test_case.assertTrue(message.HasField('default_sint64'))
+ test_case.assertTrue(message.HasField('default_fixed32'))
+ test_case.assertTrue(message.HasField('default_fixed64'))
+ test_case.assertTrue(message.HasField('default_sfixed32'))
+ test_case.assertTrue(message.HasField('default_sfixed64'))
+ test_case.assertTrue(message.HasField('default_float'))
+ test_case.assertTrue(message.HasField('default_double'))
+ test_case.assertTrue(message.HasField('default_bool'))
+ test_case.assertTrue(message.HasField('default_string'))
+ test_case.assertTrue(message.HasField('default_bytes'))
+
+ test_case.assertTrue(message.HasField('default_nested_enum'))
+ test_case.assertTrue(message.HasField('default_foreign_enum'))
+ test_case.assertTrue(message.HasField('default_import_enum'))
+
+ test_case.assertEqual(401, message.default_int32)
+ test_case.assertEqual(402, message.default_int64)
+ test_case.assertEqual(403, message.default_uint32)
+ test_case.assertEqual(404, message.default_uint64)
+ test_case.assertEqual(405, message.default_sint32)
+ test_case.assertEqual(406, message.default_sint64)
+ test_case.assertEqual(407, message.default_fixed32)
+ test_case.assertEqual(408, message.default_fixed64)
+ test_case.assertEqual(409, message.default_sfixed32)
+ test_case.assertEqual(410, message.default_sfixed64)
+ test_case.assertEqual(411, message.default_float)
+ test_case.assertEqual(412, message.default_double)
+ test_case.assertEqual(False, message.default_bool)
+ test_case.assertEqual('415', message.default_string)
+ test_case.assertEqual(b'416', message.default_bytes)
+
+ test_case.assertEqual(unittest_pb2.TestAllTypes.FOO,
+ message.default_nested_enum)
+ test_case.assertEqual(unittest_pb2.FOREIGN_FOO,
+ message.default_foreign_enum)
+ test_case.assertEqual(unittest_import_pb2.IMPORT_FOO,
+ message.default_import_enum)
+
def GoldenFile(filename):
"""Finds the given golden file and returns a file object representing it."""
@@ -558,16 +595,22 @@ def GoldenFile(filename):
path = os.path.join(path, '..')
raise RuntimeError(
- 'Could not find golden files. This test must be run from within the '
- 'protobuf source package so that it can read test data files from the '
- 'C++ source tree.')
+ 'Could not find golden files. This test must be run from within the '
+ 'protobuf source package so that it can read test data files from the '
+ 'C++ source tree.')
+
+
+def GoldenFileData(filename):
+ """Finds the given golden file and returns its contents."""
+ with GoldenFile(filename) as f:
+ return f.read()
def SetAllPackedFields(message):
"""Sets every field in the message to a unique value.
Args:
- message: A unittest_pb2.TestPackedTypes instance.
+ message: A TestPackedTypes instance.
"""
message.packed_int32.extend([601, 701])
message.packed_int64.extend([602, 702])
diff --git a/python/google/protobuf/internal/text_encoding_test.py b/python/google/protobuf/internal/text_encoding_test.py
new file mode 100755
index 00000000..db0222bd
--- /dev/null
+++ b/python/google/protobuf/internal/text_encoding_test.py
@@ -0,0 +1,68 @@
+#! /usr/bin/python
+#
+# 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.
+
+"""Tests for google.protobuf.text_encoding."""
+
+from google.apputils import basetest
+from google.protobuf import text_encoding
+
+TEST_VALUES = [
+ ("foo\\rbar\\nbaz\\t",
+ "foo\\rbar\\nbaz\\t",
+ b"foo\rbar\nbaz\t"),
+ ("\\'full of \\\"sound\\\" and \\\"fury\\\"\\'",
+ "\\'full of \\\"sound\\\" and \\\"fury\\\"\\'",
+ b"'full of \"sound\" and \"fury\"'"),
+ ("signi\\\\fying\\\\ nothing\\\\",
+ "signi\\\\fying\\\\ nothing\\\\",
+ b"signi\\fying\\ nothing\\"),
+ ("\\010\\t\\n\\013\\014\\r",
+ "\x08\\t\\n\x0b\x0c\\r",
+ b"\010\011\012\013\014\015")]
+
+
+class TextEncodingTestCase(basetest.TestCase):
+ def testCEscape(self):
+ for escaped, escaped_utf8, unescaped in TEST_VALUES:
+ self.assertEquals(escaped,
+ text_encoding.CEscape(unescaped, as_utf8=False))
+ self.assertEquals(escaped_utf8,
+ text_encoding.CEscape(unescaped, as_utf8=True))
+
+ def testCUnescape(self):
+ for escaped, escaped_utf8, unescaped in TEST_VALUES:
+ self.assertEquals(unescaped, text_encoding.CUnescape(escaped))
+ self.assertEquals(unescaped, text_encoding.CUnescape(escaped_utf8))
+
+
+if __name__ == "__main__":
+ basetest.main()
diff --git a/python/google/protobuf/internal/text_format_test.py b/python/google/protobuf/internal/text_format_test.py
index 73d97d18..7d5813fb 100755
--- a/python/google/protobuf/internal/text_format_test.py
+++ b/python/google/protobuf/internal/text_format_test.py
@@ -2,7 +2,7 @@
#
# Protocol Buffers - Google's data interchange format
# Copyright 2008 Google Inc. All rights reserved.
-# http://code.google.com/p/protobuf/
+# 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
@@ -34,68 +34,51 @@
__author__ = 'kenton@google.com (Kenton Varda)'
-import difflib
import re
-import unittest
-from google.protobuf import text_format
-from google.protobuf.internal import test_util
-from google.protobuf import unittest_pb2
+from google.apputils import basetest
+from google.protobuf.internal import _parameterized
+
from google.protobuf import unittest_mset_pb2
+from google.protobuf import unittest_pb2
+from google.protobuf import unittest_proto3_arena_pb2
+from google.protobuf.internal import api_implementation
+from google.protobuf.internal import test_util
+from google.protobuf import text_format
+# Base class with some common functionality.
+class TextFormatBase(basetest.TestCase):
-class TextFormatTest(unittest.TestCase):
def ReadGolden(self, golden_filename):
- f = test_util.GoldenFile(golden_filename)
- golden_lines = f.readlines()
- f.close()
- return golden_lines
+ with test_util.GoldenFile(golden_filename) as f:
+ return (f.readlines() if str is bytes else # PY3
+ [golden_line.decode('utf-8') for golden_line in f])
def CompareToGoldenFile(self, text, golden_filename):
golden_lines = self.ReadGolden(golden_filename)
- self.CompareToGoldenLines(text, golden_lines)
+ self.assertMultiLineEqual(text, ''.join(golden_lines))
def CompareToGoldenText(self, text, golden_text):
- self.CompareToGoldenLines(text, golden_text.splitlines(1))
+ self.assertMultiLineEqual(text, golden_text)
- def CompareToGoldenLines(self, text, golden_lines):
- actual_lines = text.splitlines(1)
- self.assertEqual(golden_lines, actual_lines,
- "Text doesn't match golden. Diff:\n" +
- ''.join(difflib.ndiff(golden_lines, actual_lines)))
+ def RemoveRedundantZeros(self, text):
+ # Some platforms print 1e+5 as 1e+005. This is fine, but we need to remove
+ # these zeros in order to match the golden file.
+ text = text.replace('e+0','e+').replace('e+0','e+') \
+ .replace('e-0','e-').replace('e-0','e-')
+ # Floating point fields are printed with .0 suffix even if they are
+ # actualy integer numbers.
+ text = re.compile('\.0$', re.MULTILINE).sub('', text)
+ return text
- def testPrintAllFields(self):
- message = unittest_pb2.TestAllTypes()
- test_util.SetAllFields(message)
- self.CompareToGoldenFile(
- self.RemoveRedundantZeros(text_format.MessageToString(message)),
- 'text_format_unittest_data.txt')
- def testPrintAllExtensions(self):
- message = unittest_pb2.TestAllExtensions()
- test_util.SetAllExtensions(message)
- self.CompareToGoldenFile(
- self.RemoveRedundantZeros(text_format.MessageToString(message)),
- 'text_format_unittest_extensions_data.txt')
+@_parameterized.Parameters(
+ (unittest_pb2),
+ (unittest_proto3_arena_pb2))
+class TextFormatTest(TextFormatBase):
- def testPrintMessageSet(self):
- message = unittest_mset_pb2.TestMessageSetContainer()
- ext1 = unittest_mset_pb2.TestMessageSetExtension1.message_set_extension
- ext2 = unittest_mset_pb2.TestMessageSetExtension2.message_set_extension
- message.message_set.Extensions[ext1].i = 23
- message.message_set.Extensions[ext2].str = 'foo'
- self.CompareToGoldenText(text_format.MessageToString(message),
- 'message_set {\n'
- ' [protobuf_unittest.TestMessageSetExtension1] {\n'
- ' i: 23\n'
- ' }\n'
- ' [protobuf_unittest.TestMessageSetExtension2] {\n'
- ' str: \"foo\"\n'
- ' }\n'
- '}\n')
-
- def testPrintExotic(self):
- message = unittest_pb2.TestAllTypes()
+ def testPrintExotic(self, message_module):
+ message = message_module.TestAllTypes()
message.repeated_int64.append(-9223372036854775808)
message.repeated_uint64.append(18446744073709551615)
message.repeated_double.append(123.456)
@@ -104,62 +87,54 @@ class TextFormatTest(unittest.TestCase):
message.repeated_string.append('\000\001\a\b\f\n\r\t\v\\\'"')
message.repeated_string.append(u'\u00fc\ua71f')
self.CompareToGoldenText(
- self.RemoveRedundantZeros(text_format.MessageToString(message)),
- 'repeated_int64: -9223372036854775808\n'
- 'repeated_uint64: 18446744073709551615\n'
- 'repeated_double: 123.456\n'
- 'repeated_double: 1.23e+22\n'
- 'repeated_double: 1.23e-18\n'
- 'repeated_string: '
- '"\\000\\001\\007\\010\\014\\n\\r\\t\\013\\\\\\\'\\""\n'
- 'repeated_string: "\\303\\274\\352\\234\\237"\n')
-
- def testPrintNestedMessageAsOneLine(self):
- message = unittest_pb2.TestAllTypes()
+ self.RemoveRedundantZeros(text_format.MessageToString(message)),
+ 'repeated_int64: -9223372036854775808\n'
+ 'repeated_uint64: 18446744073709551615\n'
+ 'repeated_double: 123.456\n'
+ 'repeated_double: 1.23e+22\n'
+ 'repeated_double: 1.23e-18\n'
+ 'repeated_string:'
+ ' "\\000\\001\\007\\010\\014\\n\\r\\t\\013\\\\\\\'\\""\n'
+ 'repeated_string: "\\303\\274\\352\\234\\237"\n')
+
+ def testPrintExoticUnicodeSubclass(self, message_module):
+ class UnicodeSub(unicode):
+ pass
+ message = message_module.TestAllTypes()
+ message.repeated_string.append(UnicodeSub(u'\u00fc\ua71f'))
+ self.CompareToGoldenText(
+ text_format.MessageToString(message),
+ 'repeated_string: "\\303\\274\\352\\234\\237"\n')
+
+ def testPrintNestedMessageAsOneLine(self, message_module):
+ message = message_module.TestAllTypes()
msg = message.repeated_nested_message.add()
- msg.bb = 42;
+ msg.bb = 42
self.CompareToGoldenText(
text_format.MessageToString(message, as_one_line=True),
'repeated_nested_message { bb: 42 }')
- def testPrintRepeatedFieldsAsOneLine(self):
- message = unittest_pb2.TestAllTypes()
+ def testPrintRepeatedFieldsAsOneLine(self, message_module):
+ message = message_module.TestAllTypes()
message.repeated_int32.append(1)
message.repeated_int32.append(1)
message.repeated_int32.append(3)
- message.repeated_string.append("Google")
- message.repeated_string.append("Zurich")
+ message.repeated_string.append('Google')
+ message.repeated_string.append('Zurich')
self.CompareToGoldenText(
text_format.MessageToString(message, as_one_line=True),
'repeated_int32: 1 repeated_int32: 1 repeated_int32: 3 '
'repeated_string: "Google" repeated_string: "Zurich"')
- def testPrintNestedNewLineInStringAsOneLine(self):
- message = unittest_pb2.TestAllTypes()
- message.optional_string = "a\nnew\nline"
+ def testPrintNestedNewLineInStringAsOneLine(self, message_module):
+ message = message_module.TestAllTypes()
+ message.optional_string = 'a\nnew\nline'
self.CompareToGoldenText(
text_format.MessageToString(message, as_one_line=True),
'optional_string: "a\\nnew\\nline"')
- def testPrintMessageSetAsOneLine(self):
- message = unittest_mset_pb2.TestMessageSetContainer()
- ext1 = unittest_mset_pb2.TestMessageSetExtension1.message_set_extension
- ext2 = unittest_mset_pb2.TestMessageSetExtension2.message_set_extension
- message.message_set.Extensions[ext1].i = 23
- message.message_set.Extensions[ext2].str = 'foo'
- self.CompareToGoldenText(
- text_format.MessageToString(message, as_one_line=True),
- 'message_set {'
- ' [protobuf_unittest.TestMessageSetExtension1] {'
- ' i: 23'
- ' }'
- ' [protobuf_unittest.TestMessageSetExtension2] {'
- ' str: \"foo\"'
- ' }'
- ' }')
-
- def testPrintExoticAsOneLine(self):
- message = unittest_pb2.TestAllTypes()
+ def testPrintExoticAsOneLine(self, message_module):
+ message = message_module.TestAllTypes()
message.repeated_int64.append(-9223372036854775808)
message.repeated_uint64.append(18446744073709551615)
message.repeated_double.append(123.456)
@@ -168,19 +143,19 @@ class TextFormatTest(unittest.TestCase):
message.repeated_string.append('\000\001\a\b\f\n\r\t\v\\\'"')
message.repeated_string.append(u'\u00fc\ua71f')
self.CompareToGoldenText(
- self.RemoveRedundantZeros(
- text_format.MessageToString(message, as_one_line=True)),
- 'repeated_int64: -9223372036854775808'
- ' repeated_uint64: 18446744073709551615'
- ' repeated_double: 123.456'
- ' repeated_double: 1.23e+22'
- ' repeated_double: 1.23e-18'
- ' repeated_string: '
- '"\\000\\001\\007\\010\\014\\n\\r\\t\\013\\\\\\\'\\""'
- ' repeated_string: "\\303\\274\\352\\234\\237"')
-
- def testRoundTripExoticAsOneLine(self):
- message = unittest_pb2.TestAllTypes()
+ self.RemoveRedundantZeros(
+ text_format.MessageToString(message, as_one_line=True)),
+ 'repeated_int64: -9223372036854775808'
+ ' repeated_uint64: 18446744073709551615'
+ ' repeated_double: 123.456'
+ ' repeated_double: 1.23e+22'
+ ' repeated_double: 1.23e-18'
+ ' repeated_string: '
+ '"\\000\\001\\007\\010\\014\\n\\r\\t\\013\\\\\\\'\\""'
+ ' repeated_string: "\\303\\274\\352\\234\\237"')
+
+ def testRoundTripExoticAsOneLine(self, message_module):
+ message = message_module.TestAllTypes()
message.repeated_int64.append(-9223372036854775808)
message.repeated_uint64.append(18446744073709551615)
message.repeated_double.append(123.456)
@@ -192,84 +167,353 @@ class TextFormatTest(unittest.TestCase):
# Test as_utf8 = False.
wire_text = text_format.MessageToString(
message, as_one_line=True, as_utf8=False)
- parsed_message = unittest_pb2.TestAllTypes()
- text_format.Merge(wire_text, parsed_message)
+ parsed_message = message_module.TestAllTypes()
+ r = text_format.Parse(wire_text, parsed_message)
+ self.assertIs(r, parsed_message)
self.assertEquals(message, parsed_message)
# Test as_utf8 = True.
wire_text = text_format.MessageToString(
message, as_one_line=True, as_utf8=True)
- parsed_message = unittest_pb2.TestAllTypes()
- text_format.Merge(wire_text, parsed_message)
- self.assertEquals(message, parsed_message)
-
- def testPrintRawUtf8String(self):
- message = unittest_pb2.TestAllTypes()
+ parsed_message = message_module.TestAllTypes()
+ r = text_format.Parse(wire_text, parsed_message)
+ self.assertIs(r, parsed_message)
+ self.assertEquals(message, parsed_message,
+ '\n%s != %s' % (message, parsed_message))
+
+ def testPrintRawUtf8String(self, message_module):
+ message = message_module.TestAllTypes()
message.repeated_string.append(u'\u00fc\ua71f')
- text = text_format.MessageToString(message, as_utf8 = True)
+ text = text_format.MessageToString(message, as_utf8=True)
self.CompareToGoldenText(text, 'repeated_string: "\303\274\352\234\237"\n')
- parsed_message = unittest_pb2.TestAllTypes()
- text_format.Merge(text, parsed_message)
- self.assertEquals(message, parsed_message)
+ parsed_message = message_module.TestAllTypes()
+ text_format.Parse(text, parsed_message)
+ self.assertEquals(message, parsed_message,
+ '\n%s != %s' % (message, parsed_message))
+
+ def testPrintFloatFormat(self, message_module):
+ # Check that float_format argument is passed to sub-message formatting.
+ message = message_module.NestedTestAllTypes()
+ # We use 1.25 as it is a round number in binary. The proto 32-bit float
+ # will not gain additional imprecise digits as a 64-bit Python float and
+ # show up in its str. 32-bit 1.2 is noisy when extended to 64-bit:
+ # >>> struct.unpack('f', struct.pack('f', 1.2))[0]
+ # 1.2000000476837158
+ # >>> struct.unpack('f', struct.pack('f', 1.25))[0]
+ # 1.25
+ message.payload.optional_float = 1.25
+ # Check rounding at 15 significant digits
+ message.payload.optional_double = -.000003456789012345678
+ # Check no decimal point.
+ message.payload.repeated_float.append(-5642)
+ # Check no trailing zeros.
+ message.payload.repeated_double.append(.000078900)
+ formatted_fields = ['optional_float: 1.25',
+ 'optional_double: -3.45678901234568e-6',
+ 'repeated_float: -5642',
+ 'repeated_double: 7.89e-5']
+ text_message = text_format.MessageToString(message, float_format='.15g')
+ self.CompareToGoldenText(
+ self.RemoveRedundantZeros(text_message),
+ 'payload {{\n {}\n {}\n {}\n {}\n}}\n'.format(*formatted_fields))
+ # as_one_line=True is a separate code branch where float_format is passed.
+ text_message = text_format.MessageToString(message, as_one_line=True,
+ float_format='.15g')
+ self.CompareToGoldenText(
+ self.RemoveRedundantZeros(text_message),
+ 'payload {{ {} {} {} {} }}'.format(*formatted_fields))
- def testMessageToString(self):
- message = unittest_pb2.ForeignMessage()
+ def testMessageToString(self, message_module):
+ message = message_module.ForeignMessage()
message.c = 123
self.assertEqual('c: 123\n', str(message))
- def RemoveRedundantZeros(self, text):
- # Some platforms print 1e+5 as 1e+005. This is fine, but we need to remove
- # these zeros in order to match the golden file.
- text = text.replace('e+0','e+').replace('e+0','e+') \
- .replace('e-0','e-').replace('e-0','e-')
- # Floating point fields are printed with .0 suffix even if they are
- # actualy integer numbers.
- text = re.compile('\.0$', re.MULTILINE).sub('', text)
- return text
+ def testParseAllFields(self, message_module):
+ message = message_module.TestAllTypes()
+ test_util.SetAllFields(message)
+ ascii_text = text_format.MessageToString(message)
+
+ parsed_message = message_module.TestAllTypes()
+ text_format.Parse(ascii_text, parsed_message)
+ self.assertEqual(message, parsed_message)
+ if message_module is unittest_pb2:
+ test_util.ExpectAllFieldsSet(self, message)
+
+ def testParseExotic(self, message_module):
+ message = message_module.TestAllTypes()
+ text = ('repeated_int64: -9223372036854775808\n'
+ 'repeated_uint64: 18446744073709551615\n'
+ 'repeated_double: 123.456\n'
+ 'repeated_double: 1.23e+22\n'
+ 'repeated_double: 1.23e-18\n'
+ 'repeated_string: \n'
+ '"\\000\\001\\007\\010\\014\\n\\r\\t\\013\\\\\\\'\\""\n'
+ 'repeated_string: "foo" \'corge\' "grault"\n'
+ 'repeated_string: "\\303\\274\\352\\234\\237"\n'
+ 'repeated_string: "\\xc3\\xbc"\n'
+ 'repeated_string: "\xc3\xbc"\n')
+ text_format.Parse(text, message)
+
+ self.assertEqual(-9223372036854775808, message.repeated_int64[0])
+ self.assertEqual(18446744073709551615, message.repeated_uint64[0])
+ self.assertEqual(123.456, message.repeated_double[0])
+ self.assertEqual(1.23e22, message.repeated_double[1])
+ self.assertEqual(1.23e-18, message.repeated_double[2])
+ self.assertEqual(
+ '\000\001\a\b\f\n\r\t\v\\\'"', message.repeated_string[0])
+ self.assertEqual('foocorgegrault', message.repeated_string[1])
+ self.assertEqual(u'\u00fc\ua71f', message.repeated_string[2])
+ self.assertEqual(u'\u00fc', message.repeated_string[3])
- def testMergeGolden(self):
+ def testParseTrailingCommas(self, message_module):
+ message = message_module.TestAllTypes()
+ text = ('repeated_int64: 100;\n'
+ 'repeated_int64: 200;\n'
+ 'repeated_int64: 300,\n'
+ 'repeated_string: "one",\n'
+ 'repeated_string: "two";\n')
+ text_format.Parse(text, message)
+
+ self.assertEqual(100, message.repeated_int64[0])
+ self.assertEqual(200, message.repeated_int64[1])
+ self.assertEqual(300, message.repeated_int64[2])
+ self.assertEqual(u'one', message.repeated_string[0])
+ self.assertEqual(u'two', message.repeated_string[1])
+
+ def testParseEmptyText(self, message_module):
+ message = message_module.TestAllTypes()
+ text = ''
+ text_format.Parse(text, message)
+ self.assertEquals(message_module.TestAllTypes(), message)
+
+ def testParseInvalidUtf8(self, message_module):
+ message = message_module.TestAllTypes()
+ text = 'repeated_string: "\\xc3\\xc3"'
+ self.assertRaises(text_format.ParseError, text_format.Parse, text, message)
+
+ def testParseSingleWord(self, message_module):
+ message = message_module.TestAllTypes()
+ text = 'foo'
+ self.assertRaisesRegexp(
+ text_format.ParseError,
+ (r'1:1 : Message type "\w+.TestAllTypes" has no field named '
+ r'"foo".'),
+ text_format.Parse, text, message)
+
+ def testParseUnknownField(self, message_module):
+ message = message_module.TestAllTypes()
+ text = 'unknown_field: 8\n'
+ self.assertRaisesRegexp(
+ text_format.ParseError,
+ (r'1:1 : Message type "\w+.TestAllTypes" has no field named '
+ r'"unknown_field".'),
+ text_format.Parse, text, message)
+
+ def testParseGroupNotClosed(self, message_module):
+ message = message_module.TestAllTypes()
+ text = 'RepeatedGroup: <'
+ self.assertRaisesWithLiteralMatch(
+ text_format.ParseError, '1:16 : Expected ">".',
+ text_format.Parse, text, message)
+
+ text = 'RepeatedGroup: {'
+ self.assertRaisesWithLiteralMatch(
+ text_format.ParseError, '1:16 : Expected "}".',
+ text_format.Parse, text, message)
+
+ def testParseEmptyGroup(self, message_module):
+ message = message_module.TestAllTypes()
+ text = 'OptionalGroup: {}'
+ text_format.Parse(text, message)
+ self.assertTrue(message.HasField('optionalgroup'))
+
+ message.Clear()
+
+ message = message_module.TestAllTypes()
+ text = 'OptionalGroup: <>'
+ text_format.Parse(text, message)
+ self.assertTrue(message.HasField('optionalgroup'))
+
+ def testParseBadEnumValue(self, message_module):
+ message = message_module.TestAllTypes()
+ text = 'optional_nested_enum: BARR'
+ self.assertRaisesRegexp(
+ text_format.ParseError,
+ (r'1:23 : Enum type "\w+.TestAllTypes.NestedEnum" '
+ r'has no value named BARR.'),
+ text_format.Parse, text, message)
+
+ message = message_module.TestAllTypes()
+ text = 'optional_nested_enum: 100'
+ self.assertRaisesRegexp(
+ text_format.ParseError,
+ (r'1:23 : Enum type "\w+.TestAllTypes.NestedEnum" '
+ r'has no value with number 100.'),
+ text_format.Parse, text, message)
+
+ def testParseBadIntValue(self, message_module):
+ message = message_module.TestAllTypes()
+ text = 'optional_int32: bork'
+ self.assertRaisesWithLiteralMatch(
+ text_format.ParseError,
+ ('1:17 : Couldn\'t parse integer: bork'),
+ text_format.Parse, text, message)
+
+ def testParseStringFieldUnescape(self, message_module):
+ message = message_module.TestAllTypes()
+ text = r'''repeated_string: "\xf\x62"
+ repeated_string: "\\xf\\x62"
+ repeated_string: "\\\xf\\\x62"
+ repeated_string: "\\\\xf\\\\x62"
+ repeated_string: "\\\\\xf\\\\\x62"
+ repeated_string: "\x5cx20"'''
+ text_format.Parse(text, message)
+
+ SLASH = '\\'
+ self.assertEqual('\x0fb', message.repeated_string[0])
+ self.assertEqual(SLASH + 'xf' + SLASH + 'x62', message.repeated_string[1])
+ self.assertEqual(SLASH + '\x0f' + SLASH + 'b', message.repeated_string[2])
+ self.assertEqual(SLASH + SLASH + 'xf' + SLASH + SLASH + 'x62',
+ message.repeated_string[3])
+ self.assertEqual(SLASH + SLASH + '\x0f' + SLASH + SLASH + 'b',
+ message.repeated_string[4])
+ self.assertEqual(SLASH + 'x20', message.repeated_string[5])
+
+ def testMergeDuplicateScalars(self, message_module):
+ message = message_module.TestAllTypes()
+ text = ('optional_int32: 42 '
+ 'optional_int32: 67')
+ r = text_format.Merge(text, message)
+ self.assertIs(r, message)
+ self.assertEqual(67, message.optional_int32)
+
+ def testMergeDuplicateNestedMessageScalars(self, message_module):
+ message = message_module.TestAllTypes()
+ text = ('optional_nested_message { bb: 1 } '
+ 'optional_nested_message { bb: 2 }')
+ r = text_format.Merge(text, message)
+ self.assertTrue(r is message)
+ self.assertEqual(2, message.optional_nested_message.bb)
+
+ def testParseOneof(self, message_module):
+ m = message_module.TestAllTypes()
+ m.oneof_uint32 = 11
+ m2 = message_module.TestAllTypes()
+ text_format.Parse(text_format.MessageToString(m), m2)
+ self.assertEqual('oneof_uint32', m2.WhichOneof('oneof_field'))
+
+
+# These are tests that aren't fundamentally specific to proto2, but are at
+# the moment because of differences between the proto2 and proto3 test schemas.
+# Ideally the schemas would be made more similar so these tests could pass.
+class OnlyWorksWithProto2RightNowTests(TextFormatBase):
+
+ def testParseGolden(self):
golden_text = '\n'.join(self.ReadGolden('text_format_unittest_data.txt'))
parsed_message = unittest_pb2.TestAllTypes()
- text_format.Merge(golden_text, parsed_message)
+ r = text_format.Parse(golden_text, parsed_message)
+ self.assertIs(r, parsed_message)
message = unittest_pb2.TestAllTypes()
test_util.SetAllFields(message)
self.assertEquals(message, parsed_message)
- def testMergeGoldenExtensions(self):
- golden_text = '\n'.join(self.ReadGolden(
- 'text_format_unittest_extensions_data.txt'))
- parsed_message = unittest_pb2.TestAllExtensions()
- text_format.Merge(golden_text, parsed_message)
-
- message = unittest_pb2.TestAllExtensions()
- test_util.SetAllExtensions(message)
- self.assertEquals(message, parsed_message)
+ def testPrintAllFields(self):
+ message = unittest_pb2.TestAllTypes()
+ test_util.SetAllFields(message)
+ self.CompareToGoldenFile(
+ self.RemoveRedundantZeros(text_format.MessageToString(message)),
+ 'text_format_unittest_data_oneof_implemented.txt')
- def testMergeAllFields(self):
+ def testPrintAllFieldsPointy(self):
message = unittest_pb2.TestAllTypes()
test_util.SetAllFields(message)
- ascii_text = text_format.MessageToString(message)
+ self.CompareToGoldenFile(
+ self.RemoveRedundantZeros(
+ text_format.MessageToString(message, pointy_brackets=True)),
+ 'text_format_unittest_data_pointy_oneof.txt')
+
+ def testPrintInIndexOrder(self):
+ message = unittest_pb2.TestFieldOrderings()
+ message.my_string = '115'
+ message.my_int = 101
+ message.my_float = 111
+ message.optional_nested_message.oo = 0
+ message.optional_nested_message.bb = 1
+ self.CompareToGoldenText(
+ self.RemoveRedundantZeros(text_format.MessageToString(
+ message, use_index_order=True)),
+ 'my_string: \"115\"\nmy_int: 101\nmy_float: 111\n'
+ 'optional_nested_message {\n oo: 0\n bb: 1\n}\n')
+ self.CompareToGoldenText(
+ self.RemoveRedundantZeros(text_format.MessageToString(
+ message)),
+ 'my_int: 101\nmy_string: \"115\"\nmy_float: 111\n'
+ 'optional_nested_message {\n bb: 1\n oo: 0\n}\n')
+ def testMergeLinesGolden(self):
+ opened = self.ReadGolden('text_format_unittest_data.txt')
parsed_message = unittest_pb2.TestAllTypes()
- text_format.Merge(ascii_text, parsed_message)
+ r = text_format.MergeLines(opened, parsed_message)
+ self.assertIs(r, parsed_message)
+
+ message = unittest_pb2.TestAllTypes()
+ test_util.SetAllFields(message)
self.assertEqual(message, parsed_message)
- test_util.ExpectAllFieldsSet(self, message)
- def testMergeAllExtensions(self):
- message = unittest_pb2.TestAllExtensions()
- test_util.SetAllExtensions(message)
- ascii_text = text_format.MessageToString(message)
+ def testParseLinesGolden(self):
+ opened = self.ReadGolden('text_format_unittest_data.txt')
+ parsed_message = unittest_pb2.TestAllTypes()
+ r = text_format.ParseLines(opened, parsed_message)
+ self.assertIs(r, parsed_message)
- parsed_message = unittest_pb2.TestAllExtensions()
- text_format.Merge(ascii_text, parsed_message)
- self.assertEqual(message, parsed_message)
+ message = unittest_pb2.TestAllTypes()
+ test_util.SetAllFields(message)
+ self.assertEquals(message, parsed_message)
+
+
+# Tests of proto2-only features (MessageSet and extensions).
+class Proto2Tests(TextFormatBase):
+
+ def testPrintMessageSet(self):
+ message = unittest_mset_pb2.TestMessageSetContainer()
+ ext1 = unittest_mset_pb2.TestMessageSetExtension1.message_set_extension
+ ext2 = unittest_mset_pb2.TestMessageSetExtension2.message_set_extension
+ message.message_set.Extensions[ext1].i = 23
+ message.message_set.Extensions[ext2].str = 'foo'
+ self.CompareToGoldenText(
+ text_format.MessageToString(message),
+ 'message_set {\n'
+ ' [protobuf_unittest.TestMessageSetExtension1] {\n'
+ ' i: 23\n'
+ ' }\n'
+ ' [protobuf_unittest.TestMessageSetExtension2] {\n'
+ ' str: \"foo\"\n'
+ ' }\n'
+ '}\n')
- def testMergeMessageSet(self):
+ def testPrintMessageSetAsOneLine(self):
+ message = unittest_mset_pb2.TestMessageSetContainer()
+ ext1 = unittest_mset_pb2.TestMessageSetExtension1.message_set_extension
+ ext2 = unittest_mset_pb2.TestMessageSetExtension2.message_set_extension
+ message.message_set.Extensions[ext1].i = 23
+ message.message_set.Extensions[ext2].str = 'foo'
+ self.CompareToGoldenText(
+ text_format.MessageToString(message, as_one_line=True),
+ 'message_set {'
+ ' [protobuf_unittest.TestMessageSetExtension1] {'
+ ' i: 23'
+ ' }'
+ ' [protobuf_unittest.TestMessageSetExtension2] {'
+ ' str: \"foo\"'
+ ' }'
+ ' }')
+
+ def testParseMessageSet(self):
message = unittest_pb2.TestAllTypes()
text = ('repeated_uint64: 1\n'
'repeated_uint64: 2\n')
- text_format.Merge(text, message)
+ text_format.Parse(text, message)
self.assertEqual(1, message.repeated_uint64[0])
self.assertEqual(2, message.repeated_uint64[1])
@@ -282,144 +526,102 @@ class TextFormatTest(unittest.TestCase):
' str: \"foo\"\n'
' }\n'
'}\n')
- text_format.Merge(text, message)
+ text_format.Parse(text, message)
ext1 = unittest_mset_pb2.TestMessageSetExtension1.message_set_extension
ext2 = unittest_mset_pb2.TestMessageSetExtension2.message_set_extension
self.assertEquals(23, message.message_set.Extensions[ext1].i)
self.assertEquals('foo', message.message_set.Extensions[ext2].str)
- def testMergeExotic(self):
- message = unittest_pb2.TestAllTypes()
- text = ('repeated_int64: -9223372036854775808\n'
- 'repeated_uint64: 18446744073709551615\n'
- 'repeated_double: 123.456\n'
- 'repeated_double: 1.23e+22\n'
- 'repeated_double: 1.23e-18\n'
- 'repeated_string: \n'
- '"\\000\\001\\007\\010\\014\\n\\r\\t\\013\\\\\\\'\\""\n'
- 'repeated_string: "foo" \'corge\' "grault"\n'
- 'repeated_string: "\\303\\274\\352\\234\\237"\n'
- 'repeated_string: "\\xc3\\xbc"\n'
- 'repeated_string: "\xc3\xbc"\n')
- text_format.Merge(text, message)
+ def testPrintAllExtensions(self):
+ message = unittest_pb2.TestAllExtensions()
+ test_util.SetAllExtensions(message)
+ self.CompareToGoldenFile(
+ self.RemoveRedundantZeros(text_format.MessageToString(message)),
+ 'text_format_unittest_extensions_data.txt')
- self.assertEqual(-9223372036854775808, message.repeated_int64[0])
- self.assertEqual(18446744073709551615, message.repeated_uint64[0])
- self.assertEqual(123.456, message.repeated_double[0])
- self.assertEqual(1.23e22, message.repeated_double[1])
- self.assertEqual(1.23e-18, message.repeated_double[2])
- self.assertEqual(
- '\000\001\a\b\f\n\r\t\v\\\'"', message.repeated_string[0])
- self.assertEqual('foocorgegrault', message.repeated_string[1])
- self.assertEqual(u'\u00fc\ua71f', message.repeated_string[2])
- self.assertEqual(u'\u00fc', message.repeated_string[3])
+ def testPrintAllExtensionsPointy(self):
+ message = unittest_pb2.TestAllExtensions()
+ test_util.SetAllExtensions(message)
+ self.CompareToGoldenFile(
+ self.RemoveRedundantZeros(text_format.MessageToString(
+ message, pointy_brackets=True)),
+ 'text_format_unittest_extensions_data_pointy.txt')
- def testMergeEmptyText(self):
- message = unittest_pb2.TestAllTypes()
- text = ''
- text_format.Merge(text, message)
- self.assertEquals(unittest_pb2.TestAllTypes(), message)
+ def testParseGoldenExtensions(self):
+ golden_text = '\n'.join(self.ReadGolden(
+ 'text_format_unittest_extensions_data.txt'))
+ parsed_message = unittest_pb2.TestAllExtensions()
+ text_format.Parse(golden_text, parsed_message)
- def testMergeInvalidUtf8(self):
- message = unittest_pb2.TestAllTypes()
- text = 'repeated_string: "\\xc3\\xc3"'
- self.assertRaises(text_format.ParseError, text_format.Merge, text, message)
+ message = unittest_pb2.TestAllExtensions()
+ test_util.SetAllExtensions(message)
+ self.assertEquals(message, parsed_message)
- def testMergeSingleWord(self):
- message = unittest_pb2.TestAllTypes()
- text = 'foo'
- self.assertRaisesWithMessage(
- text_format.ParseError,
- ('1:1 : Message type "protobuf_unittest.TestAllTypes" has no field named '
- '"foo".'),
- text_format.Merge, text, message)
+ def testParseAllExtensions(self):
+ message = unittest_pb2.TestAllExtensions()
+ test_util.SetAllExtensions(message)
+ ascii_text = text_format.MessageToString(message)
- def testMergeUnknownField(self):
- message = unittest_pb2.TestAllTypes()
- text = 'unknown_field: 8\n'
- self.assertRaisesWithMessage(
- text_format.ParseError,
- ('1:1 : Message type "protobuf_unittest.TestAllTypes" has no field named '
- '"unknown_field".'),
- text_format.Merge, text, message)
+ parsed_message = unittest_pb2.TestAllExtensions()
+ text_format.Parse(ascii_text, parsed_message)
+ self.assertEqual(message, parsed_message)
- def testMergeBadExtension(self):
+ def testParseBadExtension(self):
message = unittest_pb2.TestAllExtensions()
text = '[unknown_extension]: 8\n'
- self.assertRaisesWithMessage(
+ self.assertRaisesWithLiteralMatch(
text_format.ParseError,
'1:2 : Extension "unknown_extension" not registered.',
- text_format.Merge, text, message)
+ text_format.Parse, text, message)
message = unittest_pb2.TestAllTypes()
- self.assertRaisesWithMessage(
+ self.assertRaisesWithLiteralMatch(
text_format.ParseError,
('1:2 : Message type "protobuf_unittest.TestAllTypes" does not have '
'extensions.'),
- text_format.Merge, text, message)
-
- def testMergeGroupNotClosed(self):
- message = unittest_pb2.TestAllTypes()
- text = 'RepeatedGroup: <'
- self.assertRaisesWithMessage(
- text_format.ParseError, '1:16 : Expected ">".',
- text_format.Merge, text, message)
+ text_format.Parse, text, message)
- text = 'RepeatedGroup: {'
- self.assertRaisesWithMessage(
- text_format.ParseError, '1:16 : Expected "}".',
- text_format.Merge, text, message)
-
- def testMergeEmptyGroup(self):
- message = unittest_pb2.TestAllTypes()
- text = 'OptionalGroup: {}'
+ def testMergeDuplicateExtensionScalars(self):
+ message = unittest_pb2.TestAllExtensions()
+ text = ('[protobuf_unittest.optional_int32_extension]: 42 '
+ '[protobuf_unittest.optional_int32_extension]: 67')
text_format.Merge(text, message)
- self.assertTrue(message.HasField('optionalgroup'))
-
- message.Clear()
+ self.assertEqual(
+ 67,
+ message.Extensions[unittest_pb2.optional_int32_extension])
- message = unittest_pb2.TestAllTypes()
- text = 'OptionalGroup: <>'
- text_format.Merge(text, message)
- self.assertTrue(message.HasField('optionalgroup'))
+ def testParseDuplicateExtensionScalars(self):
+ message = unittest_pb2.TestAllExtensions()
+ text = ('[protobuf_unittest.optional_int32_extension]: 42 '
+ '[protobuf_unittest.optional_int32_extension]: 67')
+ self.assertRaisesWithLiteralMatch(
+ text_format.ParseError,
+ ('1:96 : Message type "protobuf_unittest.TestAllExtensions" '
+ 'should not have multiple '
+ '"protobuf_unittest.optional_int32_extension" extensions.'),
+ text_format.Parse, text, message)
- def testMergeBadEnumValue(self):
+ def testParseDuplicateNestedMessageScalars(self):
message = unittest_pb2.TestAllTypes()
- text = 'optional_nested_enum: BARR'
- self.assertRaisesWithMessage(
+ text = ('optional_nested_message { bb: 1 } '
+ 'optional_nested_message { bb: 2 }')
+ self.assertRaisesWithLiteralMatch(
text_format.ParseError,
- ('1:23 : Enum type "protobuf_unittest.TestAllTypes.NestedEnum" '
- 'has no value named BARR.'),
- text_format.Merge, text, message)
+ ('1:65 : Message type "protobuf_unittest.TestAllTypes.NestedMessage" '
+ 'should not have multiple "bb" fields.'),
+ text_format.Parse, text, message)
+ def testParseDuplicateScalars(self):
message = unittest_pb2.TestAllTypes()
- text = 'optional_nested_enum: 100'
- self.assertRaisesWithMessage(
+ text = ('optional_int32: 42 '
+ 'optional_int32: 67')
+ self.assertRaisesWithLiteralMatch(
text_format.ParseError,
- ('1:23 : Enum type "protobuf_unittest.TestAllTypes.NestedEnum" '
- 'has no value with number 100.'),
- text_format.Merge, text, message)
-
- def assertRaisesWithMessage(self, e_class, e, func, *args, **kwargs):
- """Same as assertRaises, but also compares the exception message."""
- if hasattr(e_class, '__name__'):
- exc_name = e_class.__name__
- else:
- exc_name = str(e_class)
-
- try:
- func(*args, **kwargs)
- except e_class, expr:
- if str(expr) != e:
- msg = '%s raised, but with wrong message: "%s" instead of "%s"'
- raise self.failureException(msg % (exc_name,
- str(expr).encode('string_escape'),
- e.encode('string_escape')))
- return
- else:
- raise self.failureException('%s not raised' % exc_name)
-
-
-class TokenizerTest(unittest.TestCase):
+ ('1:36 : Message type "protobuf_unittest.TestAllTypes" should not '
+ 'have multiple "optional_int32" fields.'),
+ text_format.Parse, text, message)
+
+
+class TokenizerTest(basetest.TestCase):
def testSimpleTokenCases(self):
text = ('identifier1:"string1"\n \n\n'
@@ -427,9 +629,9 @@ class TokenizerTest(unittest.TestCase):
'identifiER_4 : 1.1e+2 ID5:-0.23 ID6:\'aaaa\\\'bbbb\'\n'
'ID7 : "aa\\"bb"\n\n\n\n ID8: {A:inf B:-inf C:true D:false}\n'
'ID9: 22 ID10: -111111111111111111 ID11: -22\n'
- 'ID12: 2222222222222222222 '
- 'false_bool: 0 true_BOOL:t \n true_bool1: 1 false_BOOL1:f ' )
- tokenizer = text_format._Tokenizer(text)
+ 'ID12: 2222222222222222222 ID13: 1.23456f ID14: 1.2e+2f '
+ 'false_bool: 0 true_BOOL:t \n true_bool1: 1 false_BOOL1:f ')
+ tokenizer = text_format._Tokenizer(text.splitlines())
methods = [(tokenizer.ConsumeIdentifier, 'identifier1'),
':',
(tokenizer.ConsumeString, 'string1'),
@@ -456,10 +658,10 @@ class TokenizerTest(unittest.TestCase):
'{',
(tokenizer.ConsumeIdentifier, 'A'),
':',
- (tokenizer.ConsumeFloat, text_format._INFINITY),
+ (tokenizer.ConsumeFloat, float('inf')),
(tokenizer.ConsumeIdentifier, 'B'),
':',
- (tokenizer.ConsumeFloat, -text_format._INFINITY),
+ (tokenizer.ConsumeFloat, -float('inf')),
(tokenizer.ConsumeIdentifier, 'C'),
':',
(tokenizer.ConsumeBool, True),
@@ -479,6 +681,12 @@ class TokenizerTest(unittest.TestCase):
(tokenizer.ConsumeIdentifier, 'ID12'),
':',
(tokenizer.ConsumeUint64, 2222222222222222222),
+ (tokenizer.ConsumeIdentifier, 'ID13'),
+ ':',
+ (tokenizer.ConsumeFloat, 1.23456),
+ (tokenizer.ConsumeIdentifier, 'ID14'),
+ ':',
+ (tokenizer.ConsumeFloat, 1.2e+2),
(tokenizer.ConsumeIdentifier, 'false_bool'),
':',
(tokenizer.ConsumeBool, False),
@@ -509,7 +717,7 @@ class TokenizerTest(unittest.TestCase):
int64_max = (1 << 63) - 1
uint32_max = (1 << 32) - 1
text = '-1 %d %d' % (uint32_max + 1, int64_max + 1)
- tokenizer = text_format._Tokenizer(text)
+ tokenizer = text_format._Tokenizer(text.splitlines())
self.assertRaises(text_format.ParseError, tokenizer.ConsumeUint32)
self.assertRaises(text_format.ParseError, tokenizer.ConsumeUint64)
self.assertEqual(-1, tokenizer.ConsumeInt32())
@@ -523,7 +731,7 @@ class TokenizerTest(unittest.TestCase):
self.assertTrue(tokenizer.AtEnd())
text = '-0 -0 0 0'
- tokenizer = text_format._Tokenizer(text)
+ tokenizer = text_format._Tokenizer(text.splitlines())
self.assertEqual(0, tokenizer.ConsumeUint32())
self.assertEqual(0, tokenizer.ConsumeUint64())
self.assertEqual(0, tokenizer.ConsumeUint32())
@@ -532,40 +740,30 @@ class TokenizerTest(unittest.TestCase):
def testConsumeByteString(self):
text = '"string1\''
- tokenizer = text_format._Tokenizer(text)
+ tokenizer = text_format._Tokenizer(text.splitlines())
self.assertRaises(text_format.ParseError, tokenizer.ConsumeByteString)
text = 'string1"'
- tokenizer = text_format._Tokenizer(text)
+ tokenizer = text_format._Tokenizer(text.splitlines())
self.assertRaises(text_format.ParseError, tokenizer.ConsumeByteString)
text = '\n"\\xt"'
- tokenizer = text_format._Tokenizer(text)
+ tokenizer = text_format._Tokenizer(text.splitlines())
self.assertRaises(text_format.ParseError, tokenizer.ConsumeByteString)
text = '\n"\\"'
- tokenizer = text_format._Tokenizer(text)
+ tokenizer = text_format._Tokenizer(text.splitlines())
self.assertRaises(text_format.ParseError, tokenizer.ConsumeByteString)
text = '\n"\\x"'
- tokenizer = text_format._Tokenizer(text)
+ tokenizer = text_format._Tokenizer(text.splitlines())
self.assertRaises(text_format.ParseError, tokenizer.ConsumeByteString)
def testConsumeBool(self):
text = 'not-a-bool'
- tokenizer = text_format._Tokenizer(text)
+ tokenizer = text_format._Tokenizer(text.splitlines())
self.assertRaises(text_format.ParseError, tokenizer.ConsumeBool)
- def testInfNan(self):
- # Make sure our infinity and NaN definitions are sound.
- self.assertEquals(float, type(text_format._INFINITY))
- self.assertEquals(float, type(text_format._NAN))
- self.assertTrue(text_format._NAN != text_format._NAN)
-
- inf_times_zero = text_format._INFINITY * 0
- self.assertTrue(inf_times_zero != inf_times_zero)
- self.assertTrue(text_format._INFINITY > 0)
-
if __name__ == '__main__':
- unittest.main()
+ basetest.main()
diff --git a/python/google/protobuf/internal/type_checkers.py b/python/google/protobuf/internal/type_checkers.py
index 2b3cd4de..76c056c4 100755
--- a/python/google/protobuf/internal/type_checkers.py
+++ b/python/google/protobuf/internal/type_checkers.py
@@ -1,6 +1,6 @@
# Protocol Buffers - Google's data interchange format
# Copyright 2008 Google Inc. All rights reserved.
-# http://code.google.com/p/protobuf/
+# 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
@@ -28,6 +28,10 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#PY25 compatible for GAE.
+#
+# Copyright 2008 Google Inc. All Rights Reserved.
+
"""Provides type checking routines.
This module defines type checking utilities in the forms of dictionaries:
@@ -45,6 +49,9 @@ TYPE_TO_DESERIALIZE_METHOD: A dictionary with field types and deserialization
__author__ = 'robinson@google.com (Will Robinson)'
+import sys ##PY25
+if sys.version < '2.6': bytes = str ##PY25
+from google.protobuf.internal import api_implementation
from google.protobuf.internal import decoder
from google.protobuf.internal import encoder
from google.protobuf.internal import wire_format
@@ -52,22 +59,29 @@ from google.protobuf import descriptor
_FieldDescriptor = descriptor.FieldDescriptor
+def SupportsOpenEnums(field_descriptor):
+ return field_descriptor.containing_type.syntax == "proto3"
-def GetTypeChecker(cpp_type, field_type):
+def GetTypeChecker(field):
"""Returns a type checker for a message field of the specified types.
Args:
- cpp_type: C++ type of the field (see descriptor.py).
- field_type: Protocol message field type (see descriptor.py).
+ field: FieldDescriptor object for this field.
Returns:
An instance of TypeChecker which can be used to verify the types
of values assigned to a field of the specified type.
"""
- if (cpp_type == _FieldDescriptor.CPPTYPE_STRING and
- field_type == _FieldDescriptor.TYPE_STRING):
+ if (field.cpp_type == _FieldDescriptor.CPPTYPE_STRING and
+ field.type == _FieldDescriptor.TYPE_STRING):
return UnicodeValueChecker()
- return _VALUE_CHECKERS[cpp_type]
+ if field.cpp_type == _FieldDescriptor.CPPTYPE_ENUM:
+ if SupportsOpenEnums(field):
+ # When open enums are supported, any int32 can be assigned.
+ return _VALUE_CHECKERS[_FieldDescriptor.CPPTYPE_INT32]
+ else:
+ return EnumValueChecker(field.enum_type)
+ return _VALUE_CHECKERS[field.cpp_type]
# None of the typecheckers below make any attempt to guard against people
@@ -85,10 +99,15 @@ class TypeChecker(object):
self._acceptable_types = acceptable_types
def CheckValue(self, proposed_value):
+ """Type check the provided value and return it.
+
+ The returned value might have been normalized to another type.
+ """
if not isinstance(proposed_value, self._acceptable_types):
message = ('%.1024r has type %s, but expected one of: %s' %
(proposed_value, type(proposed_value), self._acceptable_types))
raise TypeError(message)
+ return proposed_value
# IntValueChecker and its subclasses perform integer type-checks
@@ -104,28 +123,53 @@ class IntValueChecker(object):
raise TypeError(message)
if not self._MIN <= proposed_value <= self._MAX:
raise ValueError('Value out of range: %d' % proposed_value)
+ # We force 32-bit values to int and 64-bit values to long to make
+ # alternate implementations where the distinction is more significant
+ # (e.g. the C++ implementation) simpler.
+ proposed_value = self._TYPE(proposed_value)
+ return proposed_value
+
+
+class EnumValueChecker(object):
+
+ """Checker used for enum fields. Performs type-check and range check."""
+
+ def __init__(self, enum_type):
+ self._enum_type = enum_type
+
+ def CheckValue(self, proposed_value):
+ if not isinstance(proposed_value, (int, long)):
+ message = ('%.1024r has type %s, but expected one of: %s' %
+ (proposed_value, type(proposed_value), (int, long)))
+ raise TypeError(message)
+ if proposed_value not in self._enum_type.values_by_number:
+ raise ValueError('Unknown enum value: %d' % proposed_value)
+ return proposed_value
class UnicodeValueChecker(object):
- """Checker used for string fields."""
+ """Checker used for string fields.
+
+ Always returns a unicode value, even if the input is of type str.
+ """
def CheckValue(self, proposed_value):
- if not isinstance(proposed_value, (str, unicode)):
+ if not isinstance(proposed_value, (bytes, unicode)):
message = ('%.1024r has type %s, but expected one of: %s' %
- (proposed_value, type(proposed_value), (str, unicode)))
+ (proposed_value, type(proposed_value), (bytes, unicode)))
raise TypeError(message)
- # If the value is of type 'str' make sure that it is in 7-bit ASCII
- # encoding.
- if isinstance(proposed_value, str):
+ # If the value is of type 'bytes' make sure that it is valid UTF-8 data.
+ if isinstance(proposed_value, bytes):
try:
- unicode(proposed_value, 'ascii')
+ proposed_value = proposed_value.decode('utf-8')
except UnicodeDecodeError:
- raise ValueError('%.1024r has type str, but isn\'t in 7-bit ASCII '
- 'encoding. Non-ASCII strings must be converted to '
+ raise ValueError('%.1024r has type bytes, but isn\'t valid UTF-8 '
+ 'encoding. Non-UTF-8 strings must be converted to '
'unicode objects before being added.' %
(proposed_value))
+ return proposed_value
class Int32ValueChecker(IntValueChecker):
@@ -133,21 +177,25 @@ class Int32ValueChecker(IntValueChecker):
# efficient.
_MIN = -2147483648
_MAX = 2147483647
+ _TYPE = int
class Uint32ValueChecker(IntValueChecker):
_MIN = 0
_MAX = (1 << 32) - 1
+ _TYPE = int
class Int64ValueChecker(IntValueChecker):
_MIN = -(1 << 63)
_MAX = (1 << 63) - 1
+ _TYPE = long
class Uint64ValueChecker(IntValueChecker):
_MIN = 0
_MAX = (1 << 64) - 1
+ _TYPE = long
# Type-checkers for all scalar CPPTYPEs.
@@ -161,8 +209,7 @@ _VALUE_CHECKERS = {
_FieldDescriptor.CPPTYPE_FLOAT: TypeChecker(
float, int, long),
_FieldDescriptor.CPPTYPE_BOOL: TypeChecker(bool, int),
- _FieldDescriptor.CPPTYPE_ENUM: Int32ValueChecker(),
- _FieldDescriptor.CPPTYPE_STRING: TypeChecker(str),
+ _FieldDescriptor.CPPTYPE_STRING: TypeChecker(bytes),
}
diff --git a/python/google/protobuf/internal/unknown_fields_test.py b/python/google/protobuf/internal/unknown_fields_test.py
new file mode 100755
index 00000000..59f9ae4c
--- /dev/null
+++ b/python/google/protobuf/internal/unknown_fields_test.py
@@ -0,0 +1,264 @@
+#! /usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# 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.
+
+"""Test for preservation of unknown fields in the pure Python implementation."""
+
+__author__ = 'bohdank@google.com (Bohdan Koval)'
+
+from google.apputils import basetest
+from google.protobuf import unittest_mset_pb2
+from google.protobuf import unittest_pb2
+from google.protobuf import unittest_proto3_arena_pb2
+from google.protobuf.internal import api_implementation
+from google.protobuf.internal import encoder
+from google.protobuf.internal import missing_enum_values_pb2
+from google.protobuf.internal import test_util
+from google.protobuf.internal import type_checkers
+
+
+class UnknownFieldsTest(basetest.TestCase):
+
+ def setUp(self):
+ self.descriptor = unittest_pb2.TestAllTypes.DESCRIPTOR
+ self.all_fields = unittest_pb2.TestAllTypes()
+ test_util.SetAllFields(self.all_fields)
+ self.all_fields_data = self.all_fields.SerializeToString()
+ self.empty_message = unittest_pb2.TestEmptyMessage()
+ self.empty_message.ParseFromString(self.all_fields_data)
+
+ def testSerialize(self):
+ data = self.empty_message.SerializeToString()
+
+ # Don't use assertEqual because we don't want to dump raw binary data to
+ # stdout.
+ self.assertTrue(data == self.all_fields_data)
+
+ def testSerializeProto3(self):
+ # Verify that proto3 doesn't preserve unknown fields.
+ message = unittest_proto3_arena_pb2.TestEmptyMessage()
+ message.ParseFromString(self.all_fields_data)
+ self.assertEqual(0, len(message.SerializeToString()))
+
+ def testByteSize(self):
+ self.assertEqual(self.all_fields.ByteSize(), self.empty_message.ByteSize())
+
+ def testListFields(self):
+ # Make sure ListFields doesn't return unknown fields.
+ self.assertEqual(0, len(self.empty_message.ListFields()))
+
+ def testSerializeMessageSetWireFormatUnknownExtension(self):
+ # Create a message using the message set wire format with an unknown
+ # message.
+ raw = unittest_mset_pb2.RawMessageSet()
+
+ # Add an unknown extension.
+ item = raw.item.add()
+ item.type_id = 1545009
+ message1 = unittest_mset_pb2.TestMessageSetExtension1()
+ message1.i = 12345
+ item.message = message1.SerializeToString()
+
+ serialized = raw.SerializeToString()
+
+ # Parse message using the message set wire format.
+ proto = unittest_mset_pb2.TestMessageSet()
+ proto.MergeFromString(serialized)
+
+ # Verify that the unknown extension is serialized unchanged
+ reserialized = proto.SerializeToString()
+ new_raw = unittest_mset_pb2.RawMessageSet()
+ new_raw.MergeFromString(reserialized)
+ self.assertEqual(raw, new_raw)
+
+ # C++ implementation for proto2 does not currently take into account unknown
+ # fields when checking equality.
+ #
+ # TODO(haberman): fix this.
+ @basetest.unittest.skipIf(
+ api_implementation.Type() == 'cpp' and api_implementation.Version() == 2,
+ 'C++ implementation does not expose unknown fields to Python')
+ def testEquals(self):
+ message = unittest_pb2.TestEmptyMessage()
+ message.ParseFromString(self.all_fields_data)
+ self.assertEqual(self.empty_message, message)
+
+ self.all_fields.ClearField('optional_string')
+ message.ParseFromString(self.all_fields.SerializeToString())
+ self.assertNotEqual(self.empty_message, message)
+
+
+@basetest.unittest.skipIf(
+ api_implementation.Type() == 'cpp' and api_implementation.Version() == 2,
+ 'C++ implementation does not expose unknown fields to Python')
+class UnknownFieldsAccessorsTest(basetest.TestCase):
+
+ def setUp(self):
+ self.descriptor = unittest_pb2.TestAllTypes.DESCRIPTOR
+ self.all_fields = unittest_pb2.TestAllTypes()
+ test_util.SetAllFields(self.all_fields)
+ self.all_fields_data = self.all_fields.SerializeToString()
+ self.empty_message = unittest_pb2.TestEmptyMessage()
+ self.empty_message.ParseFromString(self.all_fields_data)
+ self.unknown_fields = self.empty_message._unknown_fields
+
+ def GetField(self, name):
+ field_descriptor = self.descriptor.fields_by_name[name]
+ wire_type = type_checkers.FIELD_TYPE_TO_WIRE_TYPE[field_descriptor.type]
+ field_tag = encoder.TagBytes(field_descriptor.number, wire_type)
+ result_dict = {}
+ for tag_bytes, value in self.unknown_fields:
+ if tag_bytes == field_tag:
+ decoder = unittest_pb2.TestAllTypes._decoders_by_tag[tag_bytes][0]
+ decoder(value, 0, len(value), self.all_fields, result_dict)
+ return result_dict[field_descriptor]
+
+ def testEnum(self):
+ value = self.GetField('optional_nested_enum')
+ self.assertEqual(self.all_fields.optional_nested_enum, value)
+
+ def testRepeatedEnum(self):
+ value = self.GetField('repeated_nested_enum')
+ self.assertEqual(self.all_fields.repeated_nested_enum, value)
+
+ def testVarint(self):
+ value = self.GetField('optional_int32')
+ self.assertEqual(self.all_fields.optional_int32, value)
+
+ def testFixed32(self):
+ value = self.GetField('optional_fixed32')
+ self.assertEqual(self.all_fields.optional_fixed32, value)
+
+ def testFixed64(self):
+ value = self.GetField('optional_fixed64')
+ self.assertEqual(self.all_fields.optional_fixed64, value)
+
+ def testLengthDelimited(self):
+ value = self.GetField('optional_string')
+ self.assertEqual(self.all_fields.optional_string, value)
+
+ def testGroup(self):
+ value = self.GetField('optionalgroup')
+ self.assertEqual(self.all_fields.optionalgroup, value)
+
+ def testCopyFrom(self):
+ message = unittest_pb2.TestEmptyMessage()
+ message.CopyFrom(self.empty_message)
+ self.assertEqual(self.unknown_fields, message._unknown_fields)
+
+ def testMergeFrom(self):
+ message = unittest_pb2.TestAllTypes()
+ message.optional_int32 = 1
+ message.optional_uint32 = 2
+ source = unittest_pb2.TestEmptyMessage()
+ source.ParseFromString(message.SerializeToString())
+
+ message.ClearField('optional_int32')
+ message.optional_int64 = 3
+ message.optional_uint32 = 4
+ destination = unittest_pb2.TestEmptyMessage()
+ destination.ParseFromString(message.SerializeToString())
+ unknown_fields = destination._unknown_fields[:]
+
+ destination.MergeFrom(source)
+ self.assertEqual(unknown_fields + source._unknown_fields,
+ destination._unknown_fields)
+
+ def testClear(self):
+ self.empty_message.Clear()
+ self.assertEqual(0, len(self.empty_message._unknown_fields))
+
+ def testUnknownExtensions(self):
+ message = unittest_pb2.TestEmptyMessageWithExtensions()
+ message.ParseFromString(self.all_fields_data)
+ self.assertEqual(self.empty_message._unknown_fields,
+ message._unknown_fields)
+
+
+
+@basetest.unittest.skipIf(
+ api_implementation.Type() == 'cpp' and api_implementation.Version() == 2,
+ 'C++ implementation does not expose unknown fields to Python')
+class UnknownEnumValuesTest(basetest.TestCase):
+
+ def setUp(self):
+ self.descriptor = missing_enum_values_pb2.TestEnumValues.DESCRIPTOR
+
+ self.message = missing_enum_values_pb2.TestEnumValues()
+ self.message.optional_nested_enum = (
+ missing_enum_values_pb2.TestEnumValues.ZERO)
+ self.message.repeated_nested_enum.extend([
+ missing_enum_values_pb2.TestEnumValues.ZERO,
+ missing_enum_values_pb2.TestEnumValues.ONE,
+ ])
+ self.message.packed_nested_enum.extend([
+ missing_enum_values_pb2.TestEnumValues.ZERO,
+ missing_enum_values_pb2.TestEnumValues.ONE,
+ ])
+ self.message_data = self.message.SerializeToString()
+ self.missing_message = missing_enum_values_pb2.TestMissingEnumValues()
+ self.missing_message.ParseFromString(self.message_data)
+ self.unknown_fields = self.missing_message._unknown_fields
+
+ def GetField(self, name):
+ field_descriptor = self.descriptor.fields_by_name[name]
+ wire_type = type_checkers.FIELD_TYPE_TO_WIRE_TYPE[field_descriptor.type]
+ field_tag = encoder.TagBytes(field_descriptor.number, wire_type)
+ result_dict = {}
+ for tag_bytes, value in self.unknown_fields:
+ if tag_bytes == field_tag:
+ decoder = missing_enum_values_pb2.TestEnumValues._decoders_by_tag[
+ tag_bytes][0]
+ decoder(value, 0, len(value), self.message, result_dict)
+ return result_dict[field_descriptor]
+
+ def testUnknownEnumValue(self):
+ self.assertFalse(self.missing_message.HasField('optional_nested_enum'))
+ value = self.GetField('optional_nested_enum')
+ self.assertEqual(self.message.optional_nested_enum, value)
+
+ def testUnknownRepeatedEnumValue(self):
+ value = self.GetField('repeated_nested_enum')
+ self.assertEqual(self.message.repeated_nested_enum, value)
+
+ def testUnknownPackedEnumValue(self):
+ value = self.GetField('packed_nested_enum')
+ self.assertEqual(self.message.packed_nested_enum, value)
+
+ def testRoundTrip(self):
+ new_message = missing_enum_values_pb2.TestEnumValues()
+ new_message.ParseFromString(self.missing_message.SerializeToString())
+ self.assertEqual(self.message, new_message)
+
+
+if __name__ == '__main__':
+ basetest.main()
diff --git a/python/google/protobuf/internal/wire_format.py b/python/google/protobuf/internal/wire_format.py
index c941fe1a..883f5255 100755
--- a/python/google/protobuf/internal/wire_format.py
+++ b/python/google/protobuf/internal/wire_format.py
@@ -1,6 +1,6 @@
# Protocol Buffers - Google's data interchange format
# Copyright 2008 Google Inc. All rights reserved.
-# http://code.google.com/p/protobuf/
+# 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
diff --git a/python/google/protobuf/internal/wire_format_test.py b/python/google/protobuf/internal/wire_format_test.py
index 76007786..f39035ca 100755
--- a/python/google/protobuf/internal/wire_format_test.py
+++ b/python/google/protobuf/internal/wire_format_test.py
@@ -2,7 +2,7 @@
#
# Protocol Buffers - Google's data interchange format
# Copyright 2008 Google Inc. All rights reserved.
-# http://code.google.com/p/protobuf/
+# 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
@@ -34,12 +34,12 @@
__author__ = 'robinson@google.com (Will Robinson)'
-import unittest
+from google.apputils import basetest
from google.protobuf import message
from google.protobuf.internal import wire_format
-class WireFormatTest(unittest.TestCase):
+class WireFormatTest(basetest.TestCase):
def testPackTag(self):
field_number = 0xabc
@@ -195,7 +195,7 @@ class WireFormatTest(unittest.TestCase):
# Test UTF-8 string byte size calculation.
# 1 byte for tag, 1 byte for length, 8 bytes for content.
self.assertEqual(10, wire_format.StringByteSize(
- 5, unicode('\xd0\xa2\xd0\xb5\xd1\x81\xd1\x82', 'utf-8')))
+ 5, b'\xd0\xa2\xd0\xb5\xd1\x81\xd1\x82'.decode('utf-8')))
class MockMessage(object):
def __init__(self, byte_size):
@@ -250,4 +250,4 @@ class WireFormatTest(unittest.TestCase):
if __name__ == '__main__':
- unittest.main()
+ basetest.main()
diff --git a/python/google/protobuf/message.py b/python/google/protobuf/message.py
index 6f19f85f..de2f5697 100755
--- a/python/google/protobuf/message.py
+++ b/python/google/protobuf/message.py
@@ -1,6 +1,6 @@
# Protocol Buffers - Google's data interchange format
# Copyright 2008 Google Inc. All rights reserved.
-# http://code.google.com/p/protobuf/
+# 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
@@ -36,7 +36,6 @@
__author__ = 'robinson@google.com (Will Robinson)'
-
class Error(Exception): pass
class DecodeError(Error): pass
class EncodeError(Error): pass
@@ -73,6 +72,7 @@ class Message(object):
return clone
def __eq__(self, other_msg):
+ """Recursively compares two messages by value and structure."""
raise NotImplementedError
def __ne__(self, other_msg):
@@ -83,9 +83,11 @@ class Message(object):
raise TypeError('unhashable object')
def __str__(self):
+ """Outputs a human-readable representation of the message."""
raise NotImplementedError
def __unicode__(self):
+ """Outputs a human-readable representation of the message."""
raise NotImplementedError
def MergeFrom(self, other_msg):
@@ -174,7 +176,11 @@ class Message(object):
raise NotImplementedError
def ParseFromString(self, serialized):
- """Like MergeFromString(), except we clear the object first."""
+ """Parse serialized protocol buffer data into this message.
+
+ Like MergeFromString(), except we clear the object first and
+ do not return the value that MergeFromString returns.
+ """
self.Clear()
self.MergeFromString(serialized)
@@ -226,12 +232,21 @@ class Message(object):
raise NotImplementedError
def HasField(self, field_name):
- """Checks if a certain field is set for the message. Note if the
- field_name is not defined in the message descriptor, ValueError will be
- raised."""
+ """Checks if a certain field is set for the message, or if any field inside
+ a oneof group is set. Note that if the field_name is not defined in the
+ message descriptor, ValueError will be raised."""
raise NotImplementedError
def ClearField(self, field_name):
+ """Clears the contents of a given field, or the field set inside a oneof
+ group. If the name neither refers to a defined field or oneof group,
+ ValueError is raised."""
+ raise NotImplementedError
+
+ def WhichOneof(self, oneof_group):
+ """Returns the name of the field that is set inside a oneof group, or
+ None if no field is set. If no group with the given name exists, ValueError
+ will be raised."""
raise NotImplementedError
def HasExtension(self, extension_handle):
@@ -266,3 +281,12 @@ class Message(object):
via a previous _SetListener() call.
"""
raise NotImplementedError
+
+ def __getstate__(self):
+ """Support the pickle protocol."""
+ return dict(serialized=self.SerializePartialToString())
+
+ def __setstate__(self, state):
+ """Support the pickle protocol."""
+ self.__init__()
+ self.ParseFromString(state['serialized'])
diff --git a/python/google/protobuf/message_factory.py b/python/google/protobuf/message_factory.py
new file mode 100644
index 00000000..7fd7bec0
--- /dev/null
+++ b/python/google/protobuf/message_factory.py
@@ -0,0 +1,155 @@
+# 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.
+
+#PY25 compatible for GAE.
+#
+# Copyright 2012 Google Inc. All Rights Reserved.
+
+"""Provides a factory class for generating dynamic messages.
+
+The easiest way to use this class is if you have access to the FileDescriptor
+protos containing the messages you want to create you can just do the following:
+
+message_classes = message_factory.GetMessages(iterable_of_file_descriptors)
+my_proto_instance = message_classes['some.proto.package.MessageName']()
+"""
+
+__author__ = 'matthewtoia@google.com (Matt Toia)'
+
+import sys ##PY25
+from google.protobuf import descriptor_database
+from google.protobuf import descriptor_pool
+from google.protobuf import message
+from google.protobuf import reflection
+
+
+class MessageFactory(object):
+ """Factory for creating Proto2 messages from descriptors in a pool."""
+
+ def __init__(self, pool=None):
+ """Initializes a new factory."""
+ self.pool = (pool or descriptor_pool.DescriptorPool(
+ descriptor_database.DescriptorDatabase()))
+
+ # local cache of all classes built from protobuf descriptors
+ self._classes = {}
+
+ def GetPrototype(self, descriptor):
+ """Builds a proto2 message class based on the passed in descriptor.
+
+ Passing a descriptor with a fully qualified name matching a previous
+ invocation will cause the same class to be returned.
+
+ Args:
+ descriptor: The descriptor to build from.
+
+ Returns:
+ A class describing the passed in descriptor.
+ """
+ if descriptor.full_name not in self._classes:
+ descriptor_name = descriptor.name
+ if sys.version_info[0] < 3: ##PY25
+##!PY25 if str is bytes: # PY2
+ descriptor_name = descriptor.name.encode('ascii', 'ignore')
+ result_class = reflection.GeneratedProtocolMessageType(
+ descriptor_name,
+ (message.Message,),
+ {'DESCRIPTOR': descriptor, '__module__': None})
+ # If module not set, it wrongly points to the reflection.py module.
+ self._classes[descriptor.full_name] = result_class
+ for field in descriptor.fields:
+ if field.message_type:
+ self.GetPrototype(field.message_type)
+ for extension in result_class.DESCRIPTOR.extensions:
+ if extension.containing_type.full_name not in self._classes:
+ self.GetPrototype(extension.containing_type)
+ extended_class = self._classes[extension.containing_type.full_name]
+ extended_class.RegisterExtension(extension)
+ return self._classes[descriptor.full_name]
+
+ def GetMessages(self, files):
+ """Gets all the messages from a specified file.
+
+ This will find and resolve dependencies, failing if the descriptor
+ pool cannot satisfy them.
+
+ Args:
+ files: The file names to extract messages from.
+
+ Returns:
+ A dictionary mapping proto names to the message classes. This will include
+ any dependent messages as well as any messages defined in the same file as
+ a specified message.
+ """
+ result = {}
+ for file_name in files:
+ file_desc = self.pool.FindFileByName(file_name)
+ for name, msg in file_desc.message_types_by_name.iteritems():
+ if file_desc.package:
+ full_name = '.'.join([file_desc.package, name])
+ else:
+ full_name = msg.name
+ result[full_name] = self.GetPrototype(
+ self.pool.FindMessageTypeByName(full_name))
+
+ # While the extension FieldDescriptors are created by the descriptor pool,
+ # the python classes created in the factory need them to be registered
+ # explicitly, which is done below.
+ #
+ # The call to RegisterExtension will specifically check if the
+ # extension was already registered on the object and either
+ # ignore the registration if the original was the same, or raise
+ # an error if they were different.
+
+ for name, extension in file_desc.extensions_by_name.iteritems():
+ if extension.containing_type.full_name not in self._classes:
+ self.GetPrototype(extension.containing_type)
+ extended_class = self._classes[extension.containing_type.full_name]
+ extended_class.RegisterExtension(extension)
+ return result
+
+
+_FACTORY = MessageFactory()
+
+
+def GetMessages(file_protos):
+ """Builds a dictionary of all the messages available in a set of files.
+
+ Args:
+ file_protos: A sequence of file protos to build messages out of.
+
+ Returns:
+ A dictionary mapping proto names to the message classes. This will include
+ any dependent messages as well as any messages defined in the same file as
+ a specified message.
+ """
+ for file_proto in file_protos:
+ _FACTORY.pool.Add(file_proto)
+ return _FACTORY.GetMessages([file_proto.name for file_proto in file_protos])
diff --git a/python/google/protobuf/proto_builder.py b/python/google/protobuf/proto_builder.py
new file mode 100644
index 00000000..1fa28f1a
--- /dev/null
+++ b/python/google/protobuf/proto_builder.py
@@ -0,0 +1,98 @@
+# 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.
+
+"""Dynamic Protobuf class creator."""
+
+import hashlib
+import os
+
+from google.protobuf import descriptor_pb2
+from google.protobuf import message_factory
+
+
+def _GetMessageFromFactory(factory, full_name):
+ """Get a proto class from the MessageFactory by name.
+
+ Args:
+ factory: a MessageFactory instance.
+ full_name: str, the fully qualified name of the proto type.
+ Returns:
+ a class, for the type identified by full_name.
+ Raises:
+ KeyError, if the proto is not found in the factory's descriptor pool.
+ """
+ proto_descriptor = factory.pool.FindMessageTypeByName(full_name)
+ proto_cls = factory.GetPrototype(proto_descriptor)
+ return proto_cls
+
+
+def MakeSimpleProtoClass(fields, full_name, pool=None):
+ """Create a Protobuf class whose fields are basic types.
+
+ Note: this doesn't validate field names!
+
+ Args:
+ fields: dict of {name: field_type} mappings for each field in the proto.
+ full_name: str, the fully-qualified name of the proto type.
+ pool: optional DescriptorPool instance.
+ Returns:
+ a class, the new protobuf class with a FileDescriptor.
+ """
+ factory = message_factory.MessageFactory(pool=pool)
+ try:
+ proto_cls = _GetMessageFromFactory(factory, full_name)
+ return proto_cls
+ except KeyError:
+ # The factory's DescriptorPool doesn't know about this class yet.
+ pass
+
+ # Use a consistent file name that is unlikely to conflict with any imported
+ # proto files.
+ fields_hash = hashlib.sha1()
+ for f_name, f_type in sorted(fields.items()):
+ fields_hash.update(f_name.encode('utf8'))
+ fields_hash.update(str(f_type).encode('utf8'))
+ proto_file_name = fields_hash.hexdigest() + '.proto'
+
+ package, name = full_name.rsplit('.', 1)
+ file_proto = descriptor_pb2.FileDescriptorProto()
+ file_proto.name = os.path.join(package.replace('.', '/'), proto_file_name)
+ file_proto.package = package
+ desc_proto = file_proto.message_type.add()
+ desc_proto.name = name
+ for f_number, (f_name, f_type) in enumerate(sorted(fields.items()), 1):
+ field_proto = desc_proto.field.add()
+ field_proto.name = f_name
+ field_proto.number = f_number
+ field_proto.label = descriptor_pb2.FieldDescriptorProto.LABEL_OPTIONAL
+ field_proto.type = f_type
+
+ factory.pool.Add(file_proto)
+ return _GetMessageFromFactory(factory, full_name)
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..e69de29b
--- /dev/null
+++ b/python/google/protobuf/pyext/__init__.py
diff --git a/python/google/protobuf/pyext/cpp_message.py b/python/google/protobuf/pyext/cpp_message.py
new file mode 100644
index 00000000..037bb72c
--- /dev/null
+++ b/python/google/protobuf/pyext/cpp_message.py
@@ -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.
+
+"""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
+from google.protobuf import message
+
+
+def NewMessage(bases, message_descriptor, dictionary):
+ """Creates a new protocol message *class*."""
+ new_bases = []
+ for base in bases:
+ if base is message.Message:
+ # _message.Message must come before message.Message as it
+ # overrides methods in that class.
+ new_bases.append(_message.Message)
+ new_bases.append(base)
+ return tuple(new_bases)
+
+
+def InitMessage(message_descriptor, cls):
+ """Finalizes the creation of a message class."""
+ cls.AddDescriptors(message_descriptor)
diff --git a/python/google/protobuf/pyext/descriptor.cc b/python/google/protobuf/pyext/descriptor.cc
new file mode 100644
index 00000000..e77d0bb9
--- /dev/null
+++ b/python/google/protobuf/pyext/descriptor.cc
@@ -0,0 +1,1471 @@
+// 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>
+
+#define C(str) const_cast<char*>(str)
+
+#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 {
+
+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) {
+ PyThreadState *state = PyThreadState_GET();
+ if (state == NULL) {
+ return false;
+ }
+ PyFrameObject* frame = state->frame;
+ 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;
+ }
+ 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.
+
+// 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 unkown fields, and we need to parse them again.
+//
+// Always returns a new reference.
+template<class DescriptorClass>
+static PyObject* GetOrBuildOptions(const DescriptorClass *descriptor) {
+ hash_map<const void*, PyObject*>* descriptor_options =
+ GetDescriptorPool()->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();
+ PyObject* message_class(cdescriptor_pool::GetMessageClass(
+ GetDescriptorPool(), 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, NULL));
+ if (value == NULL) {
+ 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(GetDescriptorPool()->pool,
+ cmessage::GetMessageFactory());
+ bool success = cmsg->message->MergePartialFromCodedStream(&input);
+ if (!success) {
+ PyErr_Format(PyExc_ValueError, "Error parsing Options message");
+ return NULL;
+ }
+ }
+
+ // Cache the result.
+ Py_INCREF(value);
+ (*GetDescriptorPool()->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;
+} 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.
+// Always return a new reference.
+PyObject* NewInternedDescriptor(PyTypeObject* type, const void* descriptor) {
+ 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 =
+ GetDescriptorPool()->interned_descriptors->find(descriptor);
+ if (it != GetDescriptorPool()->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.
+ GetDescriptorPool()->interned_descriptors->insert(
+ std::make_pair(descriptor, reinterpret_cast<PyObject*>(py_descriptor)));
+
+ return reinterpret_cast<PyObject*>(py_descriptor);
+}
+
+static void Dealloc(PyBaseDescriptor* self) {
+ // Remove from interned dictionary
+ GetDescriptorPool()->interned_descriptors->erase(self->descriptor);
+ Py_TYPE(self)->tp_free(reinterpret_cast<PyObject*>(self));
+}
+
+static PyGetSetDef Getters[] = {
+ {NULL}
+};
+
+PyTypeObject PyBaseDescriptor_Type = {
+ PyVarObject_HEAD_INIT(&PyType_Type, 0)
+ // Keep the fully qualified _message symbol in a line for opensource.
+ "google.protobuf.internal._message."
+ "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_New(_GetDescriptor(self)->file());
+}
+
+static PyObject* GetConcreteClass(PyBaseDescriptor* self, void *closure) {
+ PyObject* concrete_class(cdescriptor_pool::GetMessageClass(
+ GetDescriptorPool(), _GetDescriptor(self)));
+ Py_XINCREF(concrete_class);
+ return concrete_class;
+}
+
+static int SetConcreteClass(PyBaseDescriptor *self, PyObject *value,
+ void *closure) {
+ // This attribute is also set from reflection.py. Check that it's actually a
+ // no-op.
+ if (value != cdescriptor_pool::GetMessageClass(
+ GetDescriptorPool(), _GetDescriptor(self))) {
+ PyErr_SetString(PyExc_AttributeError, "Cannot change _concrete_class");
+ }
+ return 0;
+}
+
+static PyObject* GetFieldsByName(PyBaseDescriptor* self, void *closure) {
+ return NewMessageFieldsByName(_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_New(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[] = {
+ { C("name"), (getter)GetName, NULL, "Last name", NULL},
+ { C("full_name"), (getter)GetFullName, NULL, "Full name", NULL},
+ { C("_concrete_class"), (getter)GetConcreteClass, (setter)SetConcreteClass, "concrete class", NULL},
+ { C("file"), (getter)GetFile, NULL, "File descriptor", NULL},
+
+ { C("fields"), (getter)GetFieldsSeq, NULL, "Fields sequence", NULL},
+ { C("fields_by_name"), (getter)GetFieldsByName, NULL, "Fields by name", NULL},
+ { C("fields_by_number"), (getter)GetFieldsByNumber, NULL, "Fields by number", NULL},
+ { C("nested_types"), (getter)GetNestedTypesSeq, NULL, "Nested types sequence", NULL},
+ { C("nested_types_by_name"), (getter)GetNestedTypesByName, NULL, "Nested types by name", NULL},
+ { C("extensions"), (getter)GetExtensions, NULL, "Extensions Sequence", NULL},
+ { C("extensions_by_name"), (getter)GetExtensionsByName, NULL, "Extensions by name", NULL},
+ { C("extension_ranges"), (getter)GetExtensionRanges, NULL, "Extension ranges", NULL},
+ { C("enum_types"), (getter)GetEnumsSeq, NULL, "Enum sequence", NULL},
+ { C("enum_types_by_name"), (getter)GetEnumTypesByName, NULL, "Enum types by name", NULL},
+ { C("enum_values_by_name"), (getter)GetEnumValuesByName, NULL, "Enum values by name", NULL},
+ { C("oneofs_by_name"), (getter)GetOneofsByName, NULL, "Oneofs by name", NULL},
+ { C("oneofs"), (getter)GetOneofsSeq, NULL, "Oneofs by name", NULL},
+ { C("containing_type"), (getter)GetContainingType, (setter)SetContainingType, "Containing type", NULL},
+ { C("is_extendable"), (getter)IsExtendable, (setter)NULL, NULL, NULL},
+ { C("has_options"), (getter)GetHasOptions, (setter)SetHasOptions, "Has Options", NULL},
+ { C("_options"), (getter)NULL, (setter)SetOptions, "Options", NULL},
+ { C("syntax"), (getter)GetSyntax, (setter)NULL, "Syntax", NULL},
+ {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)
+ // Keep the fully qualified _message symbol in a line for opensource.
+ C("google.protobuf.internal._message."
+ "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
+ C("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_New(
+ const Descriptor* message_descriptor) {
+ return descriptor::NewInternedDescriptor(
+ &PyMessageDescriptor_Type, message_descriptor);
+}
+
+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* 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_New(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_New(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_New(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_New(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_New(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[] = {
+ { C("full_name"), (getter)GetFullName, NULL, "Full name", NULL},
+ { C("name"), (getter)GetName, NULL, "Unqualified name", NULL},
+ { C("type"), (getter)GetType, NULL, "C++ Type", NULL},
+ { C("cpp_type"), (getter)GetCppType, NULL, "C++ Type", NULL},
+ { C("label"), (getter)GetLabel, NULL, "Label", NULL},
+ { C("number"), (getter)GetNumber, NULL, "Number", NULL},
+ { C("index"), (getter)GetIndex, NULL, "Index", NULL},
+ { C("default_value"), (getter)GetDefaultValue, NULL, "Default Value", NULL},
+ { C("has_default_value"), (getter)HasDefaultValue, NULL, NULL, NULL},
+ { C("is_extension"), (getter)IsExtension, NULL, "ID", NULL},
+ { C("id"), (getter)GetID, NULL, "ID", NULL},
+ { C("_cdescriptor"), (getter)GetCDescriptor, NULL, "HAACK REMOVE ME", NULL},
+
+ { C("message_type"), (getter)GetMessageType, (setter)SetMessageType, "Message type", NULL},
+ { C("enum_type"), (getter)GetEnumType, (setter)SetEnumType, "Enum type", NULL},
+ { C("containing_type"), (getter)GetContainingType, (setter)SetContainingType, "Containing type", NULL},
+ { C("extension_scope"), (getter)GetExtensionScope, (setter)NULL, "Extension scope", NULL},
+ { C("containing_oneof"), (getter)GetContainingOneof, (setter)SetContainingOneof, "Containing oneof", NULL},
+ { C("has_options"), (getter)GetHasOptions, (setter)SetHasOptions, "Has Options", NULL},
+ { C("_options"), (getter)NULL, (setter)SetOptions, "Options", NULL},
+ {NULL}
+};
+
+static PyMethodDef Methods[] = {
+ { "GetOptions", (PyCFunction)GetOptions, METH_NOARGS, },
+ {NULL}
+};
+
+} // namespace field_descriptor
+
+PyTypeObject PyFieldDescriptor_Type = {
+ PyVarObject_HEAD_INIT(&PyType_Type, 0)
+ C("google.protobuf.internal."
+ "_message.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
+ C("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_New(
+ const FieldDescriptor* field_descriptor) {
+ return descriptor::NewInternedDescriptor(
+ &PyFieldDescriptor_Type, field_descriptor);
+}
+
+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_New(_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_New(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[] = {
+ { C("full_name"), (getter)GetFullName, NULL, "Full name", NULL},
+ { C("name"), (getter)GetName, NULL, "last name", NULL},
+ { C("file"), (getter)GetFile, NULL, "File descriptor", NULL},
+ { C("values"), (getter)GetEnumvaluesSeq, NULL, "values", NULL},
+ { C("values_by_name"), (getter)GetEnumvaluesByName, NULL, "Enumvalues by name", NULL},
+ { C("values_by_number"), (getter)GetEnumvaluesByNumber, NULL, "Enumvalues by number", NULL},
+
+ { C("containing_type"), (getter)GetContainingType, (setter)SetContainingType, "Containing type", NULL},
+ { C("has_options"), (getter)GetHasOptions, (setter)SetHasOptions, "Has Options", NULL},
+ { C("_options"), (getter)NULL, (setter)SetOptions, "Options", NULL},
+ {NULL}
+};
+
+} // namespace enum_descriptor
+
+PyTypeObject PyEnumDescriptor_Type = {
+ PyVarObject_HEAD_INIT(&PyType_Type, 0)
+ // Keep the fully qualified _message symbol in a line for opensource.
+ C("google.protobuf.internal._message."
+ "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
+ C("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_getset
+ 0, // tp_members
+ enum_descriptor::Getters, // tp_getset
+ &descriptor::PyBaseDescriptor_Type, // tp_base
+};
+
+PyObject* PyEnumDescriptor_New(
+ const EnumDescriptor* enum_descriptor) {
+ return descriptor::NewInternedDescriptor(
+ &PyEnumDescriptor_Type, enum_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_New(_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[] = {
+ { C("name"), (getter)GetName, NULL, "name", NULL},
+ { C("number"), (getter)GetNumber, NULL, "number", NULL},
+ { C("index"), (getter)GetIndex, NULL, "index", NULL},
+ { C("type"), (getter)GetType, NULL, "index", NULL},
+
+ { C("has_options"), (getter)GetHasOptions, (setter)SetHasOptions, "Has Options", NULL},
+ { C("_options"), (getter)NULL, (setter)SetOptions, "Options", NULL},
+ {NULL}
+};
+
+static PyMethodDef Methods[] = {
+ { "GetOptions", (PyCFunction)GetOptions, METH_NOARGS, },
+ {NULL}
+};
+
+} // namespace enumvalue_descriptor
+
+PyTypeObject PyEnumValueDescriptor_Type = {
+ PyVarObject_HEAD_INIT(&PyType_Type, 0)
+ C("google.protobuf.internal."
+ "_message.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
+ C("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_New(
+ const EnumValueDescriptor* enumvalue_descriptor) {
+ return descriptor::NewInternedDescriptor(
+ &PyEnumValueDescriptor_Type, enumvalue_descriptor);
+}
+
+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* 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* 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[] = {
+ { C("name"), (getter)GetName, NULL, "name", NULL},
+ { C("package"), (getter)GetPackage, NULL, "package", NULL},
+ { C("serialized_pb"), (getter)GetSerializedPb, NULL, NULL, NULL},
+ { C("message_types_by_name"), (getter)GetMessageTypesByName, NULL, "Messages by name", NULL},
+ { C("enum_types_by_name"), (getter)GetEnumTypesByName, NULL, "Enums by name", NULL},
+ { C("extensions_by_name"), (getter)GetExtensionsByName, NULL, "Extensions by name", NULL},
+ { C("dependencies"), (getter)GetDependencies, NULL, "Dependencies", NULL},
+ { C("public_dependencies"), (getter)GetPublicDependencies, NULL, "Dependencies", NULL},
+
+ { C("has_options"), (getter)GetHasOptions, (setter)SetHasOptions, "Has Options", NULL},
+ { C("_options"), (getter)NULL, (setter)SetOptions, "Options", NULL},
+ { C("syntax"), (getter)GetSyntax, (setter)NULL, "Syntax", NULL},
+ {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)
+ C("google.protobuf.internal."
+ "_message.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
+ C("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
+ PyType_GenericAlloc, // tp_alloc
+ PyType_GenericNew, // tp_new
+ PyObject_Del, // tp_free
+};
+
+PyObject* PyFileDescriptor_New(const FileDescriptor* file_descriptor) {
+ return descriptor::NewInternedDescriptor(
+ &PyFileDescriptor_Type, file_descriptor);
+}
+
+PyObject* PyFileDescriptor_NewWithPb(
+ const FileDescriptor* file_descriptor, PyObject *serialized_pb) {
+ PyObject* py_descriptor = PyFileDescriptor_New(file_descriptor);
+ if (py_descriptor == NULL) {
+ return NULL;
+ }
+ if (serialized_pb != NULL) {
+ PyFileDescriptor* cfile_descriptor =
+ reinterpret_cast<PyFileDescriptor*>(py_descriptor);
+ Py_XINCREF(serialized_pb);
+ cfile_descriptor->serialized_pb = serialized_pb;
+ }
+
+ return py_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_New(containing_type);
+ } else {
+ Py_RETURN_NONE;
+ }
+}
+
+static PyGetSetDef Getters[] = {
+ { C("name"), (getter)GetName, NULL, "Name", NULL},
+ { C("full_name"), (getter)GetFullName, NULL, "Full name", NULL},
+ { C("index"), (getter)GetIndex, NULL, "Index", NULL},
+
+ { C("containing_type"), (getter)GetContainingType, NULL, "Containing type", NULL},
+ { C("fields"), (getter)GetFields, NULL, "Fields", NULL},
+ {NULL}
+};
+
+} // namespace oneof_descriptor
+
+PyTypeObject PyOneofDescriptor_Type = {
+ PyVarObject_HEAD_INIT(&PyType_Type, 0)
+ C("google.protobuf.internal."
+ "_message.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
+ C("A Oneof Descriptor"), // 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
+ oneof_descriptor::Getters, // tp_getset
+ &descriptor::PyBaseDescriptor_Type, // tp_base
+};
+
+PyObject* PyOneofDescriptor_New(
+ const OneofDescriptor* oneof_descriptor) {
+ return descriptor::NewInternedDescriptor(
+ &PyOneofDescriptor_Type, oneof_descriptor);
+}
+
+// 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) < 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) < 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 (!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..ba6e7298
--- /dev/null
+++ b/python/google/protobuf/pyext/descriptor.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.
+
+// 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;
+
+// Return a new reference to a Descriptor object.
+// The C++ pointer is usually borrowed from the global DescriptorPool.
+// In any case, it must stay alive as long as the Python object.
+PyObject* PyMessageDescriptor_New(const Descriptor* descriptor);
+PyObject* PyFieldDescriptor_New(const FieldDescriptor* descriptor);
+PyObject* PyEnumDescriptor_New(const EnumDescriptor* descriptor);
+PyObject* PyEnumValueDescriptor_New(const EnumValueDescriptor* descriptor);
+PyObject* PyOneofDescriptor_New(const OneofDescriptor* descriptor);
+PyObject* PyFileDescriptor_New(const FileDescriptor* file_descriptor);
+
+// Alternate constructor of PyFileDescriptor, used when we already have a
+// serialized FileDescriptorProto that can be cached.
+// Returns a new reference.
+PyObject* PyFileDescriptor_NewWithPb(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);
+
+// 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..06edebf8
--- /dev/null
+++ b/python/google/protobuf/pyext/descriptor_containers.cc
@@ -0,0 +1,1564 @@
+// 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* (*GetByNumberMethod)(PyContainer* self, int index);
+typedef PyObject* (*NewObjectFromItemMethod)(const void* descriptor);
+typedef const string& (*GetItemNameMethod)(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 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 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_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_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_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_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, 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);
+ if (value2 == NULL) {
+ // Not found in the other dictionary
+ return 0;
+ }
+ int cmp = PyObject_RichCompareBool(value1, 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);
+}
+
+// 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
+};
+
+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
+ 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
+ (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* 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 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_New(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 = {
+ "MessageFields",
+ (CountMethod)Count,
+ (GetByIndexMethod)GetByIndex,
+ (GetByNameMethod)GetByName,
+ (GetByNumberMethod)GetByNumber,
+ (NewObjectFromItemMethod)NewObjectFromItem,
+ (GetItemNameMethod)GetItemName,
+ (GetItemNumberMethod)GetItemNumber,
+ (GetItemIndexMethod)GetItemIndex,
+};
+
+} // namespace fields
+
+PyObject* NewMessageFieldsByName(ParentDescriptor descriptor) {
+ return descriptor::NewMappingByName(&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_New(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,
+ (GetByNumberMethod)NULL,
+ (NewObjectFromItemMethod)NewObjectFromItem,
+ (GetItemNameMethod)GetItemName,
+ (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_New(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,
+ (GetByNumberMethod)NULL,
+ (NewObjectFromItemMethod)NewObjectFromItem,
+ (GetItemNameMethod)GetItemName,
+ (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_New(item);
+}
+
+static const string& GetItemName(ItemDescriptor item) {
+ return item->name();
+}
+
+static DescriptorContainerDef ContainerDef = {
+ "MessageEnumValues",
+ (CountMethod)Count,
+ (GetByIndexMethod)GetByIndex,
+ (GetByNameMethod)GetByName,
+ (GetByNumberMethod)NULL,
+ (NewObjectFromItemMethod)NewObjectFromItem,
+ (GetItemNameMethod)GetItemName,
+ (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_New(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,
+ (GetByNumberMethod)NULL,
+ (NewObjectFromItemMethod)NewObjectFromItem,
+ (GetItemNameMethod)GetItemName,
+ (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_New(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,
+ (GetByNumberMethod)NULL,
+ (NewObjectFromItemMethod)NewObjectFromItem,
+ (GetItemNameMethod)GetItemName,
+ (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_New(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,
+ (GetByNumberMethod)GetByNumber,
+ (NewObjectFromItemMethod)NewObjectFromItem,
+ (GetItemNameMethod)GetItemName,
+ (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_New(item);
+}
+
+static int GetItemIndex(ItemDescriptor item) {
+ return item->index_in_oneof();
+}
+
+static DescriptorContainerDef ContainerDef = {
+ "OneofFields",
+ (CountMethod)Count,
+ (GetByIndexMethod)GetByIndex,
+ (GetByNameMethod)NULL,
+ (GetByNumberMethod)NULL,
+ (NewObjectFromItemMethod)NewObjectFromItem,
+ (GetItemNameMethod)NULL,
+ (GetItemNumberMethod)NULL,
+ (GetItemIndexMethod)GetItemIndex,
+};
+
+} // namespace fields
+
+PyObject* NewOneofFieldsSeq(ParentDescriptor descriptor) {
+ return descriptor::NewSequence(&fields::ContainerDef, descriptor);
+}
+
+} // namespace oneof_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_New(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,
+ (GetByNumberMethod)NULL,
+ (NewObjectFromItemMethod)NewObjectFromItem,
+ (GetItemNameMethod)GetItemName,
+ (GetItemNumberMethod)NULL,
+ (GetItemIndexMethod)GetItemIndex,
+};
+
+} // namespace messages
+
+PyObject* NewFileMessageTypesByName(const FileDescriptor* 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_New(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,
+ (GetByNumberMethod)NULL,
+ (NewObjectFromItemMethod)NewObjectFromItem,
+ (GetItemNameMethod)GetItemName,
+ (GetItemNumberMethod)NULL,
+ (GetItemIndexMethod)GetItemIndex,
+};
+
+} // namespace enums
+
+PyObject* NewFileEnumTypesByName(const FileDescriptor* 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_New(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,
+ (GetByNumberMethod)NULL,
+ (NewObjectFromItemMethod)NewObjectFromItem,
+ (GetItemNameMethod)GetItemName,
+ (GetItemNumberMethod)NULL,
+ (GetItemIndexMethod)GetItemIndex,
+};
+
+} // namespace extensions
+
+PyObject* NewFileExtensionsByName(const FileDescriptor* descriptor) {
+ return descriptor::NewMappingByName(&extensions::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_New(item);
+}
+
+static DescriptorContainerDef ContainerDef = {
+ "FileDependencies",
+ (CountMethod)Count,
+ (GetByIndexMethod)GetByIndex,
+ (GetByNameMethod)NULL,
+ (GetByNumberMethod)NULL,
+ (NewObjectFromItemMethod)NewObjectFromItem,
+ (GetItemNameMethod)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_New(item);
+}
+
+static DescriptorContainerDef ContainerDef = {
+ "FilePublicDependencies",
+ (CountMethod)Count,
+ (GetByIndexMethod)GetByIndex,
+ (GetByNameMethod)NULL,
+ (GetByNumberMethod)NULL,
+ (NewObjectFromItemMethod)NewObjectFromItem,
+ (GetItemNameMethod)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..d81537de
--- /dev/null
+++ b/python/google/protobuf/pyext/descriptor_containers.h
@@ -0,0 +1,95 @@
+// 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.
+// 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;
+
+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* 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* NewFileDependencies(const FileDescriptor* descriptor);
+PyObject* NewFilePublicDependencies(const FileDescriptor* descriptor);
+} // namespace file_descriptor
+
+
+} // namespace python
+} // namespace protobuf
+} // namespace google
diff --git a/python/google/protobuf/pyext/descriptor_pool.cc b/python/google/protobuf/pyext/descriptor_pool.cc
new file mode 100644
index 00000000..bc3077bc
--- /dev/null
+++ b/python/google/protobuf/pyext/descriptor_pool.cc
@@ -0,0 +1,370 @@
+// 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/pyext/descriptor_pool.h>
+#include <google/protobuf/pyext/descriptor.h>
+#include <google/protobuf/pyext/scoped_pyobject_ptr.h>
+
+#define C(str) const_cast<char*>(str)
+
+#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 {
+
+namespace cdescriptor_pool {
+
+PyDescriptorPool* NewDescriptorPool() {
+ PyDescriptorPool* cdescriptor_pool = PyObject_New(
+ PyDescriptorPool, &PyDescriptorPool_Type);
+ if (cdescriptor_pool == NULL) {
+ return NULL;
+ }
+
+ // Build a DescriptorPool for messages only declared in Python libraries.
+ // generated_pool() contains all messages linked in C++ libraries, and is used
+ // as underlay.
+ cdescriptor_pool->pool = new DescriptorPool(DescriptorPool::generated_pool());
+
+ // TODO(amauryfa): Rewrite the SymbolDatabase in C so that it uses the same
+ // storage.
+ cdescriptor_pool->classes_by_descriptor =
+ new PyDescriptorPool::ClassesByMessageMap();
+ cdescriptor_pool->interned_descriptors =
+ new hash_map<const void*, PyObject *>();
+ cdescriptor_pool->descriptor_options =
+ new hash_map<const void*, PyObject *>();
+
+ return cdescriptor_pool;
+}
+
+static void Dealloc(PyDescriptorPool* self) {
+ typedef PyDescriptorPool::ClassesByMessageMap::iterator iterator;
+ for (iterator it = self->classes_by_descriptor->begin();
+ it != self->classes_by_descriptor->end(); ++it) {
+ Py_DECREF(it->second);
+ }
+ delete self->classes_by_descriptor;
+ delete self->interned_descriptors; // its references were borrowed.
+ 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;
+ 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_TypeError, "Couldn't find message %.200s", name);
+ return NULL;
+ }
+
+ return PyMessageDescriptor_New(message_descriptor);
+}
+
+// Add a message class to our database.
+const Descriptor* RegisterMessageClass(
+ PyDescriptorPool* self, PyObject *message_class, PyObject* descriptor) {
+ ScopedPyObjectPtr full_message_name(
+ PyObject_GetAttrString(descriptor, "full_name"));
+ Py_ssize_t name_size;
+ char* name;
+ if (PyString_AsStringAndSize(full_message_name, &name, &name_size) < 0) {
+ return NULL;
+ }
+ const Descriptor *message_descriptor =
+ self->pool->FindMessageTypeByName(string(name, name_size));
+ if (!message_descriptor) {
+ PyErr_Format(PyExc_TypeError, "Could not find C++ descriptor for '%s'",
+ name);
+ return NULL;
+ }
+ 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 message_descriptor;
+}
+
+// Retrieve the message class added to our database.
+PyObject *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* 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_TypeError, "Couldn't find field %.200s",
+ name);
+ return NULL;
+ }
+
+ return PyFieldDescriptor_New(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_TypeError, "Couldn't find field %.200s", name);
+ return NULL;
+ }
+
+ return PyFieldDescriptor_New(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_TypeError, "Couldn't find enum %.200s", name);
+ return NULL;
+ }
+
+ return PyEnumDescriptor_New(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_TypeError, "Couldn't find oneof %.200s", name);
+ return NULL;
+ }
+
+ return PyOneofDescriptor_New(oneof_descriptor);
+}
+
+static PyMethodDef Methods[] = {
+ { C("FindFieldByName"),
+ (PyCFunction)FindFieldByName,
+ METH_O,
+ C("Searches for a field descriptor by full name.") },
+ { C("FindExtensionByName"),
+ (PyCFunction)FindExtensionByName,
+ METH_O,
+ C("Searches for extension descriptor by full name.") },
+ {NULL}
+};
+
+} // namespace cdescriptor_pool
+
+PyTypeObject PyDescriptorPool_Type = {
+ PyVarObject_HEAD_INIT(&PyType_Type, 0)
+ C("google.protobuf.internal."
+ "_message.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
+ C("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
+ 0, // tp_new
+ PyObject_Del, // tp_free
+};
+
+// 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");
+ }
+ // 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* Python_BuildFile(PyObject* ignored, PyObject* serialized_pb) {
+ char* message_type;
+ Py_ssize_t message_len;
+
+ 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 =
+ DescriptorPool::generated_pool()->FindFileByName(file_proto.name());
+ if (generated_file != NULL) {
+ return PyFileDescriptor_NewWithPb(generated_file, serialized_pb);
+ }
+
+ BuildFileErrorCollector error_collector;
+ const FileDescriptor* descriptor =
+ GetDescriptorPool()->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_NewWithPb(descriptor, serialized_pb);
+}
+
+static PyDescriptorPool* global_cdescriptor_pool = NULL;
+
+bool InitDescriptorPool() {
+ if (PyType_Ready(&PyDescriptorPool_Type) < 0)
+ return false;
+
+ global_cdescriptor_pool = cdescriptor_pool::NewDescriptorPool();
+ if (global_cdescriptor_pool == NULL) {
+ return false;
+ }
+
+ return true;
+}
+
+PyDescriptorPool* GetDescriptorPool() {
+ return global_cdescriptor_pool;
+}
+
+} // 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..4e494b89
--- /dev/null
+++ b/python/google/protobuf/pyext/descriptor_pool.h
@@ -0,0 +1,152 @@
+// 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 {
+namespace python {
+
+// 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
+
+ DescriptorPool* pool;
+
+ // 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*, PyObject*> ClassesByMessageMap;
+ ClassesByMessageMap* classes_by_descriptor;
+
+ // 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;
+
+ // 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 {
+
+// Builds a new DescriptorPool. Normally called only once per process.
+PyDescriptorPool* NewDescriptorPool();
+
+// 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.
+// Returns the message Descriptor.
+// On error, returns NULL with a Python exception set.
+const Descriptor* RegisterMessageClass(
+ PyDescriptorPool* self, PyObject* message_class, PyObject* descriptor);
+
+// 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);
+
+// 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
+
+// Implement the Python "_BuildFile" method, it takes a serialized
+// FileDescriptorProto, and adds it to the C++ DescriptorPool.
+// It returns a new FileDescriptor object, or NULL when an exception is raised.
+PyObject* Python_BuildFile(PyObject* ignored, PyObject* args);
+
+// Retrieve the global descriptor pool owned by the _message module.
+PyDescriptorPool* GetDescriptorPool();
+
+// 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..8e38fc42
--- /dev/null
+++ b/python/google/protobuf/pyext/extension_dict.cc
@@ -0,0 +1,309 @@
+// 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/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 {
+
+// TODO(tibell): Always use self->message for clarity, just like in
+// RepeatedCompositeContainer.
+static Message* GetMessage(ExtensionDict* self) {
+ if (self->parent != NULL) {
+ return self->parent->message;
+ } else {
+ return self->message;
+ }
+}
+
+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(
+ GetMessage(self), 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->parent->message)) {
+ return NULL;
+ }
+
+ if (descriptor->label() != FieldDescriptor::LABEL_REPEATED &&
+ descriptor->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE) {
+ return cmessage::InternalGetScalar(self->parent, descriptor);
+ }
+
+ PyObject* value = PyDict_GetItem(self->values, key);
+ if (value != NULL) {
+ Py_INCREF(value);
+ return value;
+ }
+
+ 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) {
+ PyObject *message_class = cdescriptor_pool::GetMessageClass(
+ GetDescriptorPool(), 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->parent->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;
+ }
+ 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 (value != NULL) {
+ if (ReleaseExtension(self, value, descriptor) < 0) {
+ return NULL;
+ }
+ }
+ if (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;
+ }
+ PyObject* result = cmessage::HasFieldByDescriptor(self->parent, descriptor);
+ return result;
+}
+
+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, name);
+ 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."),
+ { NULL, NULL }
+};
+
+} // namespace extension_dict
+
+PyTypeObject ExtensionDict_Type = {
+ PyVarObject_HEAD_INIT(&PyType_Type, 0)
+ "google.protobuf.internal."
+ "cpp._message.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
+ 0, // 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..7e1049f1
--- /dev/null
+++ b/python/google/protobuf/pyext/extension_dict.h
@@ -0,0 +1,132 @@
+// 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;
+
+using internal::shared_ptr;
+
+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);
+
+// Checks if the dict has an extension.
+//
+// Returns a new python boolean reference.
+PyObject* HasExtension(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);
+
+} // namespace extension_dict
+} // namespace python
+} // namespace protobuf
+
+} // namespace google
+#endif // GOOGLE_PROTOBUF_PYTHON_CPP_EXTENSION_DICT_H__
diff --git a/python/google/protobuf/pyext/message.cc b/python/google/protobuf/pyext/message.cc
new file mode 100644
index 00000000..a2b357b2
--- /dev/null
+++ b/python/google/protobuf/pyext/message.cc
@@ -0,0 +1,2575 @@
+// 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 <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/io/coded_stream.h>
+#include <google/protobuf/descriptor.h>
+#include <google/protobuf/dynamic_message.h>
+#include <google/protobuf/message.h>
+#include <google/protobuf/text_format.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/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 {
+
+// Forward declarations
+namespace cmessage {
+static const FieldDescriptor* GetFieldDescriptor(
+ CMessage* self, PyObject* name);
+static const Descriptor* GetMessageDescriptor(PyTypeObject* cls);
+static string GetMessageName(CMessage* self);
+int InternalReleaseFieldByDescriptor(
+ const FieldDescriptor* field_descriptor,
+ PyObject* composite_field,
+ Message* parent_message);
+} // 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) {
+ 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 =
+ cmessage::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;
+}
+
+// ---------------------------------------------------------------------
+
+static DynamicMessageFactory* message_factory;
+
+// 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* EnumTypeWrapper_class;
+PyObject* EncodeError_class;
+PyObject* DecodeError_class;
+PyObject* PickleError_class;
+
+// Constant PyString values used for GetAttr/GetItem.
+static PyObject* kDESCRIPTOR;
+static PyObject* k_cdescriptor;
+static PyObject* kfull_name;
+static PyObject* k_extensions_by_name;
+static PyObject* k_extensions_by_number;
+
+/* 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
+ 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;
+}
+
+bool CheckAndSetString(
+ PyObject* arg, Message* message,
+ const FieldDescriptor* descriptor,
+ const Reflection* reflection,
+ bool append,
+ int index) {
+ 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 false;
+ }
+
+ if (PyBytes_Check(arg)) {
+ PyObject* unicode = PyUnicode_FromEncodedObject(arg, "utf-8", NULL);
+ if (unicode == NULL) {
+ 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 false;
+ } else {
+ Py_DECREF(unicode);
+ }
+ }
+ } else if (!PyBytes_Check(arg)) {
+ FormatTypeError(arg, "bytes");
+ return false;
+ }
+
+ 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);
+ }
+
+ if (encoded_string == NULL) {
+ return false;
+ }
+
+ char* value;
+ Py_ssize_t value_len;
+ if (PyBytes_AsStringAndSize(encoded_string, &value, &value_len) < 0) {
+ Py_DECREF(encoded_string);
+ 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);
+ }
+ Py_DECREF(encoded_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 {
+
+DynamicMessageFactory* GetMessageFactory() {
+ return 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(
+ existing_field, child_message, 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, message_factory);
+}
+
+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;
+ }
+
+ 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.
+ const Message* prototype = message_factory->GetPrototype(
+ self->message->GetDescriptor());
+ self->message = prototype->New();
+ self->owner.reset(self->message);
+ } else {
+ // Otherwise, we need a mutable child message.
+ if (AssureWritable(self->parent) == -1)
+ return -1;
+
+ // Make self->message writable.
+ Message* parent_message = self->parent->message;
+ 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
+ // three places such references occur: RepeatedScalarContainer,
+ // RepeatedCompositeContainer, and ExtensionDict.
+ if (self->extensions != NULL)
+ self->extensions->message = self->message;
+ if (ForEachCompositeField(self, FixupMessageReference(self->message)) == -1)
+ return -1;
+
+ return 0;
+}
+
+// --- Globals:
+
+// Retrieve the C++ Descriptor of a message class.
+// On error, returns NULL with an exception set.
+static const Descriptor* GetMessageDescriptor(PyTypeObject* cls) {
+ ScopedPyObjectPtr descriptor(PyObject_GetAttr(
+ reinterpret_cast<PyObject*>(cls), kDESCRIPTOR));
+ if (descriptor == NULL) {
+ PyErr_SetString(PyExc_TypeError, "Message class has no DESCRIPTOR");
+ return NULL;
+ }
+ if (!PyObject_TypeCheck(descriptor, &PyMessageDescriptor_Type)) {
+ PyErr_Format(PyExc_TypeError, "Expected a message Descriptor, got %s",
+ descriptor->ob_type->tp_name);
+ return NULL;
+ }
+ return PyMessageDescriptor_AsDescriptor(descriptor);
+}
+
+// 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 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(
+ Message* message,
+ const FieldDescriptor* field_descriptor,
+ PyObject* slice,
+ PyObject* cmessage_list) {
+ 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(
+ field_descriptor, message, 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 has no \"%s\" field.",
+ PyString_AsString(name));
+ return -1;
+ }
+ if (descriptor->label() == FieldDescriptor::LABEL_REPEATED) {
+ ScopedPyObjectPtr container(GetAttr(self, name));
+ if (container == NULL) {
+ return -1;
+ }
+ if (descriptor->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
+ if (repeated_composite_container::Extend(
+ reinterpret_cast<RepeatedCompositeContainer*>(container.get()),
+ value)
+ == NULL) {
+ return -1;
+ }
+ } else {
+ if (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;
+ }
+ if (MergeFrom(reinterpret_cast<CMessage*>(message.get()),
+ value) == NULL) {
+ return -1;
+ }
+ } else {
+ if (SetAttr(self, name, value) < 0) {
+ return -1;
+ }
+ }
+ }
+ return 0;
+}
+
+// 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* self = reinterpret_cast<CMessage*>(
+ PyType_GenericAlloc(reinterpret_cast<PyTypeObject*>(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;
+
+ // If there are extension_ranges, the message is "extendable". Allocate a
+ // dictionary to store the extension fields.
+ if (descriptor->extension_range_count() > 0) {
+ // TODO(amauryfa): Delay the construction of this dict until extensions are
+ // really used on the object.
+ ExtensionDict* extension_dict = extension_dict::NewExtensionDict(self);
+ if (extension_dict == NULL) {
+ return NULL;
+ }
+ self->extensions = extension_dict;
+ }
+
+ return self;
+}
+
+// The __new__ method of Message classes.
+// Creates a new C++ message and takes ownership.
+static PyObject* New(PyTypeObject* type,
+ PyObject* unused_args, PyObject* unused_kwargs) {
+ // Retrieve the message descriptor and the default instance (=prototype).
+ const Descriptor* message_descriptor = GetMessageDescriptor(type);
+ if (message_descriptor == NULL) {
+ return NULL;
+ }
+ const Message* default_message =
+ message_factory->GetPrototype(message_descriptor);
+ if (default_message == NULL) {
+ PyErr_SetString(PyExc_TypeError, message_descriptor->full_name().c_str());
+ return NULL;
+ }
+
+ CMessage* self = NewEmptyMessage(reinterpret_cast<PyObject*>(type),
+ message_descriptor);
+ 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 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()));
+
+ 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* arg) {
+ if (self->extensions != NULL) {
+ return extension_dict::ClearExtension(self->extensions, arg);
+ }
+ PyErr_SetString(PyExc_TypeError, "Message is not extendable");
+ return NULL;
+}
+
+PyObject* HasExtension(CMessage* self, PyObject* arg) {
+ if (self->extensions != NULL) {
+ return extension_dict::HasExtension(self->extensions, arg);
+ }
+ PyErr_SetString(PyExc_TypeError, "Message is not extendable");
+ return NULL;
+}
+
+// ---------------------------------------------------------------------
+// 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 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(Message* message,
+ const Descriptor* descriptor,
+ const FieldDescriptor* field_descriptor) {
+ Message* released_message = message->GetReflection()->ReleaseMessage(
+ 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(Message* message,
+ const FieldDescriptor* field_descriptor,
+ CMessage* child_cmessage) {
+ // Release the Message
+ shared_ptr<Message> released_message(ReleaseMessage(
+ message, 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(Message* parent_message) :
+ parent_message_(parent_message) {}
+
+ int VisitRepeatedCompositeContainer(RepeatedCompositeContainer* container) {
+ return repeated_composite_container::Release(
+ reinterpret_cast<RepeatedCompositeContainer*>(container));
+ }
+
+ int VisitRepeatedScalarContainer(RepeatedScalarContainer* container) {
+ return repeated_scalar_container::Release(
+ reinterpret_cast<RepeatedScalarContainer*>(container));
+ }
+
+ int VisitCMessage(CMessage* cmessage,
+ const FieldDescriptor* field_descriptor) {
+ return ReleaseSubMessage(parent_message_, field_descriptor,
+ reinterpret_cast<CMessage*>(cmessage));
+ }
+
+ Message* parent_message_;
+};
+
+int InternalReleaseFieldByDescriptor(
+ const FieldDescriptor* field_descriptor,
+ PyObject* composite_field,
+ Message* parent_message) {
+ return VisitCompositeField(
+ field_descriptor,
+ composite_field,
+ ReleaseChild(parent_message));
+}
+
+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(field_descriptor,
+ composite_field, message) < 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->message)) == -1)
+ return NULL;
+
+ // The old ExtensionDict still aliases this CMessage, but all its
+ // fields have been released.
+ if (self->extensions != NULL) {
+ Py_CLEAR(self->extensions);
+ ExtensionDict* extension_dict = extension_dict::NewExtensionDict(self);
+ if (extension_dict == NULL) {
+ return NULL;
+ }
+ self->extensions = extension_dict;
+ }
+ 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, "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));
+ 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:
+ PythonFieldValuePrinter() : float_holder_(PyFloat_FromDouble(0)) {}
+
+ // Python has some differences from C++ when printing floating point numbers.
+ //
+ // 1) Trailing .0 is always printed.
+ // 2) Outputted is rounded to 12 digits.
+ //
+ // 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 {
+ reinterpret_cast<PyFloatObject*>(float_holder_.get())->ob_fval = value;
+ ScopedPyObjectPtr s(PyObject_Str(float_holder_.get()));
+ if (s == NULL) return string();
+#if PY_MAJOR_VERSION < 3
+ char *cstr = PyBytes_AS_STRING(static_cast<PyObject*>(s));
+#else
+ char *cstr = PyUnicode_AsUTF8(s);
+#endif
+ return string(cstr);
+ }
+
+ private:
+ // Holder for a python float object which we use to allow us to use
+ // the Python API for printing doubles. We initialize once and then
+ // directly modify it for every float printed to save on allocations
+ // and refcounting.
+ ScopedPyObjectPtr float_holder_;
+};
+
+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(reinterpret_cast<PyObject *>(arg), &CMessage_Type)) {
+ PyErr_SetString(PyExc_TypeError, "Must be a message");
+ return NULL;
+ }
+
+ other_message = reinterpret_cast<CMessage*>(arg);
+ if (other_message->message->GetDescriptor() !=
+ self->message->GetDescriptor()) {
+ PyErr_Format(PyExc_TypeError,
+ "Tried to merge from a message with a different type. "
+ "to: %s, from: %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(reinterpret_cast<PyObject *>(arg), &CMessage_Type)) {
+ PyErr_SetString(PyExc_TypeError, "Must be a message");
+ 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,
+ "Tried to copy from a message with a different type. "
+ "to: %s, from: %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.
+ Clear(self);
+
+ self->message->CopyFrom(*other_message->message);
+
+ Py_RETURN_NONE;
+}
+
+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);
+ input.SetExtensionRegistry(GetDescriptorPool()->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 (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) {
+ ScopedPyObjectPtr message_descriptor(PyObject_GetAttr(cls, kDESCRIPTOR));
+ if (message_descriptor == NULL) {
+ return NULL;
+ }
+
+ const FieldDescriptor* descriptor =
+ GetExtensionDescriptor(extension_handle);
+ if (descriptor == NULL) {
+ return NULL;
+ }
+ const Descriptor* cmessage_descriptor = GetMessageDescriptor(
+ reinterpret_cast<PyTypeObject*>(cls));
+
+ if (cmessage_descriptor != descriptor->containing_type()) {
+ if (PyObject_SetAttrString(extension_handle, "containing_type",
+ message_descriptor) < 0) {
+ 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, full_name);
+ 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, full_name, 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 (PyDict_SetItem(extensions_by_number, number, 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->message_type() == descriptor->extension_scope() &&
+ 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, message_name, 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* 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 (Py_ssize_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_New(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(
+ GetDescriptorPool(), fields[i]->message_type()) == NULL) {
+ PyErr_Clear();
+ continue;
+ }
+ PyObject* extensions = reinterpret_cast<PyObject*>(self->extensions);
+ if (extensions == NULL) {
+ return NULL;
+ }
+ // 'extension' reference later stolen by PyTuple_SET_ITEM.
+ PyObject* extension = PyObject_GetItem(extensions, extension_field);
+ 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 {
+ 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_New(fields[i]));
+ if (field_descriptor == NULL) {
+ return NULL;
+ }
+
+ PyObject* field_value = GetAttr(self, py_field_name);
+ if (field_value == NULL) {
+ PyErr_SetObject(PyExc_ValueError, py_field_name);
+ 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;
+ }
+ Py_SIZE(all_fields.get()) = actual_size;
+ return all_fields.release();
+}
+
+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 (Py_ssize_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) {
+ if (!PyObject_TypeCheck(other, &CMessage_Type)) {
+ if (opid == Py_EQ) {
+ Py_RETURN_FALSE;
+ } else if (opid == Py_NE) {
+ Py_RETURN_TRUE;
+ }
+ }
+ if (opid == Py_EQ || opid == Py_NE) {
+ ScopedPyObjectPtr self_fields(ListFields(self));
+ if (!self_fields) {
+ return NULL;
+ }
+ ScopedPyObjectPtr other_fields(ListFields(
+ reinterpret_cast<CMessage*>(other)));
+ if (!other_fields) {
+ return NULL;
+ }
+ return PyObject_RichCompare(self_fields, other_fields, opid);
+ } else {
+ Py_INCREF(Py_NotImplemented);
+ return Py_NotImplemented;
+ }
+}
+
+PyObject* InternalGetScalar(
+ CMessage* self,
+ const FieldDescriptor* field_descriptor) {
+ Message* message = self->message;
+ 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.
+ UnknownFieldSet* unknown_field_set =
+ message->GetReflection()->MutableUnknownFields(message);
+ for (int i = 0; i < unknown_field_set->field_count(); ++i) {
+ if (unknown_field_set->field(i).number() ==
+ field_descriptor->number()) {
+ 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();
+ const Message& sub_message = reflection->GetMessage(
+ *self->message, field_descriptor, message_factory);
+
+ PyObject *message_class = cdescriptor_pool::GetMessageClass(
+ GetDescriptorPool(), field_descriptor->message_type());
+ if (message_class == NULL) {
+ return NULL;
+ }
+
+ CMessage* cmsg = cmessage::NewEmptyMessage(message_class,
+ sub_message.GetDescriptor());
+ 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 InternalSetScalar(
+ CMessage* self,
+ const FieldDescriptor* field_descriptor,
+ PyObject* arg) {
+ Message* message = self->message;
+ const Reflection* reflection = message->GetReflection();
+
+ if (!CheckFieldBelongsToMessage(field_descriptor, message)) {
+ return -1;
+ }
+
+ if (MaybeReleaseOverlappingOneofField(self, field_descriptor) < 0) {
+ 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;
+}
+
+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;
+}
+
+// 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, number) == -1) {
+ return false;
+ }
+ return true;
+}
+
+
+// Finalize the creation of the Message class.
+// Called from its metaclass: GeneratedProtocolMessageType.__init__().
+static PyObject* AddDescriptors(PyObject* cls, PyObject* descriptor) {
+ const Descriptor* message_descriptor =
+ cdescriptor_pool::RegisterMessageClass(
+ GetDescriptorPool(), cls, descriptor);
+ if (message_descriptor == NULL) {
+ return NULL;
+ }
+
+ // If there are extension_ranges, the message is "extendable", and extension
+ // classes will register themselves in this class.
+ if (message_descriptor->extension_range_count() > 0) {
+ ScopedPyObjectPtr by_name(PyDict_New());
+ if (PyObject_SetAttr(cls, k_extensions_by_name, by_name) < 0) {
+ return NULL;
+ }
+ ScopedPyObjectPtr by_number(PyDict_New());
+ if (PyObject_SetAttr(cls, k_extensions_by_number, by_number) < 0) {
+ return NULL;
+ }
+ }
+
+ // For each field set: cls.<field>_FIELD_NUMBER = <number>
+ for (int i = 0; i < message_descriptor->field_count(); ++i) {
+ if (!AddFieldNumberToClass(cls, message_descriptor->field(i))) {
+ return NULL;
+ }
+ }
+
+ // 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 < message_descriptor->enum_type_count(); ++i) {
+ const EnumDescriptor* enum_descriptor = message_descriptor->enum_type(i);
+ ScopedPyObjectPtr enum_type(PyEnumDescriptor_New(enum_descriptor));
+ if (enum_type == NULL) {
+ return NULL;
+ }
+ // Add wrapped enum type to message class.
+ ScopedPyObjectPtr wrapped(PyObject_CallFunctionObjArgs(
+ EnumTypeWrapper_class, enum_type.get(), NULL));
+ if (wrapped == NULL) {
+ return NULL;
+ }
+ if (PyObject_SetAttrString(
+ cls, enum_descriptor->name().c_str(), wrapped) == -1) {
+ return NULL;
+ }
+
+ // 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 NULL;
+ }
+ if (PyObject_SetAttrString(
+ cls, enum_value_descriptor->name().c_str(), value_number) == -1) {
+ return NULL;
+ }
+ }
+ }
+
+ // 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 < message_descriptor->extension_count(); ++i) {
+ const google::protobuf::FieldDescriptor* field = message_descriptor->extension(i);
+ ScopedPyObjectPtr extension_field(PyFieldDescriptor_New(field));
+ if (extension_field == NULL) {
+ return NULL;
+ }
+
+ // Add the extension field to the message class.
+ if (PyObject_SetAttrString(
+ cls, field->name().c_str(), extension_field) == -1) {
+ return NULL;
+ }
+
+ // For each extension set cls.<extension name>_FIELD_NUMBER = <number>.
+ if (!AddFieldNumberToClass(cls, field)) {
+ return NULL;
+ }
+ }
+
+ Py_RETURN_NONE;
+}
+
+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 (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, method_name,
+ self, Py_True, NULL));
+ Py_DECREF(Py_True);
+ if (encoded == NULL) {
+ return NULL;
+ }
+#if PY_MAJOR_VERSION < 3
+ PyObject* decoded = PyString_AsDecodedObject(encoded, "utf-8", NULL);
+#else
+ PyObject* decoded = PyUnicode_FromEncodedObject(encoded, "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, "serialized", serialized) < 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 (ParseFromString(self, serialized) == NULL) {
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
+// CMessage static methods:
+PyObject* _GetMessageDescriptor(PyObject* unused, PyObject* arg) {
+ return cdescriptor_pool::FindMessageByName(GetDescriptorPool(), arg);
+}
+
+PyObject* _GetFieldDescriptor(PyObject* unused, PyObject* arg) {
+ return cdescriptor_pool::FindFieldByName(GetDescriptorPool(), arg);
+}
+
+PyObject* _GetExtensionDescriptor(PyObject* unused, PyObject* arg) {
+ return cdescriptor_pool::FindExtensionByName(GetDescriptorPool(), arg);
+}
+
+PyObject* _GetEnumDescriptor(PyObject* unused, PyObject* arg) {
+ return cdescriptor_pool::FindEnumTypeByName(GetDescriptorPool(), arg);
+}
+
+PyObject* _GetOneofDescriptor(PyObject* unused, PyObject* arg) {
+ return cdescriptor_pool::FindOneofByName(GetDescriptorPool(), arg);
+}
+
+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 PyMemberDef Members[] = {
+ {"Extensions", T_OBJECT_EX, offsetof(CMessage, extensions), 0,
+ "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." },
+ { "AddDescriptors", (PyCFunction)AddDescriptors, METH_O | METH_CLASS,
+ "Adds field descriptors to the class" },
+ { "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." },
+ { "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.
+ { "_BuildFile", (PyCFunction)Python_BuildFile, METH_O | METH_STATIC,
+ "Registers a new protocol buffer file in the global C++ descriptor pool." },
+ { "_GetMessageDescriptor", (PyCFunction)_GetMessageDescriptor,
+ METH_O | METH_STATIC, "Finds a message descriptor in the message pool." },
+ { "_GetFieldDescriptor", (PyCFunction)_GetFieldDescriptor,
+ METH_O | METH_STATIC, "Finds a field descriptor in the message pool." },
+ { "_GetExtensionDescriptor", (PyCFunction)_GetExtensionDescriptor,
+ METH_O | METH_STATIC,
+ "Finds a extension descriptor in the message pool." },
+ { "_GetEnumDescriptor", (PyCFunction)_GetEnumDescriptor,
+ METH_O | METH_STATIC,
+ "Finds an enum descriptor in the message pool." },
+ { "_GetOneofDescriptor", (PyCFunction)_GetOneofDescriptor,
+ METH_O | METH_STATIC,
+ "Finds an oneof descriptor in the message pool." },
+ { "_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->label() == FieldDescriptor::LABEL_REPEATED) {
+ PyObject* py_container = NULL;
+ if (field_descriptor->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
+ PyObject *message_class = cdescriptor_pool::GetMessageClass(
+ GetDescriptorPool(), 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 (!SetCompositeField(self, name, sub_message)) {
+ Py_DECREF(sub_message);
+ return NULL;
+ }
+ return sub_message;
+ }
+
+ return InternalGetScalar(self, 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");
+ return -1;
+}
+
+} // namespace cmessage
+
+PyTypeObject CMessage_Type = {
+ PyVarObject_HEAD_INIT(&PyType_Type, 0)
+ // Keep the fully qualified _message symbol in a line for opensource.
+ "google.protobuf.pyext._message."
+ "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
+ 0, // tp_repr
+ 0, // tp_as_number
+ 0, // tp_as_sequence
+ 0, // tp_as_mapping
+ 0, // 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
+ cmessage::Members, // tp_members
+ 0, // 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");
+
+ message_factory = new DynamicMessageFactory(GetDescriptorPool()->pool);
+ message_factory->SetDelegateToGeneratedFactory(true);
+}
+
+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();
+
+ CMessage_Type.tp_hash = PyObject_HashNotImplemented;
+ 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));
+ if (immutable_dict == NULL) {
+ return false;
+ }
+ if (PyDict_SetItem(CMessage_Type.tp_dict,
+ k_extensions_by_name, immutable_dict) < 0) {
+ return false;
+ }
+ if (PyDict_SetItem(CMessage_Type.tp_dict,
+ k_extensions_by_number, immutable_dict) < 0) {
+ return false;
+ }
+
+ PyModule_AddObject(m, "Message", reinterpret_cast<PyObject*>(&CMessage_Type));
+
+ RepeatedScalarContainer_Type.tp_hash =
+ PyObject_HashNotImplemented;
+ if (PyType_Ready(&RepeatedScalarContainer_Type) < 0) {
+ return false;
+ }
+
+ PyModule_AddObject(m, "RepeatedScalarContainer",
+ reinterpret_cast<PyObject*>(
+ &RepeatedScalarContainer_Type));
+
+ RepeatedCompositeContainer_Type.tp_hash = PyObject_HashNotImplemented;
+ if (PyType_Ready(&RepeatedCompositeContainer_Type) < 0) {
+ return false;
+ }
+
+ PyModule_AddObject(
+ m, "RepeatedCompositeContainer",
+ reinterpret_cast<PyObject*>(
+ &RepeatedCompositeContainer_Type));
+
+ ExtensionDict_Type.tp_hash = PyObject_HashNotImplemented;
+ if (PyType_Ready(&ExtensionDict_Type) < 0) {
+ return false;
+ }
+
+ PyModule_AddObject(
+ m, "ExtensionDict",
+ reinterpret_cast<PyObject*>(&ExtensionDict_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));
+
+ 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");
+ 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
+
+
+#if PY_MAJOR_VERSION >= 3
+static struct PyModuleDef _module = {
+ PyModuleDef_HEAD_INIT,
+ "_message",
+ google::protobuf::python::module_docstring,
+ -1,
+ NULL,
+ 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", NULL, google::protobuf::python::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
+ }
+}
+} // namespace google
diff --git a/python/google/protobuf/pyext/message.h b/python/google/protobuf/pyext/message.h
new file mode 100644
index 00000000..2f2da795
--- /dev/null
+++ b/python/google/protobuf/pyext/message.h
@@ -0,0 +1,310 @@
+// 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 DynamicMessageFactory;
+
+using internal::shared_ptr;
+
+namespace python {
+
+struct ExtensionDict;
+
+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;
+
+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);
+
+// 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(Message* message,
+ 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(Message* message,
+ 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);
+
+// Retrieves the specified scalar value from the message.
+//
+// Returns a new python reference.
+PyObject* InternalGetScalar(CMessage* self,
+ 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);
+
+DynamicMessageFactory* GetMessageFactory();
+
+} // 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 C(str) const_cast<char*>(str)
+
+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);
+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;
+
+} // namespace python
+} // namespace protobuf
+
+} // namespace google
+#endif // GOOGLE_PROTOBUF_PYTHON_CPP_MESSAGE_H__
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-proto2.cc b/python/google/protobuf/pyext/python-proto2.cc
deleted file mode 100644
index f2799e62..00000000
--- a/python/google/protobuf/pyext/python-proto2.cc
+++ /dev/null
@@ -1,1658 +0,0 @@
-// Protocol Buffers - Google's data interchange format
-// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
-//
-// 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 <map>
-#include <string>
-#include <vector>
-
-#include <google/protobuf/stubs/common.h>
-#include <google/protobuf/pyext/python_descriptor.h>
-#include <google/protobuf/io/coded_stream.h>
-#include <google/protobuf/descriptor.h>
-#include <google/protobuf/dynamic_message.h>
-#include <google/protobuf/message.h>
-#include <google/protobuf/unknown_field_set.h>
-#include <google/protobuf/pyext/python_protobuf.h>
-
-/* Is 64bit */
-#define IS_64BIT (SIZEOF_LONG == 8)
-
-#define FIELD_BELONGS_TO_MESSAGE(field_descriptor, message) \
- ((message)->GetDescriptor() == (field_descriptor)->containing_type())
-
-#define FIELD_IS_REPEATED(field_descriptor) \
- ((field_descriptor)->label() == google::protobuf::FieldDescriptor::LABEL_REPEATED)
-
-#define GOOGLE_CHECK_GET_INT32(arg, value) \
- int32 value; \
- if (!CheckAndGetInteger(arg, &value, kint32min_py, kint32max_py)) { \
- return NULL; \
- }
-
-#define GOOGLE_CHECK_GET_INT64(arg, value) \
- int64 value; \
- if (!CheckAndGetInteger(arg, &value, kint64min_py, kint64max_py)) { \
- return NULL; \
- }
-
-#define GOOGLE_CHECK_GET_UINT32(arg, value) \
- uint32 value; \
- if (!CheckAndGetInteger(arg, &value, kPythonZero, kuint32max_py)) { \
- return NULL; \
- }
-
-#define GOOGLE_CHECK_GET_UINT64(arg, value) \
- uint64 value; \
- if (!CheckAndGetInteger(arg, &value, kPythonZero, kuint64max_py)) { \
- return NULL; \
- }
-
-#define GOOGLE_CHECK_GET_FLOAT(arg, value) \
- float value; \
- if (!CheckAndGetFloat(arg, &value)) { \
- return NULL; \
- } \
-
-#define GOOGLE_CHECK_GET_DOUBLE(arg, value) \
- double value; \
- if (!CheckAndGetDouble(arg, &value)) { \
- return NULL; \
- }
-
-#define GOOGLE_CHECK_GET_BOOL(arg, value) \
- bool value; \
- if (!CheckAndGetBool(arg, &value)) { \
- return NULL; \
- }
-
-#define C(str) const_cast<char*>(str)
-
-// --- Globals:
-
-// Constants used for integer type range checking.
-static PyObject* kPythonZero;
-static PyObject* kint32min_py;
-static PyObject* kint32max_py;
-static PyObject* kuint32max_py;
-static PyObject* kint64min_py;
-static PyObject* kint64max_py;
-static PyObject* kuint64max_py;
-
-namespace google {
-namespace protobuf {
-namespace python {
-
-// --- Support Routines:
-
-static void AddConstants(PyObject* module) {
- struct NameValue {
- char* name;
- int32 value;
- } constants[] = {
- // Labels:
- {"LABEL_OPTIONAL", google::protobuf::FieldDescriptor::LABEL_OPTIONAL},
- {"LABEL_REQUIRED", google::protobuf::FieldDescriptor::LABEL_REQUIRED},
- {"LABEL_REPEATED", google::protobuf::FieldDescriptor::LABEL_REPEATED},
- // CPP types:
- {"CPPTYPE_MESSAGE", google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE},
- // Field Types:
- {"TYPE_MESSAGE", google::protobuf::FieldDescriptor::TYPE_MESSAGE},
- // End.
- {NULL, 0}
- };
-
- for (NameValue* constant = constants;
- constant->name != NULL; constant++) {
- PyModule_AddIntConstant(module, constant->name, constant->value);
- }
-}
-
-// --- CMessage Custom Type:
-
-// ------ Type Forward Declaration:
-
-struct CMessage;
-struct CMessage_Type;
-
-static void CMessageDealloc(CMessage* self);
-static int CMessageInit(CMessage* self, PyObject *args, PyObject *kwds);
-static PyObject* CMessageStr(CMessage* self);
-
-static PyObject* CMessage_AddMessage(CMessage* self, PyObject* args);
-static PyObject* CMessage_AddRepeatedScalar(CMessage* self, PyObject* args);
-static PyObject* CMessage_AssignRepeatedScalar(CMessage* self, PyObject* args);
-static PyObject* CMessage_ByteSize(CMessage* self, PyObject* args);
-static PyObject* CMessage_Clear(CMessage* self, PyObject* args);
-static PyObject* CMessage_ClearField(CMessage* self, PyObject* args);
-static PyObject* CMessage_ClearFieldByDescriptor(
- CMessage* self, PyObject* args);
-static PyObject* CMessage_CopyFrom(CMessage* self, PyObject* args);
-static PyObject* CMessage_DebugString(CMessage* self, PyObject* args);
-static PyObject* CMessage_DeleteRepeatedField(CMessage* self, PyObject* args);
-static PyObject* CMessage_Equals(CMessage* self, PyObject* args);
-static PyObject* CMessage_FieldLength(CMessage* self, PyObject* args);
-static PyObject* CMessage_FindInitializationErrors(CMessage* self);
-static PyObject* CMessage_GetRepeatedMessage(CMessage* self, PyObject* args);
-static PyObject* CMessage_GetRepeatedScalar(CMessage* self, PyObject* args);
-static PyObject* CMessage_GetScalar(CMessage* self, PyObject* args);
-static PyObject* CMessage_HasField(CMessage* self, PyObject* args);
-static PyObject* CMessage_HasFieldByDescriptor(CMessage* self, PyObject* args);
-static PyObject* CMessage_IsInitialized(CMessage* self, PyObject* args);
-static PyObject* CMessage_ListFields(CMessage* self, PyObject* args);
-static PyObject* CMessage_MergeFrom(CMessage* self, PyObject* args);
-static PyObject* CMessage_MergeFromString(CMessage* self, PyObject* args);
-static PyObject* CMessage_MutableMessage(CMessage* self, PyObject* args);
-static PyObject* CMessage_NewSubMessage(CMessage* self, PyObject* args);
-static PyObject* CMessage_SetScalar(CMessage* self, PyObject* args);
-static PyObject* CMessage_SerializePartialToString(
- CMessage* self, PyObject* args);
-static PyObject* CMessage_SerializeToString(CMessage* self, PyObject* args);
-static PyObject* CMessage_SetInParent(CMessage* self, PyObject* args);
-static PyObject* CMessage_SwapRepeatedFieldElements(
- CMessage* self, PyObject* args);
-
-// ------ Object Definition:
-
-typedef struct CMessage {
- PyObject_HEAD
-
- struct CMessage* parent; // NULL if wasn't created from another message.
- CFieldDescriptor* parent_field;
- const char* full_name;
- google::protobuf::Message* message;
- bool free_message;
- bool read_only;
-} CMessage;
-
-// ------ Method Table:
-
-#define CMETHOD(name, args, doc) \
- { C(#name), (PyCFunction)CMessage_##name, args, C(doc) }
-static PyMethodDef CMessageMethods[] = {
- CMETHOD(AddMessage, METH_O,
- "Adds a new message to a repeated composite field."),
- CMETHOD(AddRepeatedScalar, METH_VARARGS,
- "Adds a scalar to a repeated scalar field."),
- CMETHOD(AssignRepeatedScalar, METH_VARARGS,
- "Clears and sets the values of a repeated scalar field."),
- CMETHOD(ByteSize, METH_NOARGS,
- "Returns the size of the message in bytes."),
- CMETHOD(Clear, METH_NOARGS,
- "Clears a protocol message."),
- CMETHOD(ClearField, METH_O,
- "Clears a protocol message field by name."),
- CMETHOD(ClearFieldByDescriptor, METH_O,
- "Clears a protocol message field by descriptor."),
- CMETHOD(CopyFrom, METH_O,
- "Copies a protocol message into the current message."),
- CMETHOD(DebugString, METH_NOARGS,
- "Returns the debug string of a protocol message."),
- CMETHOD(DeleteRepeatedField, METH_VARARGS,
- "Deletes a slice of values from a repeated field."),
- CMETHOD(Equals, METH_O,
- "Checks if two protocol messages are equal (by identity)."),
- CMETHOD(FieldLength, METH_O,
- "Returns the number of elements in a repeated field."),
- CMETHOD(FindInitializationErrors, METH_NOARGS,
- "Returns the initialization errors of a message."),
- CMETHOD(GetRepeatedMessage, METH_VARARGS,
- "Returns a message from a repeated composite field."),
- CMETHOD(GetRepeatedScalar, METH_VARARGS,
- "Returns a scalar value from a repeated scalar field."),
- CMETHOD(GetScalar, METH_O,
- "Returns the scalar value of a field."),
- CMETHOD(HasField, METH_O,
- "Checks if a message field is set."),
- CMETHOD(HasFieldByDescriptor, METH_O,
- "Checks if a message field is set by given its descriptor"),
- CMETHOD(IsInitialized, METH_NOARGS,
- "Checks if all required fields of a protocol message are set."),
- CMETHOD(ListFields, METH_NOARGS,
- "Lists all set fields of a message."),
- CMETHOD(MergeFrom, METH_O,
- "Merges a protocol message into the current message."),
- CMETHOD(MergeFromString, METH_O,
- "Merges a serialized message into the current message."),
- CMETHOD(MutableMessage, METH_O,
- "Returns a new instance of a nested protocol message."),
- CMETHOD(NewSubMessage, METH_O,
- "Creates and returns a python message given the descriptor of a "
- "composite field of the current message."),
- CMETHOD(SetScalar, METH_VARARGS,
- "Sets the value of a singular scalar field."),
- CMETHOD(SerializePartialToString, METH_VARARGS,
- "Serializes the message to a string, even if it isn't initialized."),
- CMETHOD(SerializeToString, METH_NOARGS,
- "Serializes the message to a string, only for initialized messages."),
- CMETHOD(SetInParent, METH_NOARGS,
- "Sets the has bit of the given field in its parent message."),
- CMETHOD(SwapRepeatedFieldElements, METH_VARARGS,
- "Swaps the elements in two positions in a repeated field."),
- { NULL, NULL }
-};
-#undef CMETHOD
-
-static PyMemberDef CMessageMembers[] = {
- { C("full_name"), T_STRING, offsetof(CMessage, full_name), 0, "Full name" },
- { NULL }
-};
-
-// ------ Type Definition:
-
-// The definition for the type object that captures the type of CMessage
-// in Python.
-PyTypeObject CMessage_Type = {
- PyObject_HEAD_INIT(&PyType_Type)
- 0,
- C("google3.net.google.protobuf.python.internal."
- "_net_proto2___python."
- "CMessage"), // tp_name
- sizeof(CMessage), // tp_basicsize
- 0, // tp_itemsize
- (destructor)CMessageDealloc, // 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
- (reprfunc)CMessageStr, // tp_str
- 0, // tp_getattro
- 0, // tp_setattro
- 0, // tp_as_buffer
- Py_TPFLAGS_DEFAULT, // tp_flags
- C("A ProtocolMessage"), // tp_doc
- 0, // tp_traverse
- 0, // tp_clear
- 0, // tp_richcompare
- 0, // tp_weaklistoffset
- 0, // tp_iter
- 0, // tp_iternext
- CMessageMethods, // tp_methods
- CMessageMembers, // tp_members
- 0, // tp_getset
- 0, // tp_base
- 0, // tp_dict
- 0, // tp_descr_get
- 0, // tp_descr_set
- 0, // tp_dictoffset
- (initproc)CMessageInit, // tp_init
- PyType_GenericAlloc, // tp_alloc
- PyType_GenericNew, // tp_new
- PyObject_Del, // tp_free
-};
-
-// ------ Helper Functions:
-
-static void FormatTypeError(PyObject* arg, char* expected_types) {
- PyObject* s = PyObject_Str(PyObject_Type(arg));
- PyObject* repr = PyObject_Repr(PyObject_Type(arg));
- PyErr_Format(PyExc_TypeError,
- "%.100s has type %.100s, but expected one of: %s",
- PyString_AS_STRING(repr),
- PyString_AS_STRING(s),
- expected_types);
- Py_DECREF(s);
- Py_DECREF(repr);
-}
-
-template <class T>
-static bool CheckAndGetInteger(
- PyObject* arg, T* value, PyObject* min, PyObject* max) {
- bool is_long = PyLong_Check(arg);
- if (!PyInt_Check(arg) && !is_long) {
- FormatTypeError(arg, "int, long");
- return false;
- }
-
- if (PyObject_Compare(min, arg) > 0 || PyObject_Compare(max, arg) < 0) {
- PyObject* s = PyObject_Str(arg);
- PyErr_Format(PyExc_ValueError,
- "Value out of range: %s",
- PyString_AS_STRING(s));
- Py_DECREF(s);
- return false;
- }
- if (is_long) {
- if (min == kPythonZero) {
- *value = static_cast<T>(PyLong_AsUnsignedLongLong(arg));
- } else {
- *value = static_cast<T>(PyLong_AsLongLong(arg));
- }
- } else {
- *value = static_cast<T>(PyInt_AsLong(arg));
- }
- return true;
-}
-
-static 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;
-}
-
-static bool CheckAndGetFloat(PyObject* arg, float* value) {
- double double_value;
- if (!CheckAndGetDouble(arg, &double_value)) {
- return false;
- }
- *value = static_cast<float>(double_value);
- return true;
-}
-
-static 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;
-}
-
-google::protobuf::DynamicMessageFactory* global_message_factory = NULL;
-static const google::protobuf::Message* CreateMessage(const char* message_type) {
- string message_name(message_type);
- const google::protobuf::Descriptor* descriptor =
- GetDescriptorPool()->FindMessageTypeByName(message_name);
- if (descriptor == NULL) {
- return NULL;
- }
- return global_message_factory->GetPrototype(descriptor);
-}
-
-static bool CheckAndSetString(
- PyObject* arg, google::protobuf::Message* message,
- const google::protobuf::FieldDescriptor* descriptor,
- const google::protobuf::Reflection* reflection,
- bool append,
- int index) {
- GOOGLE_DCHECK(descriptor->type() == google::protobuf::FieldDescriptor::TYPE_STRING ||
- descriptor->type() == google::protobuf::FieldDescriptor::TYPE_BYTES);
- if (descriptor->type() == google::protobuf::FieldDescriptor::TYPE_STRING) {
- if (!PyString_Check(arg) && !PyUnicode_Check(arg)) {
- FormatTypeError(arg, "str, unicode");
- return false;
- }
-
- if (PyString_Check(arg)) {
- PyObject* unicode = PyUnicode_FromEncodedObject(arg, "ascii", NULL);
- if (unicode == NULL) {
- PyObject* repr = PyObject_Repr(arg);
- PyErr_Format(PyExc_ValueError,
- "%s has type str, but isn't in 7-bit ASCII "
- "encoding. Non-ASCII strings must be converted to "
- "unicode objects before being added.",
- PyString_AS_STRING(repr));
- Py_DECREF(repr);
- return false;
- } else {
- Py_DECREF(unicode);
- }
- }
- } else if (!PyString_Check(arg)) {
- FormatTypeError(arg, "str");
- return false;
- }
-
- PyObject* encoded_string = NULL;
- if (descriptor->type() == google::protobuf::FieldDescriptor::TYPE_STRING) {
- if (PyString_Check(arg)) {
- encoded_string = PyString_AsEncodedObject(arg, "utf-8", NULL);
- } else {
- encoded_string = PyUnicode_AsEncodedObject(arg, "utf-8", NULL);
- }
- } else {
- // In this case field type is "bytes".
- encoded_string = arg;
- Py_INCREF(encoded_string);
- }
-
- if (encoded_string == NULL) {
- return false;
- }
-
- char* value;
- Py_ssize_t value_len;
- if (PyString_AsStringAndSize(encoded_string, &value, &value_len) < 0) {
- Py_DECREF(encoded_string);
- 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);
- }
- Py_DECREF(encoded_string);
- return true;
-}
-
-static PyObject* ToStringObject(
- const google::protobuf::FieldDescriptor* descriptor, string value) {
- if (descriptor->type() != google::protobuf::FieldDescriptor::TYPE_STRING) {
- return PyString_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 = PyString_FromStringAndSize(value.c_str(), value.length());
- }
- return result;
-}
-
-static void AssureWritable(CMessage* self) {
- if (self == NULL ||
- self->parent == NULL ||
- self->parent_field == NULL) {
- return;
- }
-
- if (!self->read_only) {
- return;
- }
-
- AssureWritable(self->parent);
-
- google::protobuf::Message* message = self->parent->message;
- const google::protobuf::Reflection* reflection = message->GetReflection();
- self->message = reflection->MutableMessage(
- message, self->parent_field->descriptor, global_message_factory);
- self->read_only = false;
- self->parent = NULL;
- self->parent_field = NULL;
-}
-
-static PyObject* InternalGetScalar(
- google::protobuf::Message* message,
- const google::protobuf::FieldDescriptor* field_descriptor) {
- const google::protobuf::Reflection* reflection = message->GetReflection();
-
- if (!FIELD_BELONGS_TO_MESSAGE(field_descriptor, message)) {
- PyErr_SetString(
- PyExc_KeyError, "Field does not belong to message!");
- return NULL;
- }
-
- PyObject* result = NULL;
- switch (field_descriptor->cpp_type()) {
- case google::protobuf::FieldDescriptor::CPPTYPE_INT32: {
- int32 value = reflection->GetInt32(*message, field_descriptor);
- result = PyInt_FromLong(value);
- break;
- }
- case google::protobuf::FieldDescriptor::CPPTYPE_INT64: {
- int64 value = reflection->GetInt64(*message, field_descriptor);
-#if IS_64BIT
- result = PyInt_FromLong(value);
-#else
- result = PyLong_FromLongLong(value);
-#endif
- break;
- }
- case google::protobuf::FieldDescriptor::CPPTYPE_UINT32: {
- uint32 value = reflection->GetUInt32(*message, field_descriptor);
-#if IS_64BIT
- result = PyInt_FromLong(value);
-#else
- result = PyLong_FromLongLong(value);
-#endif
- break;
- }
- case google::protobuf::FieldDescriptor::CPPTYPE_UINT64: {
- uint64 value = reflection->GetUInt64(*message, field_descriptor);
-#if IS_64BIT
- if (value <= static_cast<uint64>(kint64max)) {
- result = PyInt_FromLong(static_cast<uint64>(value));
- }
-#else
- if (value <= static_cast<uint32>(kint32max)) {
- result = PyInt_FromLong(static_cast<uint32>(value));
- }
-#endif
- else { // NOLINT
- result = PyLong_FromUnsignedLongLong(value);
- }
- break;
- }
- case google::protobuf::FieldDescriptor::CPPTYPE_FLOAT: {
- float value = reflection->GetFloat(*message, field_descriptor);
- result = PyFloat_FromDouble(value);
- break;
- }
- case google::protobuf::FieldDescriptor::CPPTYPE_DOUBLE: {
- double value = reflection->GetDouble(*message, field_descriptor);
- result = PyFloat_FromDouble(value);
- break;
- }
- case google::protobuf::FieldDescriptor::CPPTYPE_BOOL: {
- bool value = reflection->GetBool(*message, field_descriptor);
- result = PyBool_FromLong(value);
- break;
- }
- case google::protobuf::FieldDescriptor::CPPTYPE_STRING: {
- string value = reflection->GetString(*message, field_descriptor);
- result = ToStringObject(field_descriptor, value);
- break;
- }
- case google::protobuf::FieldDescriptor::CPPTYPE_ENUM: {
- if (!message->GetReflection()->HasField(*message, field_descriptor)) {
- // Look for the value in the unknown fields.
- google::protobuf::UnknownFieldSet* unknown_field_set =
- message->GetReflection()->MutableUnknownFields(message);
- for (int i = 0; i < unknown_field_set->field_count(); ++i) {
- if (unknown_field_set->field(i).number() ==
- field_descriptor->number()) {
- result = PyInt_FromLong(unknown_field_set->field(i).varint());
- break;
- }
- }
- }
-
- if (result == NULL) {
- const google::protobuf::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;
-}
-
-static PyObject* InternalSetScalar(
- google::protobuf::Message* message, const google::protobuf::FieldDescriptor* field_descriptor,
- PyObject* arg) {
- const google::protobuf::Reflection* reflection = message->GetReflection();
-
- if (!FIELD_BELONGS_TO_MESSAGE(field_descriptor, message)) {
- PyErr_SetString(
- PyExc_KeyError, "Field does not belong to message!");
- return NULL;
- }
-
- switch (field_descriptor->cpp_type()) {
- case google::protobuf::FieldDescriptor::CPPTYPE_INT32: {
- GOOGLE_CHECK_GET_INT32(arg, value);
- reflection->SetInt32(message, field_descriptor, value);
- break;
- }
- case google::protobuf::FieldDescriptor::CPPTYPE_INT64: {
- GOOGLE_CHECK_GET_INT64(arg, value);
- reflection->SetInt64(message, field_descriptor, value);
- break;
- }
- case google::protobuf::FieldDescriptor::CPPTYPE_UINT32: {
- GOOGLE_CHECK_GET_UINT32(arg, value);
- reflection->SetUInt32(message, field_descriptor, value);
- break;
- }
- case google::protobuf::FieldDescriptor::CPPTYPE_UINT64: {
- GOOGLE_CHECK_GET_UINT64(arg, value);
- reflection->SetUInt64(message, field_descriptor, value);
- break;
- }
- case google::protobuf::FieldDescriptor::CPPTYPE_FLOAT: {
- GOOGLE_CHECK_GET_FLOAT(arg, value);
- reflection->SetFloat(message, field_descriptor, value);
- break;
- }
- case google::protobuf::FieldDescriptor::CPPTYPE_DOUBLE: {
- GOOGLE_CHECK_GET_DOUBLE(arg, value);
- reflection->SetDouble(message, field_descriptor, value);
- break;
- }
- case google::protobuf::FieldDescriptor::CPPTYPE_BOOL: {
- GOOGLE_CHECK_GET_BOOL(arg, value);
- reflection->SetBool(message, field_descriptor, value);
- break;
- }
- case google::protobuf::FieldDescriptor::CPPTYPE_STRING: {
- if (!CheckAndSetString(
- arg, message, field_descriptor, reflection, false, -1)) {
- return NULL;
- }
- break;
- }
- case google::protobuf::FieldDescriptor::CPPTYPE_ENUM: {
- GOOGLE_CHECK_GET_INT32(arg, value);
- const google::protobuf::EnumDescriptor* enum_descriptor =
- field_descriptor->enum_type();
- const google::protobuf::EnumValueDescriptor* enum_value =
- enum_descriptor->FindValueByNumber(value);
- if (enum_value != NULL) {
- reflection->SetEnum(message, field_descriptor, enum_value);
- } else {
- bool added = false;
- // Add the value to the unknown fields.
- google::protobuf::UnknownFieldSet* unknown_field_set =
- message->GetReflection()->MutableUnknownFields(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->mutable_field(i)->set_varint(value);
- added = true;
- break;
- }
- }
-
- if (!added) {
- unknown_field_set->AddVarint(field_descriptor->number(), value);
- }
- reflection->ClearField(message, field_descriptor);
- }
- break;
- }
- default:
- PyErr_Format(
- PyExc_SystemError, "Setting value to a field of unknown type %d",
- field_descriptor->cpp_type());
- }
-
- Py_RETURN_NONE;
-}
-
-static PyObject* InternalAddRepeatedScalar(
- google::protobuf::Message* message, const google::protobuf::FieldDescriptor* field_descriptor,
- PyObject* arg) {
-
- if (!FIELD_BELONGS_TO_MESSAGE(field_descriptor, message)) {
- PyErr_SetString(
- PyExc_KeyError, "Field does not belong to message!");
- return NULL;
- }
-
- const google::protobuf::Reflection* reflection = message->GetReflection();
- switch (field_descriptor->cpp_type()) {
- case google::protobuf::FieldDescriptor::CPPTYPE_INT32: {
- GOOGLE_CHECK_GET_INT32(arg, value);
- reflection->AddInt32(message, field_descriptor, value);
- break;
- }
- case google::protobuf::FieldDescriptor::CPPTYPE_INT64: {
- GOOGLE_CHECK_GET_INT64(arg, value);
- reflection->AddInt64(message, field_descriptor, value);
- break;
- }
- case google::protobuf::FieldDescriptor::CPPTYPE_UINT32: {
- GOOGLE_CHECK_GET_UINT32(arg, value);
- reflection->AddUInt32(message, field_descriptor, value);
- break;
- }
- case google::protobuf::FieldDescriptor::CPPTYPE_UINT64: {
- GOOGLE_CHECK_GET_UINT64(arg, value);
- reflection->AddUInt64(message, field_descriptor, value);
- break;
- }
- case google::protobuf::FieldDescriptor::CPPTYPE_FLOAT: {
- GOOGLE_CHECK_GET_FLOAT(arg, value);
- reflection->AddFloat(message, field_descriptor, value);
- break;
- }
- case google::protobuf::FieldDescriptor::CPPTYPE_DOUBLE: {
- GOOGLE_CHECK_GET_DOUBLE(arg, value);
- reflection->AddDouble(message, field_descriptor, value);
- break;
- }
- case google::protobuf::FieldDescriptor::CPPTYPE_BOOL: {
- GOOGLE_CHECK_GET_BOOL(arg, value);
- reflection->AddBool(message, field_descriptor, value);
- break;
- }
- case google::protobuf::FieldDescriptor::CPPTYPE_STRING: {
- if (!CheckAndSetString(
- arg, message, field_descriptor, reflection, true, -1)) {
- return NULL;
- }
- break;
- }
- case google::protobuf::FieldDescriptor::CPPTYPE_ENUM: {
- GOOGLE_CHECK_GET_INT32(arg, value);
- const google::protobuf::EnumDescriptor* enum_descriptor =
- field_descriptor->enum_type();
- const google::protobuf::EnumValueDescriptor* enum_value =
- enum_descriptor->FindValueByNumber(value);
- if (enum_value != NULL) {
- reflection->AddEnum(message, field_descriptor, enum_value);
- } else {
- PyObject* s = PyObject_Str(arg);
- PyErr_Format(PyExc_ValueError, "Unknown enum value: %s",
- PyString_AS_STRING(s));
- Py_DECREF(s);
- return NULL;
- }
- break;
- }
- default:
- PyErr_Format(
- PyExc_SystemError, "Adding value to a field of unknown type %d",
- field_descriptor->cpp_type());
- }
-
- Py_RETURN_NONE;
-}
-
-static PyObject* InternalGetRepeatedScalar(
- CMessage* cmessage, const google::protobuf::FieldDescriptor* field_descriptor,
- int index) {
- google::protobuf::Message* message = cmessage->message;
- const google::protobuf::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", index);
- return NULL;
- }
-
- PyObject* result = NULL;
- switch (field_descriptor->cpp_type()) {
- case google::protobuf::FieldDescriptor::CPPTYPE_INT32: {
- int32 value = reflection->GetRepeatedInt32(
- *message, field_descriptor, index);
- result = PyInt_FromLong(value);
- break;
- }
- case google::protobuf::FieldDescriptor::CPPTYPE_INT64: {
- int64 value = reflection->GetRepeatedInt64(
- *message, field_descriptor, index);
- result = PyLong_FromLongLong(value);
- break;
- }
- case google::protobuf::FieldDescriptor::CPPTYPE_UINT32: {
- uint32 value = reflection->GetRepeatedUInt32(
- *message, field_descriptor, index);
- result = PyLong_FromLongLong(value);
- break;
- }
- case google::protobuf::FieldDescriptor::CPPTYPE_UINT64: {
- uint64 value = reflection->GetRepeatedUInt64(
- *message, field_descriptor, index);
- result = PyLong_FromUnsignedLongLong(value);
- break;
- }
- case google::protobuf::FieldDescriptor::CPPTYPE_FLOAT: {
- float value = reflection->GetRepeatedFloat(
- *message, field_descriptor, index);
- result = PyFloat_FromDouble(value);
- break;
- }
- case google::protobuf::FieldDescriptor::CPPTYPE_DOUBLE: {
- double value = reflection->GetRepeatedDouble(
- *message, field_descriptor, index);
- result = PyFloat_FromDouble(value);
- break;
- }
- case google::protobuf::FieldDescriptor::CPPTYPE_BOOL: {
- bool value = reflection->GetRepeatedBool(
- *message, field_descriptor, index);
- result = PyBool_FromLong(value ? 1 : 0);
- break;
- }
- case google::protobuf::FieldDescriptor::CPPTYPE_ENUM: {
- const google::protobuf::EnumValueDescriptor* enum_value =
- message->GetReflection()->GetRepeatedEnum(
- *message, field_descriptor, index);
- result = PyInt_FromLong(enum_value->number());
- break;
- }
- case google::protobuf::FieldDescriptor::CPPTYPE_STRING: {
- string value = reflection->GetRepeatedString(
- *message, field_descriptor, index);
- result = ToStringObject(field_descriptor, value);
- break;
- }
- case google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE: {
- CMessage* py_cmsg = PyObject_New(CMessage, &CMessage_Type);
- if (py_cmsg == NULL) {
- return NULL;
- }
- const google::protobuf::Message& msg = reflection->GetRepeatedMessage(
- *message, field_descriptor, index);
- py_cmsg->parent = cmessage;
- py_cmsg->full_name = field_descriptor->full_name().c_str();
- py_cmsg->message = const_cast<google::protobuf::Message*>(&msg);
- py_cmsg->free_message = false;
- py_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* InternalGetRepeatedScalarSlice(
- CMessage* cmessage, const google::protobuf::FieldDescriptor* field_descriptor,
- PyObject* slice) {
- Py_ssize_t from;
- Py_ssize_t to;
- Py_ssize_t step;
- Py_ssize_t length;
- bool return_list = false;
- google::protobuf::Message* message = cmessage->message;
-
- if (PyInt_Check(slice)) {
- from = to = PyInt_AsLong(slice);
- } else if (PyLong_Check(slice)) {
- from = to = PyLong_AsLong(slice);
- } else if (PySlice_Check(slice)) {
- const google::protobuf::Reflection* reflection = message->GetReflection();
- length = reflection->FieldSize(*message, field_descriptor);
- PySlice_GetIndices(
- reinterpret_cast<PySliceObject*>(slice), length, &from, &to, &step);
- return_list = true;
- } else {
- PyErr_SetString(PyExc_TypeError, "list indices must be integers");
- return NULL;
- }
-
- if (!return_list) {
- return InternalGetRepeatedScalar(cmessage, field_descriptor, 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;
- PyObject* s = InternalGetRepeatedScalar(
- cmessage, field_descriptor, index);
- PyList_Append(list, s);
- Py_DECREF(s);
- }
- } else {
- if (step > 0) return list;
- for (Py_ssize_t index = from; index > to; index += step) {
- if (index < 0 || index >= length) break;
- PyObject* s = InternalGetRepeatedScalar(
- cmessage, field_descriptor, index);
- PyList_Append(list, s);
- Py_DECREF(s);
- }
- }
- return list;
-}
-
-// ------ C Constructor/Destructor:
-
-static int CMessageInit(CMessage* self, PyObject *args, PyObject *kwds) {
- self->message = NULL;
- return 0;
-}
-
-static void CMessageDealloc(CMessage* self) {
- if (self->free_message) {
- if (self->read_only) {
- PyErr_WriteUnraisable(reinterpret_cast<PyObject*>(self));
- }
- delete self->message;
- }
- self->ob_type->tp_free(reinterpret_cast<PyObject*>(self));
-}
-
-// ------ Methods:
-
-static PyObject* CMessage_Clear(CMessage* self, PyObject* args) {
- AssureWritable(self);
- self->message->Clear();
- Py_RETURN_NONE;
-}
-
-static PyObject* CMessage_IsInitialized(CMessage* self, PyObject* args) {
- return PyBool_FromLong(self->message->IsInitialized() ? 1 : 0);
-}
-
-static PyObject* CMessage_HasField(CMessage* self, PyObject* arg) {
- char* field_name;
- if (PyString_AsStringAndSize(arg, &field_name, NULL) < 0) {
- return NULL;
- }
-
- google::protobuf::Message* message = self->message;
- const google::protobuf::Descriptor* descriptor = message->GetDescriptor();
- const google::protobuf::FieldDescriptor* field_descriptor =
- descriptor->FindFieldByName(field_name);
- if (field_descriptor == NULL) {
- PyErr_Format(PyExc_ValueError, "Unknown field %s.", field_name);
- return NULL;
- }
-
- bool has_field =
- message->GetReflection()->HasField(*message, field_descriptor);
- return PyBool_FromLong(has_field ? 1 : 0);
-}
-
-static PyObject* CMessage_HasFieldByDescriptor(CMessage* self, PyObject* arg) {
- CFieldDescriptor* cfield_descriptor = NULL;
- if (!PyObject_TypeCheck(reinterpret_cast<PyObject *>(arg),
- &CFieldDescriptor_Type)) {
- PyErr_SetString(PyExc_TypeError, "Must be a field descriptor");
- return NULL;
- }
- cfield_descriptor = reinterpret_cast<CFieldDescriptor*>(arg);
-
- google::protobuf::Message* message = self->message;
- const google::protobuf::FieldDescriptor* field_descriptor =
- cfield_descriptor->descriptor;
-
- if (!FIELD_BELONGS_TO_MESSAGE(field_descriptor, message)) {
- PyErr_SetString(PyExc_KeyError,
- "Field does not belong to message!");
- return NULL;
- }
-
- if (FIELD_IS_REPEATED(field_descriptor)) {
- 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);
-}
-
-static PyObject* CMessage_ClearFieldByDescriptor(
- CMessage* self, PyObject* arg) {
- CFieldDescriptor* cfield_descriptor = NULL;
- if (!PyObject_TypeCheck(reinterpret_cast<PyObject *>(arg),
- &CFieldDescriptor_Type)) {
- PyErr_SetString(PyExc_TypeError, "Must be a field descriptor");
- return NULL;
- }
- cfield_descriptor = reinterpret_cast<CFieldDescriptor*>(arg);
-
- google::protobuf::Message* message = self->message;
- const google::protobuf::FieldDescriptor* field_descriptor =
- cfield_descriptor->descriptor;
-
- if (!FIELD_BELONGS_TO_MESSAGE(field_descriptor, message)) {
- PyErr_SetString(PyExc_KeyError,
- "Field does not belong to message!");
- return NULL;
- }
-
- message->GetReflection()->ClearField(message, field_descriptor);
- Py_RETURN_NONE;
-}
-
-static PyObject* CMessage_ClearField(CMessage* self, PyObject* arg) {
- char* field_name;
- if (PyString_AsStringAndSize(arg, &field_name, NULL) < 0) {
- return NULL;
- }
-
- google::protobuf::Message* message = self->message;
- const google::protobuf::Descriptor* descriptor = message->GetDescriptor();
- const google::protobuf::FieldDescriptor* field_descriptor =
- descriptor->FindFieldByName(field_name);
- if (field_descriptor == NULL) {
- PyErr_Format(PyExc_ValueError, "Unknown field %s.", field_name);
- return NULL;
- }
-
- message->GetReflection()->ClearField(message, field_descriptor);
- Py_RETURN_NONE;
-}
-
-static PyObject* CMessage_GetScalar(CMessage* self, PyObject* arg) {
- CFieldDescriptor* cdescriptor = NULL;
- if (!PyObject_TypeCheck(reinterpret_cast<PyObject *>(arg),
- &CFieldDescriptor_Type)) {
- PyErr_SetString(PyExc_TypeError, "Must be a field descriptor");
- return NULL;
- }
- cdescriptor = reinterpret_cast<CFieldDescriptor*>(arg);
-
- google::protobuf::Message* message = self->message;
- return InternalGetScalar(message, cdescriptor->descriptor);
-}
-
-static PyObject* CMessage_GetRepeatedScalar(CMessage* self, PyObject* args) {
- CFieldDescriptor* cfield_descriptor;
- PyObject* slice;
- if (!PyArg_ParseTuple(args, C("O!O:GetRepeatedScalar"),
- &CFieldDescriptor_Type, &cfield_descriptor, &slice)) {
- return NULL;
- }
-
- return InternalGetRepeatedScalarSlice(
- self, cfield_descriptor->descriptor, slice);
-}
-
-static PyObject* CMessage_AssignRepeatedScalar(CMessage* self, PyObject* args) {
- CFieldDescriptor* cfield_descriptor;
- PyObject* slice;
- if (!PyArg_ParseTuple(args, C("O!O:AssignRepeatedScalar"),
- &CFieldDescriptor_Type, &cfield_descriptor, &slice)) {
- return NULL;
- }
-
- AssureWritable(self);
- google::protobuf::Message* message = self->message;
- message->GetReflection()->ClearField(message, cfield_descriptor->descriptor);
-
- PyObject* iter = PyObject_GetIter(slice);
- PyObject* next;
- while ((next = PyIter_Next(iter)) != NULL) {
- if (InternalAddRepeatedScalar(
- message, cfield_descriptor->descriptor, next) == NULL) {
- Py_DECREF(iter);
- return NULL;
- }
- }
- Py_DECREF(iter);
- Py_RETURN_NONE;
-}
-
-static PyObject* CMessage_DeleteRepeatedField(CMessage* self, PyObject* args) {
- CFieldDescriptor* cfield_descriptor;
- PyObject* slice;
- if (!PyArg_ParseTuple(args, C("O!O:DeleteRepeatedField"),
- &CFieldDescriptor_Type, &cfield_descriptor, &slice)) {
- return NULL;
- }
- AssureWritable(self);
-
- Py_ssize_t length, from, to, step, slice_length;
- google::protobuf::Message* message = self->message;
- const google::protobuf::FieldDescriptor* field_descriptor =
- cfield_descriptor->descriptor;
- const google::protobuf::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 NULL;
- }
- } else if (PySlice_Check(slice)) {
- from = to = step = slice_length = 0;
- PySlice_GetIndicesEx(
- reinterpret_cast<PySliceObject*>(slice),
- 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 NULL;
- }
-
- 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);
- }
- ++to;
- }
- }
-
- while (i > to) {
- reflection->RemoveLast(message, field_descriptor);
- --i;
- }
-
- Py_RETURN_NONE;
-}
-
-
-static PyObject* CMessage_SetScalar(CMessage* self, PyObject* args) {
- CFieldDescriptor* cfield_descriptor;
- PyObject* arg;
- if (!PyArg_ParseTuple(args, C("O!O:SetScalar"),
- &CFieldDescriptor_Type, &cfield_descriptor, &arg)) {
- return NULL;
- }
- AssureWritable(self);
-
- return InternalSetScalar(self->message, cfield_descriptor->descriptor, arg);
-}
-
-static PyObject* CMessage_AddRepeatedScalar(CMessage* self, PyObject* args) {
- CFieldDescriptor* cfield_descriptor;
- PyObject* value;
- if (!PyArg_ParseTuple(args, C("O!O:AddRepeatedScalar"),
- &CFieldDescriptor_Type, &cfield_descriptor, &value)) {
- return NULL;
- }
- AssureWritable(self);
-
- return InternalAddRepeatedScalar(
- self->message, cfield_descriptor->descriptor, value);
-}
-
-static PyObject* CMessage_FieldLength(CMessage* self, PyObject* arg) {
- CFieldDescriptor* cfield_descriptor;
- if (!PyObject_TypeCheck(reinterpret_cast<PyObject *>(arg),
- &CFieldDescriptor_Type)) {
- PyErr_SetString(PyExc_TypeError, "Must be a field descriptor");
- return NULL;
- }
- cfield_descriptor = reinterpret_cast<CFieldDescriptor*>(arg);
-
- google::protobuf::Message* message = self->message;
- int length = message->GetReflection()->FieldSize(
- *message, cfield_descriptor->descriptor);
- return PyInt_FromLong(length);
-}
-
-static PyObject* CMessage_DebugString(CMessage* self, PyObject* args) {
- return PyString_FromString(self->message->DebugString().c_str());
-}
-
-static PyObject* CMessage_SerializeToString(CMessage* self, PyObject* args) {
- int size = self->message->ByteSize();
- if (size <= 0) {
- return PyString_FromString("");
- }
- PyObject* result = PyString_FromStringAndSize(NULL, size);
- if (result == NULL) {
- return NULL;
- }
- char* buffer = PyString_AS_STRING(result);
- self->message->SerializeWithCachedSizesToArray(
- reinterpret_cast<uint8*>(buffer));
- return result;
-}
-
-static PyObject* CMessage_SerializePartialToString(
- CMessage* self, PyObject* args) {
- string contents;
- self->message->SerializePartialToString(&contents);
- return PyString_FromStringAndSize(contents.c_str(), contents.size());
-}
-
-static PyObject* CMessageStr(CMessage* self) {
- char str[1024];
- str[sizeof(str) - 1] = 0;
- snprintf(str, sizeof(str) - 1, "CMessage: <%p>", self->message);
- return PyString_FromString(str);
-}
-
-static PyObject* CMessage_MergeFrom(CMessage* self, PyObject* arg) {
- CMessage* other_message;
- if (!PyObject_TypeCheck(reinterpret_cast<PyObject *>(arg), &CMessage_Type)) {
- PyErr_SetString(PyExc_TypeError, "Must be a message");
- return NULL;
- }
-
- other_message = reinterpret_cast<CMessage*>(arg);
- if (other_message->message->GetDescriptor() !=
- self->message->GetDescriptor()) {
- PyErr_Format(PyExc_TypeError,
- "Tried to merge from a message with a different type. "
- "to: %s, from: %s",
- self->message->GetDescriptor()->full_name().c_str(),
- other_message->message->GetDescriptor()->full_name().c_str());
- return NULL;
- }
- AssureWritable(self);
-
- self->message->MergeFrom(*other_message->message);
- Py_RETURN_NONE;
-}
-
-static PyObject* CMessage_CopyFrom(CMessage* self, PyObject* arg) {
- CMessage* other_message;
- if (!PyObject_TypeCheck(reinterpret_cast<PyObject *>(arg), &CMessage_Type)) {
- PyErr_SetString(PyExc_TypeError, "Must be a message");
- return NULL;
- }
-
- other_message = reinterpret_cast<CMessage*>(arg);
- if (other_message->message->GetDescriptor() !=
- self->message->GetDescriptor()) {
- PyErr_Format(PyExc_TypeError,
- "Tried to copy from a message with a different type. "
- "to: %s, from: %s",
- self->message->GetDescriptor()->full_name().c_str(),
- other_message->message->GetDescriptor()->full_name().c_str());
- return NULL;
- }
-
- AssureWritable(self);
-
- self->message->CopyFrom(*other_message->message);
- Py_RETURN_NONE;
-}
-
-static PyObject* CMessage_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);
- google::protobuf::io::CodedInputStream input(
- reinterpret_cast<const uint8*>(data), data_length);
- bool success = self->message->MergePartialFromCodedStream(&input);
- if (success) {
- return PyInt_FromLong(self->message->ByteSize());
- } else {
- return PyInt_FromLong(-1);
- }
-}
-
-static PyObject* CMessage_ByteSize(CMessage* self, PyObject* args) {
- return PyLong_FromLong(self->message->ByteSize());
-}
-
-static PyObject* CMessage_SetInParent(CMessage* self, PyObject* args) {
- AssureWritable(self);
- Py_RETURN_NONE;
-}
-
-static PyObject* CMessage_SwapRepeatedFieldElements(
- CMessage* self, PyObject* args) {
- CFieldDescriptor* cfield_descriptor;
- int index1, index2;
- if (!PyArg_ParseTuple(args, C("O!ii:SwapRepeatedFieldElements"),
- &CFieldDescriptor_Type, &cfield_descriptor,
- &index1, &index2)) {
- return NULL;
- }
-
- google::protobuf::Message* message = self->message;
- const google::protobuf::Reflection* reflection = message->GetReflection();
-
- reflection->SwapElements(
- message, cfield_descriptor->descriptor, index1, index2);
- Py_RETURN_NONE;
-}
-
-static PyObject* CMessage_AddMessage(CMessage* self, PyObject* arg) {
- CFieldDescriptor* cfield_descriptor;
- if (!PyObject_TypeCheck(reinterpret_cast<PyObject *>(arg),
- &CFieldDescriptor_Type)) {
- PyErr_SetString(PyExc_TypeError, "Must be a field descriptor");
- return NULL;
- }
- cfield_descriptor = reinterpret_cast<CFieldDescriptor*>(arg);
- AssureWritable(self);
-
- CMessage* py_cmsg = PyObject_New(CMessage, &CMessage_Type);
- if (py_cmsg == NULL) {
- return NULL;
- }
-
- google::protobuf::Message* message = self->message;
- const google::protobuf::Reflection* reflection = message->GetReflection();
- google::protobuf::Message* sub_message =
- reflection->AddMessage(message, cfield_descriptor->descriptor);
-
- py_cmsg->parent = NULL;
- py_cmsg->full_name = sub_message->GetDescriptor()->full_name().c_str();
- py_cmsg->message = sub_message;
- py_cmsg->free_message = false;
- py_cmsg->read_only = false;
- return reinterpret_cast<PyObject*>(py_cmsg);
-}
-
-static PyObject* CMessage_GetRepeatedMessage(CMessage* self, PyObject* args) {
- CFieldDescriptor* cfield_descriptor;
- PyObject* slice;
- if (!PyArg_ParseTuple(args, C("O!O:GetRepeatedMessage"),
- &CFieldDescriptor_Type, &cfield_descriptor, &slice)) {
- return NULL;
- }
-
- return InternalGetRepeatedScalarSlice(
- self, cfield_descriptor->descriptor, slice);
-}
-
-static PyObject* CMessage_NewSubMessage(CMessage* self, PyObject* arg) {
- CFieldDescriptor* cfield_descriptor;
- if (!PyObject_TypeCheck(reinterpret_cast<PyObject *>(arg),
- &CFieldDescriptor_Type)) {
- PyErr_SetString(PyExc_TypeError, "Must be a field descriptor");
- return NULL;
- }
- cfield_descriptor = reinterpret_cast<CFieldDescriptor*>(arg);
-
- CMessage* py_cmsg = PyObject_New(CMessage, &CMessage_Type);
- if (py_cmsg == NULL) {
- return NULL;
- }
-
- google::protobuf::Message* message = self->message;
- const google::protobuf::Reflection* reflection = message->GetReflection();
- const google::protobuf::Message& sub_message =
- reflection->GetMessage(*message, cfield_descriptor->descriptor,
- global_message_factory);
-
- py_cmsg->full_name = sub_message.GetDescriptor()->full_name().c_str();
- py_cmsg->parent = self;
- py_cmsg->parent_field = cfield_descriptor;
- py_cmsg->message = const_cast<google::protobuf::Message*>(&sub_message);
- py_cmsg->free_message = false;
- py_cmsg->read_only = true;
- return reinterpret_cast<PyObject*>(py_cmsg);
-}
-
-static PyObject* CMessage_MutableMessage(CMessage* self, PyObject* arg) {
- CFieldDescriptor* cfield_descriptor;
- if (!PyObject_TypeCheck(reinterpret_cast<PyObject *>(arg),
- &CFieldDescriptor_Type)) {
- PyErr_SetString(PyExc_TypeError, "Must be a field descriptor");
- return NULL;
- }
- cfield_descriptor = reinterpret_cast<CFieldDescriptor*>(arg);
- AssureWritable(self);
-
- CMessage* py_cmsg = PyObject_New(CMessage, &CMessage_Type);
- if (py_cmsg == NULL) {
- return NULL;
- }
-
- google::protobuf::Message* message = self->message;
- const google::protobuf::Reflection* reflection = message->GetReflection();
- google::protobuf::Message* mutable_message =
- reflection->MutableMessage(message, cfield_descriptor->descriptor,
- global_message_factory);
-
- py_cmsg->full_name = mutable_message->GetDescriptor()->full_name().c_str();
- py_cmsg->message = mutable_message;
- py_cmsg->free_message = false;
- py_cmsg->read_only = false;
- return reinterpret_cast<PyObject*>(py_cmsg);
-}
-
-static PyObject* CMessage_Equals(CMessage* self, PyObject* arg) {
- CMessage* other_message;
- if (!PyObject_TypeCheck(reinterpret_cast<PyObject *>(arg), &CMessage_Type)) {
- PyErr_SetString(PyExc_TypeError, "Must be a message");
- return NULL;
- }
- other_message = reinterpret_cast<CMessage*>(arg);
-
- if (other_message->message == self->message) {
- return PyBool_FromLong(1);
- }
-
- if (other_message->message->GetDescriptor() !=
- self->message->GetDescriptor()) {
- return PyBool_FromLong(0);
- }
-
- return PyBool_FromLong(1);
-}
-
-static PyObject* CMessage_ListFields(CMessage* self, PyObject* args) {
- google::protobuf::Message* message = self->message;
- const google::protobuf::Reflection* reflection = message->GetReflection();
- vector<const google::protobuf::FieldDescriptor*> fields;
- reflection->ListFields(*message, &fields);
-
- PyObject* list = PyList_New(fields.size());
- if (list == NULL) {
- return NULL;
- }
-
- for (unsigned int i = 0; i < fields.size(); ++i) {
- bool is_extension = fields[i]->is_extension();
- PyObject* t = PyTuple_New(2);
- if (t == NULL) {
- Py_DECREF(list);
- return NULL;
- }
-
- PyObject* is_extension_object = PyBool_FromLong(is_extension ? 1 : 0);
-
- PyObject* field_name;
- const string* s;
- if (is_extension) {
- s = &fields[i]->full_name();
- } else {
- s = &fields[i]->name();
- }
- field_name = PyString_FromStringAndSize(s->c_str(), s->length());
- if (field_name == NULL) {
- Py_DECREF(list);
- Py_DECREF(t);
- return NULL;
- }
-
- PyTuple_SET_ITEM(t, 0, is_extension_object);
- PyTuple_SET_ITEM(t, 1, field_name);
- PyList_SET_ITEM(list, i, t);
- }
-
- return list;
-}
-
-static PyObject* CMessage_FindInitializationErrors(CMessage* self) {
- google::protobuf::Message* message = self->message;
- vector<string> errors;
- message->FindInitializationErrors(&errors);
-
- PyObject* error_list = PyList_New(errors.size());
- if (error_list == NULL) {
- return NULL;
- }
- for (unsigned int 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;
-}
-
-// ------ Python Constructor:
-
-PyObject* Python_NewCMessage(PyObject* ignored, PyObject* arg) {
- const char* message_type = PyString_AsString(arg);
- if (message_type == NULL) {
- return NULL;
- }
-
- const google::protobuf::Message* message = CreateMessage(message_type);
- if (message == NULL) {
- PyErr_Format(PyExc_TypeError, "Couldn't create message of type %s!",
- message_type);
- return NULL;
- }
-
- CMessage* py_cmsg = PyObject_New(CMessage, &CMessage_Type);
- if (py_cmsg == NULL) {
- return NULL;
- }
- py_cmsg->message = message->New();
- py_cmsg->free_message = true;
- py_cmsg->full_name = message->GetDescriptor()->full_name().c_str();
- py_cmsg->read_only = false;
- py_cmsg->parent = NULL;
- py_cmsg->parent_field = NULL;
- return reinterpret_cast<PyObject*>(py_cmsg);
-}
-
-// --- Module Functions (exposed to Python):
-
-PyMethodDef methods[] = {
- { C("NewCMessage"), (PyCFunction)Python_NewCMessage,
- METH_O,
- C("Creates a new C++ protocol message, given its full name.") },
- { C("NewCDescriptorPool"), (PyCFunction)Python_NewCDescriptorPool,
- METH_NOARGS,
- C("Creates a new C++ descriptor pool.") },
- { C("BuildFile"), (PyCFunction)Python_BuildFile,
- METH_O,
- C("Registers a new protocol buffer file in the global C++ descriptor "
- "pool.") },
- {NULL}
-};
-
-// --- Exposing the C proto living inside Python proto to C code:
-
-extern const Message* (*GetCProtoInsidePyProtoPtr)(PyObject* msg);
-extern Message* (*MutableCProtoInsidePyProtoPtr)(PyObject* msg);
-
-static const google::protobuf::Message* GetCProtoInsidePyProtoImpl(PyObject* msg) {
- PyObject* c_msg_obj = PyObject_GetAttrString(msg, "_cmsg");
- if (c_msg_obj == NULL) {
- PyErr_Clear();
- return NULL;
- }
- Py_DECREF(c_msg_obj);
- if (!PyObject_TypeCheck(c_msg_obj, &CMessage_Type)) {
- return NULL;
- }
- CMessage* c_msg = reinterpret_cast<CMessage*>(c_msg_obj);
- return c_msg->message;
-}
-
-static google::protobuf::Message* MutableCProtoInsidePyProtoImpl(PyObject* msg) {
- PyObject* c_msg_obj = PyObject_GetAttrString(msg, "_cmsg");
- if (c_msg_obj == NULL) {
- PyErr_Clear();
- return NULL;
- }
- Py_DECREF(c_msg_obj);
- if (!PyObject_TypeCheck(c_msg_obj, &CMessage_Type)) {
- return NULL;
- }
- CMessage* c_msg = reinterpret_cast<CMessage*>(c_msg_obj);
- AssureWritable(c_msg);
- return c_msg->message;
-}
-
-// --- Module Init Function:
-
-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.";
-
-extern "C" {
- void init_net_proto2___python() {
- // Initialize constants.
- 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);
-
- global_message_factory = new DynamicMessageFactory(GetDescriptorPool());
- global_message_factory->SetDelegateToGeneratedFactory(true);
-
- // Export our functions to Python.
- PyObject *m;
- m = Py_InitModule3(C("_net_proto2___python"), methods, C(module_docstring));
- if (m == NULL) {
- return;
- }
-
- AddConstants(m);
-
- CMessage_Type.tp_new = PyType_GenericNew;
- if (PyType_Ready(&CMessage_Type) < 0) {
- return;
- }
-
- if (!InitDescriptor()) {
- return;
- }
-
- // Override {Get,Mutable}CProtoInsidePyProto.
- GetCProtoInsidePyProtoPtr = GetCProtoInsidePyProtoImpl;
- MutableCProtoInsidePyProtoPtr = MutableCProtoInsidePyProtoImpl;
- }
-}
-
-} // namespace python
-} // namespace protobuf
-} // namespace google
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_descriptor.cc b/python/google/protobuf/pyext/python_descriptor.cc
deleted file mode 100644
index fb87bad1..00000000
--- a/python/google/protobuf/pyext/python_descriptor.cc
+++ /dev/null
@@ -1,334 +0,0 @@
-// Protocol Buffers - Google's data interchange format
-// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
-//
-// 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 <google/protobuf/pyext/python_descriptor.h>
-#include <google/protobuf/descriptor.pb.h>
-
-#define C(str) const_cast<char*>(str)
-
-namespace google {
-namespace protobuf {
-namespace python {
-
-static void CFieldDescriptorDealloc(CFieldDescriptor* self);
-
-static google::protobuf::DescriptorPool* g_descriptor_pool = NULL;
-
-static PyObject* CFieldDescriptor_GetFullName(
- CFieldDescriptor* self, void *closure) {
- Py_XINCREF(self->full_name);
- return self->full_name;
-}
-
-static PyObject* CFieldDescriptor_GetName(
- CFieldDescriptor *self, void *closure) {
- Py_XINCREF(self->name);
- return self->name;
-}
-
-static PyObject* CFieldDescriptor_GetCppType(
- CFieldDescriptor *self, void *closure) {
- Py_XINCREF(self->cpp_type);
- return self->cpp_type;
-}
-
-static PyObject* CFieldDescriptor_GetLabel(
- CFieldDescriptor *self, void *closure) {
- Py_XINCREF(self->label);
- return self->label;
-}
-
-static PyObject* CFieldDescriptor_GetID(
- CFieldDescriptor *self, void *closure) {
- Py_XINCREF(self->id);
- return self->id;
-}
-
-
-static PyGetSetDef CFieldDescriptorGetters[] = {
- { C("full_name"),
- (getter)CFieldDescriptor_GetFullName, NULL, "Full name", NULL},
- { C("name"),
- (getter)CFieldDescriptor_GetName, NULL, "last name", NULL},
- { C("cpp_type"),
- (getter)CFieldDescriptor_GetCppType, NULL, "C++ Type", NULL},
- { C("label"),
- (getter)CFieldDescriptor_GetLabel, NULL, "Label", NULL},
- { C("id"),
- (getter)CFieldDescriptor_GetID, NULL, "ID", NULL},
- {NULL}
-};
-
-PyTypeObject CFieldDescriptor_Type = {
- PyObject_HEAD_INIT(&PyType_Type)
- 0,
- C("google3.net.google.protobuf.python.internal."
- "_net_proto2___python."
- "CFieldDescriptor"), // tp_name
- sizeof(CFieldDescriptor), // tp_basicsize
- 0, // tp_itemsize
- (destructor)CFieldDescriptorDealloc, // 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
- C("A Field Descriptor"), // 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
- CFieldDescriptorGetters, // tp_getset
- 0, // tp_base
- 0, // tp_dict
- 0, // tp_descr_get
- 0, // tp_descr_set
- 0, // tp_dictoffset
- 0, // tp_init
- PyType_GenericAlloc, // tp_alloc
- PyType_GenericNew, // tp_new
- PyObject_Del, // tp_free
-};
-
-static void CFieldDescriptorDealloc(CFieldDescriptor* self) {
- Py_DECREF(self->full_name);
- Py_DECREF(self->name);
- Py_DECREF(self->cpp_type);
- Py_DECREF(self->label);
- Py_DECREF(self->id);
- self->ob_type->tp_free(reinterpret_cast<PyObject*>(self));
-}
-
-typedef struct {
- PyObject_HEAD
-
- const google::protobuf::DescriptorPool* pool;
-} CDescriptorPool;
-
-static void CDescriptorPoolDealloc(CDescriptorPool* self);
-
-static PyObject* CDescriptorPool_NewCDescriptor(
- const google::protobuf::FieldDescriptor* field_descriptor) {
- CFieldDescriptor* cfield_descriptor = PyObject_New(
- CFieldDescriptor, &CFieldDescriptor_Type);
- if (cfield_descriptor == NULL) {
- return NULL;
- }
- cfield_descriptor->descriptor = field_descriptor;
-
- cfield_descriptor->full_name = PyString_FromString(
- field_descriptor->full_name().c_str());
- cfield_descriptor->name = PyString_FromString(
- field_descriptor->name().c_str());
- cfield_descriptor->cpp_type = PyLong_FromLong(field_descriptor->cpp_type());
- cfield_descriptor->label = PyLong_FromLong(field_descriptor->label());
- cfield_descriptor->id = PyLong_FromVoidPtr(cfield_descriptor);
- return reinterpret_cast<PyObject*>(cfield_descriptor);
-}
-
-static PyObject* CDescriptorPool_FindFieldByName(
- CDescriptorPool* self, PyObject* arg) {
- const char* full_field_name = PyString_AsString(arg);
- if (full_field_name == NULL) {
- return NULL;
- }
-
- const google::protobuf::FieldDescriptor* field_descriptor = NULL;
-
- field_descriptor = self->pool->FindFieldByName(full_field_name);
- if (field_descriptor == NULL) {
- PyErr_Format(PyExc_TypeError, "Couldn't find field %.200s",
- full_field_name);
- return NULL;
- }
-
- return CDescriptorPool_NewCDescriptor(field_descriptor);
-}
-
-static PyObject* CDescriptorPool_FindExtensionByName(
- CDescriptorPool* self, PyObject* arg) {
- const char* full_field_name = PyString_AsString(arg);
- if (full_field_name == NULL) {
- return NULL;
- }
-
- const google::protobuf::FieldDescriptor* field_descriptor =
- self->pool->FindExtensionByName(full_field_name);
- if (field_descriptor == NULL) {
- PyErr_Format(PyExc_TypeError, "Couldn't find field %.200s",
- full_field_name);
- return NULL;
- }
-
- return CDescriptorPool_NewCDescriptor(field_descriptor);
-}
-
-static PyMethodDef CDescriptorPoolMethods[] = {
- { C("FindFieldByName"),
- (PyCFunction)CDescriptorPool_FindFieldByName,
- METH_O,
- C("Searches for a field descriptor by full name.") },
- { C("FindExtensionByName"),
- (PyCFunction)CDescriptorPool_FindExtensionByName,
- METH_O,
- C("Searches for extension descriptor by full name.") },
- {NULL}
-};
-
-PyTypeObject CDescriptorPool_Type = {
- PyObject_HEAD_INIT(&PyType_Type)
- 0,
- C("google3.net.google.protobuf.python.internal."
- "_net_proto2___python."
- "CFieldDescriptor"), // tp_name
- sizeof(CDescriptorPool), // tp_basicsize
- 0, // tp_itemsize
- (destructor)CDescriptorPoolDealloc, // 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
- C("A Descriptor Pool"), // tp_doc
- 0, // tp_traverse
- 0, // tp_clear
- 0, // tp_richcompare
- 0, // tp_weaklistoffset
- 0, // tp_iter
- 0, // tp_iternext
- CDescriptorPoolMethods, // 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
- PyType_GenericAlloc, // tp_alloc
- PyType_GenericNew, // tp_new
- PyObject_Del, // tp_free
-};
-
-static void CDescriptorPoolDealloc(CDescriptorPool* self) {
- self->ob_type->tp_free(reinterpret_cast<PyObject*>(self));
-}
-
-google::protobuf::DescriptorPool* GetDescriptorPool() {
- if (g_descriptor_pool == NULL) {
- g_descriptor_pool = new google::protobuf::DescriptorPool(
- google::protobuf::DescriptorPool::generated_pool());
- }
- return g_descriptor_pool;
-}
-
-PyObject* Python_NewCDescriptorPool(PyObject* ignored, PyObject* args) {
- CDescriptorPool* cdescriptor_pool = PyObject_New(
- CDescriptorPool, &CDescriptorPool_Type);
- if (cdescriptor_pool == NULL) {
- return NULL;
- }
- cdescriptor_pool->pool = GetDescriptorPool();
- return reinterpret_cast<PyObject*>(cdescriptor_pool);
-}
-
-PyObject* Python_BuildFile(PyObject* ignored, PyObject* arg) {
- char* message_type;
- Py_ssize_t message_len;
-
- if (PyString_AsStringAndSize(arg, &message_type, &message_len) < 0) {
- return NULL;
- }
-
- google::protobuf::FileDescriptorProto file_proto;
- if (!file_proto.ParseFromArray(message_type, message_len)) {
- PyErr_SetString(PyExc_TypeError, "Couldn't parse file content!");
- return NULL;
- }
-
- // If this file is already in the generated pool, don't add it again.
- if (google::protobuf::DescriptorPool::generated_pool()->FindFileByName(
- file_proto.name()) != NULL) {
- Py_RETURN_NONE;
- }
-
- const google::protobuf::FileDescriptor* descriptor = GetDescriptorPool()->BuildFile(
- file_proto);
- if (descriptor == NULL) {
- PyErr_SetString(PyExc_TypeError,
- "Couldn't build proto file into descriptor pool!");
- return NULL;
- }
-
- Py_RETURN_NONE;
-}
-
-bool InitDescriptor() {
- CFieldDescriptor_Type.tp_new = PyType_GenericNew;
- if (PyType_Ready(&CFieldDescriptor_Type) < 0)
- return false;
-
- CDescriptorPool_Type.tp_new = PyType_GenericNew;
- if (PyType_Ready(&CDescriptorPool_Type) < 0)
- return false;
- return true;
-}
-
-} // namespace python
-} // namespace protobuf
-} // namespace google
diff --git a/python/google/protobuf/pyext/python_protobuf.h b/python/google/protobuf/pyext/python_protobuf.h
index c5b0b1cd..beb6e460 100644
--- a/python/google/protobuf/pyext/python_protobuf.h
+++ b/python/google/protobuf/pyext/python_protobuf.h
@@ -1,6 +1,6 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
-// http://code.google.com/p/protobuf/
+// 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
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..0fe98e73
--- /dev/null
+++ b/python/google/protobuf/pyext/repeated_composite_container.cc
@@ -0,0 +1,776 @@
+// 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/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/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);
+
+// Returns a new reference.
+static PyObject* GetKey(PyObject* x) {
+ // Just the identity function.
+ Py_INCREF(x);
+ return x;
+}
+
+#define GET_KEY(keyfunc, value) \
+ ((keyfunc) == NULL ? \
+ GetKey((value)) : \
+ PyObject_CallFunctionObjArgs((keyfunc), (value), NULL))
+
+// Converts a comparison function that returns -1, 0, or 1 into a
+// less-than predicate.
+//
+// Returns -1 on error, 1 if x < y, 0 if x >= y.
+static int islt(PyObject *x, PyObject *y, PyObject *compare) {
+ if (compare == NULL)
+ return PyObject_RichCompareBool(x, y, Py_LT);
+
+ ScopedPyObjectPtr res(PyObject_CallFunctionObjArgs(compare, x, y, NULL));
+ if (res == NULL)
+ return -1;
+ if (!PyInt_Check(res)) {
+ PyErr_Format(PyExc_TypeError,
+ "comparison function must return int, not %.200s",
+ Py_TYPE(res)->tp_name);
+ return -1;
+ }
+ return PyInt_AsLong(res) < 0;
+}
+
+// Copied from uarrsort.c but swaps memcpy swaps with protobuf/python swaps
+// TODO(anuraag): Is there a better way to do this then reinventing the wheel?
+static int InternalQuickSort(RepeatedCompositeContainer* self,
+ Py_ssize_t start,
+ Py_ssize_t limit,
+ PyObject* cmp,
+ PyObject* keyfunc) {
+ if (limit - start <= 1)
+ return 0; // Nothing to sort.
+
+ GOOGLE_CHECK_ATTACHED(self);
+
+ Message* message = self->message;
+ const Reflection* reflection = message->GetReflection();
+ const FieldDescriptor* descriptor = self->parent_field_descriptor;
+ Py_ssize_t left;
+ Py_ssize_t right;
+
+ PyObject* children = self->child_messages;
+
+ do {
+ left = start;
+ right = limit;
+ ScopedPyObjectPtr mid(
+ GET_KEY(keyfunc, PyList_GET_ITEM(children, (start + limit) / 2)));
+ do {
+ ScopedPyObjectPtr key(GET_KEY(keyfunc, PyList_GET_ITEM(children, left)));
+ int is_lt = islt(key, mid, cmp);
+ if (is_lt == -1)
+ return -1;
+ /* array[left]<x */
+ while (is_lt) {
+ ++left;
+ ScopedPyObjectPtr key(GET_KEY(keyfunc,
+ PyList_GET_ITEM(children, left)));
+ is_lt = islt(key, mid, cmp);
+ if (is_lt == -1)
+ return -1;
+ }
+ key.reset(GET_KEY(keyfunc, PyList_GET_ITEM(children, right - 1)));
+ is_lt = islt(mid, key, cmp);
+ if (is_lt == -1)
+ return -1;
+ while (is_lt) {
+ --right;
+ ScopedPyObjectPtr key(GET_KEY(keyfunc,
+ PyList_GET_ITEM(children, right - 1)));
+ is_lt = islt(mid, key, cmp);
+ if (is_lt == -1)
+ return -1;
+ }
+ if (left < right) {
+ --right;
+ if (left < right) {
+ reflection->SwapElements(message, descriptor, left, right);
+ PyObject* tmp = PyList_GET_ITEM(children, left);
+ PyList_SET_ITEM(children, left, PyList_GET_ITEM(children, right));
+ PyList_SET_ITEM(children, right, tmp);
+ }
+ ++left;
+ }
+ } while (left < right);
+
+ if ((right - start) < (limit - left)) {
+ /* sort [start..right[ */
+ if (start < (right - 1)) {
+ InternalQuickSort(self, start, right, cmp, keyfunc);
+ }
+
+ /* sort [left..limit[ */
+ start = left;
+ } else {
+ /* sort [left..limit[ */
+ if (left < (limit - 1)) {
+ InternalQuickSort(self, left, limit, cmp, keyfunc);
+ }
+
+ /* sort [start..right[ */
+ limit = right;
+ }
+ } while (start < (limit - 1));
+
+ return 0;
+}
+
+#undef GET_KEY
+
+// ---------------------------------------------------------------------
+// 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->subclass_init,
+ sub_message.GetDescriptor());
+ 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) < 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->subclass_init,
+ sub_message->GetDescriptor());
+ 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->subclass_init, 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))) != NULL) {
+ if (!PyObject_TypeCheck(next, &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 (cmessage::MergeFrom(new_cmessage, next) == 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->message != NULL) {
+ if (cmessage::InternalDeleteRepeatedField(self->message,
+ 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, 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));
+ if (list == NULL) {
+ return NULL;
+ }
+ ScopedPyObjectPtr other_list(
+ Subscript(
+ reinterpret_cast<RepeatedCompositeContainer*>(other), full_slice));
+ if (other_list == NULL) {
+ return NULL;
+ }
+ return PyObject_RichCompare(list, other_list, opid);
+ } else {
+ Py_INCREF(Py_NotImplemented);
+ return Py_NotImplemented;
+ }
+}
+
+// ---------------------------------------------------------------------
+// sort()
+
+static PyObject* SortAttached(RepeatedCompositeContainer* self,
+ PyObject* args,
+ PyObject* kwds) {
+ // Sort the underlying Message array.
+ PyObject *compare = NULL;
+ int reverse = 0;
+ PyObject *keyfunc = NULL;
+ static char *kwlist[] = {"cmp", "key", "reverse", 0};
+
+ if (args != NULL) {
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOi:sort",
+ kwlist, &compare, &keyfunc, &reverse))
+ return NULL;
+ }
+ if (compare == Py_None)
+ compare = NULL;
+ if (keyfunc == Py_None)
+ keyfunc = NULL;
+
+ const Py_ssize_t length = Length(self);
+ if (InternalQuickSort(self, 0, length, compare, keyfunc) < 0)
+ return NULL;
+
+ // Finally reverse the result if requested.
+ if (reverse) {
+ Message* message = self->message;
+ const Reflection* reflection = message->GetReflection();
+ const FieldDescriptor* descriptor = self->parent_field_descriptor;
+
+ // Reverse the Message array.
+ for (int i = 0; i < length / 2; ++i)
+ reflection->SwapElements(message, descriptor, i, length - i - 1);
+
+ // Reverse the Python list.
+ ScopedPyObjectPtr res(PyObject_CallMethod(self->child_messages,
+ "reverse", NULL));
+ if (res == NULL)
+ return NULL;
+ }
+
+ Py_RETURN_NONE;
+}
+
+static PyObject* SortReleased(RepeatedCompositeContainer* self,
+ PyObject* args,
+ PyObject* kwds) {
+ ScopedPyObjectPtr m(PyObject_GetAttrString(self->child_messages, "sort"));
+ if (m == NULL)
+ return NULL;
+ if (PyObject_Call(m, args, kwds) == NULL)
+ return NULL;
+ Py_RETURN_NONE;
+}
+
+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 (self->message == NULL) {
+ return SortReleased(self, args, kwds);
+ } else {
+ return SortAttached(self, args, kwds);
+ }
+}
+
+// ---------------------------------------------------------------------
+
+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, NULL) < 0) {
+ return NULL;
+ }
+ return item;
+}
+
+// The caller takes ownership of the returned Message.
+Message* ReleaseLast(const FieldDescriptor* field,
+ const Descriptor* type,
+ Message* message) {
+ GOOGLE_CHECK_NOTNULL(field);
+ GOOGLE_CHECK_NOTNULL(type);
+ GOOGLE_CHECK_NOTNULL(message);
+
+ Message* released_message = message->GetReflection()->ReleaseLast(
+ message, field);
+ // TODO(tibell): Deal with proto1.
+
+ // 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 =
+ cmessage::GetMessageFactory()->GetPrototype(type);
+ GOOGLE_CHECK_NOTNULL(prototype);
+ return prototype->New();
+ } else {
+ return released_message;
+ }
+}
+
+// Release field of message and transfer the ownership to cmessage.
+void ReleaseLastTo(const FieldDescriptor* field,
+ Message* message,
+ CMessage* cmessage) {
+ GOOGLE_CHECK_NOTNULL(field);
+ GOOGLE_CHECK_NOTNULL(message);
+ GOOGLE_CHECK_NOTNULL(cmessage);
+
+ shared_ptr<Message> released_message(
+ ReleaseLast(field, cmessage->message->GetDescriptor(), message));
+ cmessage->parent = NULL;
+ cmessage->parent_field_descriptor = NULL;
+ cmessage->message = released_message.get();
+ cmessage->read_only = false;
+ cmessage::SetOwner(cmessage, 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(field, message, 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,
+ PyObject *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->subclass_init = 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->subclass_init);
+ // 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)
+ // Keep the fully qualified _message symbol in a line for opensource.
+ "google.protobuf.pyext._message."
+ "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
+ 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 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..ce7cee0f
--- /dev/null
+++ b/python/google/protobuf/pyext/repeated_composite_container.h
@@ -0,0 +1,177 @@
+// 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;
+
+using internal::shared_ptr;
+
+namespace python {
+
+struct CMessage;
+
+// 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;
+
+ // A callable that is used to create new child messages.
+ PyObject* subclass_init;
+
+ // 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,
+ PyObject *concrete_class);
+
+// Returns the number of items in this repeated composite container.
+static Py_ssize_t Length(RepeatedCompositeContainer* self);
+
+// 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 'message', and transfers the ownership of the released
+// Message to 'cmessage'.
+//
+// Corresponds to reflection api method ReleaseMessage.
+void ReleaseLastTo(const FieldDescriptor* field,
+ Message* message,
+ CMessage* cmessage);
+
+} // 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..110a4c85
--- /dev/null
+++ b/python/google/protobuf/pyext/repeated_scalar_container.cc
@@ -0,0 +1,813 @@
+// 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/descriptor.h>
+#include <google/protobuf/dynamic_message.h>
+#include <google/protobuf/message.h>
+#include <google/protobuf/pyext/descriptor.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 (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(message, field_descriptor,
+ py_index, 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));
+ }
+ 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);
+ }
+ } 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);
+ }
+ }
+ 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));
+ }
+ 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(
+ message, 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));
+ if (new_list == NULL) {
+ return -1;
+ }
+ if (PySequence_SetSlice(new_list, from, to, value) < 0) {
+ return -1;
+ }
+
+ return InternalAssignRepeatedField(self, new_list);
+}
+
+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))) != NULL) {
+ if (Append(self, next) == 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));
+ if (PyList_Insert(new_list, index, value) < 0) {
+ return NULL;
+ }
+ int ret = InternalAssignRepeatedField(self, new_list);
+ 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, 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));
+ other = other_list_deleter.get();
+ }
+
+ ScopedPyObjectPtr list(Subscript(self, full_slice));
+ if (list == NULL) {
+ return NULL;
+ }
+ return PyObject_RichCompare(list, 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));
+ if (list == NULL) {
+ return NULL;
+ }
+ ScopedPyObjectPtr m(PyObject_GetAttrString(list, "sort"));
+ if (m == NULL) {
+ return NULL;
+ }
+ ScopedPyObjectPtr res(PyObject_Call(m, args, kwds));
+ if (res == NULL) {
+ return NULL;
+ }
+ int ret = InternalAssignRepeatedField(self, list);
+ 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));
+ if (values == NULL) {
+ return -1;
+ }
+ Message* new_message = cmessage::GetMessageFactory()->GetPrototype(
+ from->message->GetDescriptor())->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) < 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)
+ // Keep the fully qualified _message symbol in a line for opensource.
+ "google.protobuf.pyext._message."
+ "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
+ 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 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..5dfa21e0
--- /dev/null
+++ b/python/google/protobuf/pyext/repeated_scalar_container.h
@@ -0,0 +1,118 @@
+// 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;
+
+using internal::shared_ptr;
+
+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..18ddd5cd
--- /dev/null
+++ b/python/google/protobuf/pyext/scoped_pyobject_ptr.h
@@ -0,0 +1,97 @@
+// 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->reset(this->get()) works.
+ PyObject* reset(PyObject* p = NULL) {
+ if (p != ptr_) {
+ Py_XDECREF(ptr_);
+ ptr_ = p;
+ }
+ return ptr_;
+ }
+
+ // Releases ownership of the object.
+ PyObject* release() {
+ PyObject* p = ptr_;
+ ptr_ = NULL;
+ return p;
+ }
+
+ operator PyObject*() { return ptr_; }
+
+ 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__
diff --git a/python/google/protobuf/reflection.py b/python/google/protobuf/reflection.py
index 1373c882..55e653a0 100755
--- a/python/google/protobuf/reflection.py
+++ b/python/google/protobuf/reflection.py
@@ -1,6 +1,6 @@
# Protocol Buffers - Google's data interchange format
# Copyright 2008 Google Inc. All rights reserved.
-# http://code.google.com/p/protobuf/
+# 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
@@ -50,17 +50,18 @@ __author__ = 'robinson@google.com (Will Robinson)'
from google.protobuf.internal import api_implementation
from google.protobuf import descriptor as descriptor_mod
+from google.protobuf import message
+
_FieldDescriptor = descriptor_mod.FieldDescriptor
if api_implementation.Type() == 'cpp':
- from google.protobuf.internal import cpp_message
- _NewMessage = cpp_message.NewMessage
- _InitMessage = cpp_message.InitMessage
+ from google.protobuf.pyext import cpp_message as message_impl
else:
- from google.protobuf.internal import python_message
- _NewMessage = python_message.NewMessage
- _InitMessage = python_message.InitMessage
+ from google.protobuf.internal import python_message as message_impl
+
+_NewMessage = message_impl.NewMessage
+_InitMessage = message_impl.InitMessage
class GeneratedProtocolMessageType(type):
@@ -84,6 +85,10 @@ class GeneratedProtocolMessageType(type):
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
@@ -112,11 +117,10 @@ class GeneratedProtocolMessageType(type):
Newly-allocated class.
"""
descriptor = dictionary[GeneratedProtocolMessageType._DESCRIPTOR_KEY]
- _NewMessage(descriptor, dictionary)
+ bases = _NewMessage(bases, descriptor, dictionary)
superclass = super(GeneratedProtocolMessageType, cls)
new_class = superclass.__new__(cls, name, bases, dictionary)
- setattr(descriptor, '_concrete_class', new_class)
return new_class
def __init__(cls, name, bases, dictionary):
@@ -140,3 +144,56 @@ class GeneratedProtocolMessageType(type):
_InitMessage(descriptor, cls)
superclass = super(GeneratedProtocolMessageType, cls)
superclass.__init__(name, bases, dictionary)
+ setattr(descriptor, '_concrete_class', cls)
+
+
+def ParseMessage(descriptor, byte_str):
+ """Generate a new Message instance from this Descriptor and a byte string.
+
+ Args:
+ descriptor: Protobuf Descriptor object
+ byte_str: Serialized protocol buffer byte string
+
+ Returns:
+ Newly created protobuf Message object.
+ """
+ result_class = MakeClass(descriptor)
+ new_msg = result_class()
+ new_msg.ParseFromString(byte_str)
+ return new_msg
+
+
+def MakeClass(descriptor):
+ """Construct a class object for a protobuf described by descriptor.
+
+ Composite descriptors are handled by defining the new class as a member of the
+ parent class, recursing as deep as necessary.
+ This is the dynamic equivalent to:
+
+ class Parent(message.Message):
+ __metaclass__ = GeneratedProtocolMessageType
+ DESCRIPTOR = descriptor
+ class Child(message.Message):
+ __metaclass__ = GeneratedProtocolMessageType
+ DESCRIPTOR = descriptor.nested_types[0]
+
+ Sample usage:
+ file_descriptor = descriptor_pb2.FileDescriptorProto()
+ file_descriptor.ParseFromString(proto2_string)
+ msg_descriptor = descriptor.MakeDescriptor(file_descriptor.message_type[0])
+ msg_class = reflection.MakeClass(msg_descriptor)
+ msg = msg_class()
+
+ Args:
+ descriptor: A descriptor.Descriptor object describing the protobuf.
+ Returns:
+ The Message class object described by the descriptor.
+ """
+ attributes = {}
+ for name, nested_type in descriptor.nested_types_by_name.items():
+ attributes[name] = MakeClass(nested_type)
+
+ attributes[GeneratedProtocolMessageType._DESCRIPTOR_KEY] = descriptor
+
+ return GeneratedProtocolMessageType(str(descriptor.name), (message.Message,),
+ attributes)
diff --git a/python/google/protobuf/service.py b/python/google/protobuf/service.py
index 180b70e8..9e00de70 100755
--- a/python/google/protobuf/service.py
+++ b/python/google/protobuf/service.py
@@ -1,6 +1,6 @@
# Protocol Buffers - Google's data interchange format
# Copyright 2008 Google Inc. All rights reserved.
-# http://code.google.com/p/protobuf/
+# 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
diff --git a/python/google/protobuf/service_reflection.py b/python/google/protobuf/service_reflection.py
index 851e83e7..1c3636af 100755
--- a/python/google/protobuf/service_reflection.py
+++ b/python/google/protobuf/service_reflection.py
@@ -1,6 +1,6 @@
# Protocol Buffers - Google's data interchange format
# Copyright 2008 Google Inc. All rights reserved.
-# http://code.google.com/p/protobuf/
+# 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
diff --git a/python/google/protobuf/symbol_database.py b/python/google/protobuf/symbol_database.py
new file mode 100644
index 00000000..4c70b393
--- /dev/null
+++ b/python/google/protobuf/symbol_database.py
@@ -0,0 +1,185 @@
+# 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.
+
+"""A database of Python protocol buffer generated symbols.
+
+SymbolDatabase makes it easy to create new instances of a registered type, given
+only the type's protocol buffer symbol name. Once all symbols are registered,
+they can be accessed using either the MessageFactory interface which
+SymbolDatabase exposes, or the DescriptorPool interface of the underlying
+pool.
+
+Example usage:
+
+ db = symbol_database.SymbolDatabase()
+
+ # Register symbols of interest, from one or multiple files.
+ db.RegisterFileDescriptor(my_proto_pb2.DESCRIPTOR)
+ db.RegisterMessage(my_proto_pb2.MyMessage)
+ db.RegisterEnumDescriptor(my_proto_pb2.MyEnum.DESCRIPTOR)
+
+ # The database can be used as a MessageFactory, to generate types based on
+ # their name:
+ types = db.GetMessages(['my_proto.proto'])
+ my_message_instance = types['MyMessage']()
+
+ # The database's underlying descriptor pool can be queried, so it's not
+ # necessary to know a type's filename to be able to generate it:
+ filename = db.pool.FindFileContainingSymbol('MyMessage')
+ my_message_instance = db.GetMessages([filename])['MyMessage']()
+
+ # This functionality is also provided directly via a convenience method:
+ my_message_instance = db.GetSymbol('MyMessage')()
+"""
+
+
+from google.protobuf import descriptor_pool
+
+
+class SymbolDatabase(object):
+ """A database of Python generated symbols.
+
+ SymbolDatabase also models message_factory.MessageFactory.
+
+ The symbol database can be used to keep a global registry of all protocol
+ buffer types used within a program.
+ """
+
+ def __init__(self):
+ """Constructor."""
+
+ self._symbols = {}
+ self._symbols_by_file = {}
+ self.pool = descriptor_pool.DescriptorPool()
+
+ def RegisterMessage(self, message):
+ """Registers the given message type in the local database.
+
+ Args:
+ message: a message.Message, to be registered.
+
+ Returns:
+ The provided message.
+ """
+
+ desc = message.DESCRIPTOR
+ self._symbols[desc.full_name] = message
+ if desc.file.name not in self._symbols_by_file:
+ self._symbols_by_file[desc.file.name] = {}
+ self._symbols_by_file[desc.file.name][desc.full_name] = message
+ self.pool.AddDescriptor(desc)
+ return message
+
+ def RegisterEnumDescriptor(self, enum_descriptor):
+ """Registers the given enum descriptor in the local database.
+
+ Args:
+ enum_descriptor: a descriptor.EnumDescriptor.
+
+ Returns:
+ The provided descriptor.
+ """
+ self.pool.AddEnumDescriptor(enum_descriptor)
+ return enum_descriptor
+
+ def RegisterFileDescriptor(self, file_descriptor):
+ """Registers the given file descriptor in the local database.
+
+ Args:
+ file_descriptor: a descriptor.FileDescriptor.
+
+ Returns:
+ The provided descriptor.
+ """
+ self.pool.AddFileDescriptor(file_descriptor)
+
+ def GetSymbol(self, symbol):
+ """Tries to find a symbol in the local database.
+
+ Currently, this method only returns message.Message instances, however, if
+ may be extended in future to support other symbol types.
+
+ Args:
+ symbol: A str, a protocol buffer symbol.
+
+ Returns:
+ A Python class corresponding to the symbol.
+
+ Raises:
+ KeyError: if the symbol could not be found.
+ """
+
+ return self._symbols[symbol]
+
+ def GetPrototype(self, descriptor):
+ """Builds a proto2 message class based on the passed in descriptor.
+
+ Passing a descriptor with a fully qualified name matching a previous
+ invocation will cause the same class to be returned.
+
+ Args:
+ descriptor: The descriptor to build from.
+
+ Returns:
+ A class describing the passed in descriptor.
+ """
+
+ return self.GetSymbol(descriptor.full_name)
+
+ def GetMessages(self, files):
+ """Gets all the messages from a specified file.
+
+ This will find and resolve dependencies, failing if they are not registered
+ in the symbol database.
+
+
+ Args:
+ files: The file names to extract messages from.
+
+ Returns:
+ A dictionary mapping proto names to the message classes. This will include
+ any dependent messages as well as any messages defined in the same file as
+ a specified message.
+
+ Raises:
+ KeyError: if a file could not be found.
+ """
+
+ result = {}
+ for f in files:
+ result.update(self._symbols_by_file[f])
+ return result
+
+_DEFAULT = SymbolDatabase()
+
+
+def Default():
+ """Returns the default SymbolDatabase."""
+ return _DEFAULT
diff --git a/python/google/protobuf/text_encoding.py b/python/google/protobuf/text_encoding.py
new file mode 100644
index 00000000..2d86a67c
--- /dev/null
+++ b/python/google/protobuf/text_encoding.py
@@ -0,0 +1,110 @@
+# 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.
+
+#PY25 compatible for GAE.
+#
+"""Encoding related utilities."""
+
+import re
+import sys ##PY25
+
+# Lookup table for utf8
+_cescape_utf8_to_str = [chr(i) for i in xrange(0, 256)]
+_cescape_utf8_to_str[9] = r'\t' # optional escape
+_cescape_utf8_to_str[10] = r'\n' # optional escape
+_cescape_utf8_to_str[13] = r'\r' # optional escape
+_cescape_utf8_to_str[39] = r"\'" # optional escape
+
+_cescape_utf8_to_str[34] = r'\"' # necessary escape
+_cescape_utf8_to_str[92] = r'\\' # necessary escape
+
+# Lookup table for non-utf8, with necessary escapes at (o >= 127 or o < 32)
+_cescape_byte_to_str = ([r'\%03o' % i for i in xrange(0, 32)] +
+ [chr(i) for i in xrange(32, 127)] +
+ [r'\%03o' % i for i in xrange(127, 256)])
+_cescape_byte_to_str[9] = r'\t' # optional escape
+_cescape_byte_to_str[10] = r'\n' # optional escape
+_cescape_byte_to_str[13] = r'\r' # optional escape
+_cescape_byte_to_str[39] = r"\'" # optional escape
+
+_cescape_byte_to_str[34] = r'\"' # necessary escape
+_cescape_byte_to_str[92] = r'\\' # necessary escape
+
+
+def CEscape(text, as_utf8):
+ """Escape a bytes string for use in an ascii protocol buffer.
+
+ text.encode('string_escape') does not seem to satisfy our needs as it
+ encodes unprintable characters using two-digit hex escapes whereas our
+ C++ unescaping function allows hex escapes to be any length. So,
+ "\0011".encode('string_escape') ends up being "\\x011", which will be
+ decoded in C++ as a single-character string with char code 0x11.
+
+ Args:
+ text: A byte string to be escaped
+ as_utf8: Specifies if result should be returned in UTF-8 encoding
+ Returns:
+ Escaped string
+ """
+ # PY3 hack: make Ord work for str and bytes:
+ # //platforms/networking/data uses unicode here, hence basestring.
+ Ord = ord if isinstance(text, basestring) else lambda x: x
+ if as_utf8:
+ return ''.join(_cescape_utf8_to_str[Ord(c)] for c in text)
+ return ''.join(_cescape_byte_to_str[Ord(c)] for c in text)
+
+
+_CUNESCAPE_HEX = re.compile(r'(\\+)x([0-9a-fA-F])(?![0-9a-fA-F])')
+_cescape_highbit_to_str = ([chr(i) for i in range(0, 127)] +
+ [r'\%03o' % i for i in range(127, 256)])
+
+
+def CUnescape(text):
+ """Unescape a text string with C-style escape sequences to UTF-8 bytes."""
+
+ def ReplaceHex(m):
+ # Only replace the match if the number of leading back slashes is odd. i.e.
+ # the slash itself is not escaped.
+ if len(m.group(1)) & 1:
+ return m.group(1) + 'x0' + m.group(2)
+ return m.group(0)
+
+ # This is required because the 'string_escape' encoding doesn't
+ # allow single-digit hex escapes (like '\xf').
+ result = _CUNESCAPE_HEX.sub(ReplaceHex, text)
+
+ if sys.version_info[0] < 3: ##PY25
+##!PY25 if str is bytes: # PY2
+ return result.decode('string_escape')
+ result = ''.join(_cescape_highbit_to_str[ord(c)] for c in result)
+ return (result.encode('ascii') # Make it bytes to allow decode.
+ .decode('unicode_escape')
+ # Make it bytes again to return the proper type.
+ .encode('raw_unicode_escape'))
diff --git a/python/google/protobuf/text_format.py b/python/google/protobuf/text_format.py
index c3a1cf60..a47ce3e3 100755
--- a/python/google/protobuf/text_format.py
+++ b/python/google/protobuf/text_format.py
@@ -1,6 +1,6 @@
# Protocol Buffers - Google's data interchange format
# Copyright 2008 Google Inc. All rights reserved.
-# http://code.google.com/p/protobuf/
+# 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
@@ -28,6 +28,10 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#PY25 compatible for GAE.
+#
+# Copyright 2007 Google Inc. All Rights Reserved.
+
"""Contains routines for printing protocol messages in text format."""
__author__ = 'kenton@google.com (Kenton Varda)'
@@ -35,27 +39,61 @@ __author__ = 'kenton@google.com (Kenton Varda)'
import cStringIO
import re
-from collections import deque
from google.protobuf.internal import type_checkers
from google.protobuf import descriptor
+from google.protobuf import text_encoding
+
+__all__ = ['MessageToString', 'PrintMessage', 'PrintField',
+ 'PrintFieldValue', 'Merge']
-__all__ = [ 'MessageToString', 'PrintMessage', 'PrintField',
- 'PrintFieldValue', 'Merge' ]
+_INTEGER_CHECKERS = (type_checkers.Uint32ValueChecker(),
+ type_checkers.Int32ValueChecker(),
+ type_checkers.Uint64ValueChecker(),
+ type_checkers.Int64ValueChecker())
+_FLOAT_INFINITY = re.compile('-?inf(?:inity)?f?', re.IGNORECASE)
+_FLOAT_NAN = re.compile('nanf?', re.IGNORECASE)
+_FLOAT_TYPES = frozenset([descriptor.FieldDescriptor.CPPTYPE_FLOAT,
+ descriptor.FieldDescriptor.CPPTYPE_DOUBLE])
-# Infinity and NaN are not explicitly supported by Python pre-2.6, and
-# float('inf') does not work on Windows (pre-2.6).
-_INFINITY = 1e10000 # overflows, thus will actually be infinity.
-_NAN = _INFINITY * 0
+class Error(Exception):
+ """Top-level module error for text_format."""
-class ParseError(Exception):
+
+class ParseError(Error):
"""Thrown in case of ASCII parsing error."""
-def MessageToString(message, as_utf8=False, as_one_line=False):
+def MessageToString(message, as_utf8=False, as_one_line=False,
+ pointy_brackets=False, use_index_order=False,
+ float_format=None):
+ """Convert protobuf message to text format.
+
+ Floating point values can be formatted compactly with 15 digits of
+ precision (which is the most that IEEE 754 "double" can guarantee)
+ using float_format='.15g'.
+
+ Args:
+ message: The protocol buffers message.
+ as_utf8: Produce text output in UTF8 format.
+ as_one_line: Don't introduce newlines between fields.
+ pointy_brackets: If True, use angle brackets instead of curly braces for
+ nesting.
+ use_index_order: If True, print fields of a proto message using the order
+ defined in source code instead of the field number. By default, use the
+ field number order.
+ float_format: If set, use this to specify floating point number formatting
+ (per the "Format Specification Mini-Language"); otherwise, str() is used.
+
+ Returns:
+ A string of the text formatted protocol buffer message.
+ """
out = cStringIO.StringIO()
- PrintMessage(message, out, as_utf8=as_utf8, as_one_line=as_one_line)
+ PrintMessage(message, out, as_utf8=as_utf8, as_one_line=as_one_line,
+ pointy_brackets=pointy_brackets,
+ use_index_order=use_index_order,
+ float_format=float_format)
result = out.getvalue()
out.close()
if as_one_line:
@@ -63,20 +101,32 @@ def MessageToString(message, as_utf8=False, as_one_line=False):
return result
-def PrintMessage(message, out, indent=0, as_utf8=False, as_one_line=False):
- for field, value in message.ListFields():
+def PrintMessage(message, out, indent=0, as_utf8=False, as_one_line=False,
+ pointy_brackets=False, use_index_order=False,
+ float_format=None):
+ fields = message.ListFields()
+ if use_index_order:
+ fields.sort(key=lambda x: x[0].index)
+ for field, value in fields:
if field.label == descriptor.FieldDescriptor.LABEL_REPEATED:
for element in value:
- PrintField(field, element, out, indent, as_utf8, as_one_line)
+ PrintField(field, element, out, indent, as_utf8, as_one_line,
+ pointy_brackets=pointy_brackets,
+ use_index_order=use_index_order,
+ float_format=float_format)
else:
- PrintField(field, value, out, indent, as_utf8, as_one_line)
+ PrintField(field, value, out, indent, as_utf8, as_one_line,
+ pointy_brackets=pointy_brackets,
+ use_index_order=use_index_order,
+ float_format=float_format)
-def PrintField(field, value, out, indent=0, as_utf8=False, as_one_line=False):
+def PrintField(field, value, out, indent=0, as_utf8=False, as_one_line=False,
+ pointy_brackets=False, use_index_order=False, float_format=None):
"""Print a single field name/value pair. For repeated fields, the value
should be a single element."""
- out.write(' ' * indent);
+ out.write(' ' * indent)
if field.is_extension:
out.write('[')
if (field.containing_type.GetOptions().message_set_wire_format and
@@ -98,71 +148,182 @@ def PrintField(field, value, out, indent=0, as_utf8=False, as_one_line=False):
# don't include it.
out.write(': ')
- PrintFieldValue(field, value, out, indent, as_utf8, as_one_line)
+ PrintFieldValue(field, value, out, indent, as_utf8, as_one_line,
+ pointy_brackets=pointy_brackets,
+ use_index_order=use_index_order,
+ float_format=float_format)
if as_one_line:
out.write(' ')
else:
out.write('\n')
-def PrintFieldValue(field, value, out, indent=0,
- as_utf8=False, as_one_line=False):
+def PrintFieldValue(field, value, out, indent=0, as_utf8=False,
+ as_one_line=False, pointy_brackets=False,
+ use_index_order=False,
+ float_format=None):
"""Print a single field value (not including name). For repeated fields,
the value should be a single element."""
+ if pointy_brackets:
+ openb = '<'
+ closeb = '>'
+ else:
+ openb = '{'
+ closeb = '}'
+
if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE:
if as_one_line:
- out.write(' { ')
- PrintMessage(value, out, indent, as_utf8, as_one_line)
- out.write('}')
+ out.write(' %s ' % openb)
+ PrintMessage(value, out, indent, as_utf8, as_one_line,
+ pointy_brackets=pointy_brackets,
+ use_index_order=use_index_order,
+ float_format=float_format)
+ out.write(closeb)
else:
- out.write(' {\n')
- PrintMessage(value, out, indent + 2, as_utf8, as_one_line)
- out.write(' ' * indent + '}')
+ out.write(' %s\n' % openb)
+ PrintMessage(value, out, indent + 2, as_utf8, as_one_line,
+ pointy_brackets=pointy_brackets,
+ use_index_order=use_index_order,
+ float_format=float_format)
+ out.write(' ' * indent + closeb)
elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_ENUM:
- out.write(field.enum_type.values_by_number[value].name)
+ enum_value = field.enum_type.values_by_number.get(value, None)
+ if enum_value is not None:
+ out.write(enum_value.name)
+ else:
+ out.write(str(value))
elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_STRING:
out.write('\"')
- if type(value) is unicode:
- out.write(_CEscape(value.encode('utf-8'), as_utf8))
+ if isinstance(value, unicode):
+ out_value = value.encode('utf-8')
else:
- out.write(_CEscape(value, as_utf8))
+ out_value = value
+ if field.type == descriptor.FieldDescriptor.TYPE_BYTES:
+ # We need to escape non-UTF8 chars in TYPE_BYTES field.
+ out_as_utf8 = False
+ else:
+ out_as_utf8 = as_utf8
+ out.write(text_encoding.CEscape(out_value, out_as_utf8))
out.write('\"')
elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_BOOL:
if value:
- out.write("true")
+ out.write('true')
else:
- out.write("false")
+ out.write('false')
+ elif field.cpp_type in _FLOAT_TYPES and float_format is not None:
+ out.write('{1:{0}}'.format(float_format, value))
else:
out.write(str(value))
+def Parse(text, message):
+ """Parses an ASCII representation of a protocol message into a message.
+
+ Args:
+ text: Message ASCII representation.
+ message: A protocol buffer message to merge into.
+
+ Returns:
+ The same message passed as argument.
+
+ Raises:
+ ParseError: On ASCII parsing problems.
+ """
+ if not isinstance(text, str): text = text.decode('utf-8')
+ return ParseLines(text.split('\n'), message)
+
+
def Merge(text, message):
- """Merges an ASCII representation of a protocol message into a message.
+ """Parses an ASCII representation of a protocol message into a message.
+
+ Like Parse(), but allows repeated values for a non-repeated field, and uses
+ the last one.
Args:
text: Message ASCII representation.
message: A protocol buffer message to merge into.
+ Returns:
+ The same message passed as argument.
+
Raises:
ParseError: On ASCII parsing problems.
"""
- tokenizer = _Tokenizer(text)
+ return MergeLines(text.split('\n'), message)
+
+
+def ParseLines(lines, message):
+ """Parses an ASCII representation of a protocol message into a message.
+
+ Args:
+ lines: An iterable of lines of a message's ASCII representation.
+ message: A protocol buffer message to merge into.
+
+ Returns:
+ The same message passed as argument.
+
+ Raises:
+ ParseError: On ASCII parsing problems.
+ """
+ _ParseOrMerge(lines, message, False)
+ return message
+
+
+def MergeLines(lines, message):
+ """Parses an ASCII representation of a protocol message into a message.
+
+ Args:
+ lines: An iterable of lines of a message's ASCII representation.
+ message: A protocol buffer message to merge into.
+
+ Returns:
+ The same message passed as argument.
+
+ Raises:
+ ParseError: On ASCII parsing problems.
+ """
+ _ParseOrMerge(lines, message, True)
+ return message
+
+
+def _ParseOrMerge(lines, message, allow_multiple_scalars):
+ """Converts an ASCII representation of a protocol message into a message.
+
+ Args:
+ lines: Lines of a message's ASCII representation.
+ message: A protocol buffer message to merge into.
+ allow_multiple_scalars: Determines if repeated values for a non-repeated
+ field are permitted, e.g., the string "foo: 1 foo: 2" for a
+ required/optional field named "foo".
+
+ Raises:
+ ParseError: On ASCII parsing problems.
+ """
+ tokenizer = _Tokenizer(lines)
while not tokenizer.AtEnd():
- _MergeField(tokenizer, message)
+ _MergeField(tokenizer, message, allow_multiple_scalars)
-def _MergeField(tokenizer, message):
+def _MergeField(tokenizer, message, allow_multiple_scalars):
"""Merges a single protocol message field into a message.
Args:
tokenizer: A tokenizer to parse the field name and values.
message: A protocol message to record the data.
+ allow_multiple_scalars: Determines if repeated values for a non-repeated
+ field are permitted, e.g., the string "foo: 1 foo: 2" for a
+ required/optional field named "foo".
Raises:
ParseError: In case of ASCII parsing problems.
"""
message_descriptor = message.DESCRIPTOR
+ if (hasattr(message_descriptor, 'syntax') and
+ message_descriptor.syntax == 'proto3'):
+ # Proto3 doesn't represent presence so we can't test if multiple
+ # scalars have occurred. We have to allow them.
+ allow_multiple_scalars = True
if tokenizer.TryConsume('['):
name = [tokenizer.ConsumeIdentifier()]
while tokenizer.TryConsume('.'):
@@ -173,7 +334,9 @@ def _MergeField(tokenizer, message):
raise tokenizer.ParseErrorPreviousToken(
'Message type "%s" does not have extensions.' %
message_descriptor.full_name)
+ # pylint: disable=protected-access
field = message.Extensions._FindExtensionByName(name)
+ # pylint: enable=protected-access
if not field:
raise tokenizer.ParseErrorPreviousToken(
'Extension "%s" not registered.' % name)
@@ -227,18 +390,26 @@ def _MergeField(tokenizer, message):
while not tokenizer.TryConsume(end_token):
if tokenizer.AtEnd():
raise tokenizer.ParseErrorPreviousToken('Expected "%s".' % (end_token))
- _MergeField(tokenizer, sub_message)
+ _MergeField(tokenizer, sub_message, allow_multiple_scalars)
else:
- _MergeScalarField(tokenizer, message, field)
+ _MergeScalarField(tokenizer, message, field, allow_multiple_scalars)
+
+ # For historical reasons, fields may optionally be separated by commas or
+ # semicolons.
+ if not tokenizer.TryConsume(','):
+ tokenizer.TryConsume(';')
-def _MergeScalarField(tokenizer, message, field):
+def _MergeScalarField(tokenizer, message, field, allow_multiple_scalars):
"""Merges a single protocol message scalar field into a message.
Args:
tokenizer: A tokenizer to parse the field value.
message: A protocol message to record the data.
field: The descriptor of the field to be merged.
+ allow_multiple_scalars: Determines if repeated values for a non-repeated
+ field are permitted, e.g., the string "foo: 1 foo: 2" for a
+ required/optional field named "foo".
Raises:
ParseError: In case of ASCII parsing problems.
@@ -271,24 +442,7 @@ def _MergeScalarField(tokenizer, message, field):
elif field.type == descriptor.FieldDescriptor.TYPE_BYTES:
value = tokenizer.ConsumeByteString()
elif field.type == descriptor.FieldDescriptor.TYPE_ENUM:
- # Enum can be specified by a number (the enum value), or by
- # a string literal (the enum name).
- enum_descriptor = field.enum_type
- if tokenizer.LookingAtInteger():
- number = tokenizer.ConsumeInt32()
- enum_value = enum_descriptor.values_by_number.get(number, None)
- if enum_value is None:
- raise tokenizer.ParseErrorPreviousToken(
- 'Enum type "%s" has no value with number %d.' % (
- enum_descriptor.full_name, number))
- else:
- identifier = tokenizer.ConsumeIdentifier()
- enum_value = enum_descriptor.values_by_name.get(identifier, None)
- if enum_value is None:
- raise tokenizer.ParseErrorPreviousToken(
- 'Enum type "%s" has no value named %s.' % (
- enum_descriptor.full_name, identifier))
- value = enum_value.number
+ value = tokenizer.ConsumeEnum(field)
else:
raise RuntimeError('Unknown field type %d' % field.type)
@@ -299,9 +453,19 @@ def _MergeScalarField(tokenizer, message, field):
getattr(message, field.name).append(value)
else:
if field.is_extension:
- message.Extensions[field] = value
+ if not allow_multiple_scalars and message.HasExtension(field):
+ raise tokenizer.ParseErrorPreviousToken(
+ 'Message type "%s" should not have multiple "%s" extensions.' %
+ (message.DESCRIPTOR.full_name, field.full_name))
+ else:
+ message.Extensions[field] = value
else:
- setattr(message, field.name, value)
+ if not allow_multiple_scalars and message.HasField(field.name):
+ raise tokenizer.ParseErrorPreviousToken(
+ 'Message type "%s" should not have multiple "%s" fields.' %
+ (message.DESCRIPTOR.full_name, field.name))
+ else:
+ setattr(message, field.name, value)
class _Tokenizer(object):
@@ -319,26 +483,19 @@ class _Tokenizer(object):
'[0-9+-][0-9a-zA-Z_.+-]*|' # a number
'\"([^\"\n\\\\]|\\\\.)*(\"|\\\\?$)|' # a double-quoted string
'\'([^\'\n\\\\]|\\\\.)*(\'|\\\\?$)') # a single-quoted string
- _IDENTIFIER = re.compile('\w+')
- _INTEGER_CHECKERS = [type_checkers.Uint32ValueChecker(),
- type_checkers.Int32ValueChecker(),
- type_checkers.Uint64ValueChecker(),
- type_checkers.Int64ValueChecker()]
- _FLOAT_INFINITY = re.compile('-?inf(inity)?f?', re.IGNORECASE)
- _FLOAT_NAN = re.compile("nanf?", re.IGNORECASE)
-
- def __init__(self, text_message):
- self._text_message = text_message
+ _IDENTIFIER = re.compile(r'\w+')
+ def __init__(self, lines):
self._position = 0
self._line = -1
self._column = 0
self._token_start = None
self.token = ''
- self._lines = deque(text_message.split('\n'))
+ self._lines = iter(lines)
self._current_line = ''
self._previous_line = 0
self._previous_column = 0
+ self._more_lines = True
self._SkipWhitespace()
self.NextToken()
@@ -348,16 +505,19 @@ class _Tokenizer(object):
Returns:
True iff the end was reached.
"""
- return self.token == ''
+ return not self.token
def _PopLine(self):
while len(self._current_line) <= self._column:
- if not self._lines:
+ try:
+ self._current_line = self._lines.next()
+ except StopIteration:
self._current_line = ''
+ self._more_lines = False
return
- self._line += 1
- self._column = 0
- self._current_line = self._lines.popleft()
+ else:
+ self._line += 1
+ self._column = 0
def _SkipWhitespace(self):
while True:
@@ -394,17 +554,6 @@ class _Tokenizer(object):
if not self.TryConsume(token):
raise self._ParseError('Expected "%s".' % token)
- def LookingAtInteger(self):
- """Checks if the current token is an integer.
-
- Returns:
- True iff the current token is an integer.
- """
- if not self.token:
- return False
- c = self.token[0]
- return (c >= '0' and c <= '9') or c == '-' or c == '+'
-
def ConsumeIdentifier(self):
"""Consumes protocol message field identifier.
@@ -430,9 +579,9 @@ class _Tokenizer(object):
ParseError: If a signed 32bit integer couldn't be consumed.
"""
try:
- result = self._ParseInteger(self.token, is_signed=True, is_long=False)
+ result = ParseInteger(self.token, is_signed=True, is_long=False)
except ValueError, e:
- raise self._IntegerParseError(e)
+ raise self._ParseError(str(e))
self.NextToken()
return result
@@ -446,9 +595,9 @@ class _Tokenizer(object):
ParseError: If an unsigned 32bit integer couldn't be consumed.
"""
try:
- result = self._ParseInteger(self.token, is_signed=False, is_long=False)
+ result = ParseInteger(self.token, is_signed=False, is_long=False)
except ValueError, e:
- raise self._IntegerParseError(e)
+ raise self._ParseError(str(e))
self.NextToken()
return result
@@ -462,9 +611,9 @@ class _Tokenizer(object):
ParseError: If a signed 64bit integer couldn't be consumed.
"""
try:
- result = self._ParseInteger(self.token, is_signed=True, is_long=True)
+ result = ParseInteger(self.token, is_signed=True, is_long=True)
except ValueError, e:
- raise self._IntegerParseError(e)
+ raise self._ParseError(str(e))
self.NextToken()
return result
@@ -478,9 +627,9 @@ class _Tokenizer(object):
ParseError: If an unsigned 64bit integer couldn't be consumed.
"""
try:
- result = self._ParseInteger(self.token, is_signed=False, is_long=True)
+ result = ParseInteger(self.token, is_signed=False, is_long=True)
except ValueError, e:
- raise self._IntegerParseError(e)
+ raise self._ParseError(str(e))
self.NextToken()
return result
@@ -493,21 +642,10 @@ class _Tokenizer(object):
Raises:
ParseError: If a floating point number couldn't be consumed.
"""
- text = self.token
- if self._FLOAT_INFINITY.match(text):
- self.NextToken()
- if text.startswith('-'):
- return -_INFINITY
- return _INFINITY
-
- if self._FLOAT_NAN.match(text):
- self.NextToken()
- return _NAN
-
try:
- result = float(text)
+ result = ParseFloat(self.token)
except ValueError, e:
- raise self._FloatParseError(e)
+ raise self._ParseError(str(e))
self.NextToken()
return result
@@ -520,14 +658,12 @@ class _Tokenizer(object):
Raises:
ParseError: If a boolean value couldn't be consumed.
"""
- if self.token in ('true', 't', '1'):
- self.NextToken()
- return True
- elif self.token in ('false', 'f', '0'):
- self.NextToken()
- return False
- else:
- raise self._ParseError('Expected "true" or "false".')
+ try:
+ result = ParseBool(self.token)
+ except ValueError, e:
+ raise self._ParseError(str(e))
+ self.NextToken()
+ return result
def ConsumeString(self):
"""Consumes a string value.
@@ -538,9 +674,9 @@ class _Tokenizer(object):
Raises:
ParseError: If a string value couldn't be consumed.
"""
- bytes = self.ConsumeByteString()
+ the_bytes = self.ConsumeByteString()
try:
- return unicode(bytes, 'utf-8')
+ return unicode(the_bytes, 'utf-8')
except UnicodeDecodeError, e:
raise self._StringParseError(e)
@@ -553,10 +689,11 @@ class _Tokenizer(object):
Raises:
ParseError: If a byte array value couldn't be consumed.
"""
- list = [self._ConsumeSingleByteString()]
- while len(self.token) > 0 and self.token[0] in ('\'', '"'):
- list.append(self._ConsumeSingleByteString())
- return "".join(list)
+ the_list = [self._ConsumeSingleByteString()]
+ while self.token and self.token[0] in ('\'', '"'):
+ the_list.append(self._ConsumeSingleByteString())
+ return ''.encode('latin1').join(the_list) ##PY25
+##!PY25 return b''.join(the_list)
def _ConsumeSingleByteString(self):
"""Consume one token of a string literal.
@@ -567,48 +704,24 @@ class _Tokenizer(object):
"""
text = self.token
if len(text) < 1 or text[0] not in ('\'', '"'):
- raise self._ParseError('Exptected string.')
+ raise self._ParseError('Expected string but found: "%r"' % text)
if len(text) < 2 or text[-1] != text[0]:
raise self._ParseError('String missing ending quote.')
try:
- result = _CUnescape(text[1:-1])
+ result = text_encoding.CUnescape(text[1:-1])
except ValueError, e:
raise self._ParseError(str(e))
self.NextToken()
return result
- def _ParseInteger(self, text, is_signed=False, is_long=False):
- """Parses an integer.
-
- Args:
- text: The text to parse.
- is_signed: True if a signed integer must be parsed.
- is_long: True if a long integer must be parsed.
-
- Returns:
- The integer value.
-
- Raises:
- ValueError: Thrown Iff the text is not a valid integer.
- """
- pos = 0
- if text.startswith('-'):
- pos += 1
-
- base = 10
- if text.startswith('0x', pos) or text.startswith('0X', pos):
- base = 16
- elif text.startswith('0', pos):
- base = 8
-
- # Do the actual parsing. Exception handling is propagated to caller.
- result = int(text, base)
-
- # Check if the integer is sane. Exceptions handled by callers.
- checker = self._INTEGER_CHECKERS[2 * int(is_long) + int(is_signed)]
- checker.CheckValue(result)
+ def ConsumeEnum(self, field):
+ try:
+ result = ParseEnum(field, self.token)
+ except ValueError, e:
+ raise self._ParseError(str(e))
+ self.NextToken()
return result
def ParseErrorPreviousToken(self, message):
@@ -626,13 +739,7 @@ class _Tokenizer(object):
def _ParseError(self, message):
"""Creates and *returns* a ParseError for the current token."""
return ParseError('%d:%d : %s' % (
- self._line + 1, self._column - len(self.token) + 1, message))
-
- def _IntegerParseError(self, e):
- return self._ParseError('Couldn\'t parse integer: ' + str(e))
-
- def _FloatParseError(self, e):
- return self._ParseError('Couldn\'t parse number: ' + str(e))
+ self._line + 1, self._column + 1, message))
def _StringParseError(self, e):
return self._ParseError('Couldn\'t parse string: ' + str(e))
@@ -645,7 +752,7 @@ class _Tokenizer(object):
self._column += len(self.token)
self._SkipWhitespace()
- if not self._lines and len(self._current_line) <= self._column:
+ if not self._more_lines:
self.token = ''
return
@@ -657,35 +764,121 @@ class _Tokenizer(object):
self.token = self._current_line[self._column]
-# text.encode('string_escape') does not seem to satisfy our needs as it
-# encodes unprintable characters using two-digit hex escapes whereas our
-# C++ unescaping function allows hex escapes to be any length. So,
-# "\0011".encode('string_escape') ends up being "\\x011", which will be
-# decoded in C++ as a single-character string with char code 0x11.
-def _CEscape(text, as_utf8):
- def escape(c):
- o = ord(c)
- if o == 10: return r"\n" # optional escape
- if o == 13: return r"\r" # optional escape
- if o == 9: return r"\t" # optional escape
- if o == 39: return r"\'" # optional escape
+def ParseInteger(text, is_signed=False, is_long=False):
+ """Parses an integer.
- if o == 34: return r'\"' # necessary escape
- if o == 92: return r"\\" # necessary escape
+ Args:
+ text: The text to parse.
+ is_signed: True if a signed integer must be parsed.
+ is_long: True if a long integer must be parsed.
- # necessary escapes
- if not as_utf8 and (o >= 127 or o < 32): return "\\%03o" % o
- return c
- return "".join([escape(c) for c in text])
+ Returns:
+ The integer value.
+ Raises:
+ ValueError: Thrown Iff the text is not a valid integer.
+ """
+ # Do the actual parsing. Exception handling is propagated to caller.
+ try:
+ # We force 32-bit values to int and 64-bit values to long to make
+ # alternate implementations where the distinction is more significant
+ # (e.g. the C++ implementation) simpler.
+ if is_long:
+ result = long(text, 0)
+ else:
+ result = int(text, 0)
+ except ValueError:
+ raise ValueError('Couldn\'t parse integer: %s' % text)
-_CUNESCAPE_HEX = re.compile('\\\\x([0-9a-fA-F]{2}|[0-9a-fA-F])')
+ # Check if the integer is sane. Exceptions handled by callers.
+ checker = _INTEGER_CHECKERS[2 * int(is_long) + int(is_signed)]
+ checker.CheckValue(result)
+ return result
+
+
+def ParseFloat(text):
+ """Parse a floating point number.
+
+ Args:
+ text: Text to parse.
+ Returns:
+ The number parsed.
-def _CUnescape(text):
- def ReplaceHex(m):
- return chr(int(m.group(0)[2:], 16))
- # This is required because the 'string_escape' encoding doesn't
- # allow single-digit hex escapes (like '\xf').
- result = _CUNESCAPE_HEX.sub(ReplaceHex, text)
- return result.decode('string_escape')
+ Raises:
+ ValueError: If a floating point number couldn't be parsed.
+ """
+ try:
+ # Assume Python compatible syntax.
+ return float(text)
+ except ValueError:
+ # Check alternative spellings.
+ if _FLOAT_INFINITY.match(text):
+ if text[0] == '-':
+ return float('-inf')
+ else:
+ return float('inf')
+ elif _FLOAT_NAN.match(text):
+ return float('nan')
+ else:
+ # assume '1.0f' format
+ try:
+ return float(text.rstrip('f'))
+ except ValueError:
+ raise ValueError('Couldn\'t parse float: %s' % text)
+
+
+def ParseBool(text):
+ """Parse a boolean value.
+
+ Args:
+ text: Text to parse.
+
+ Returns:
+ Boolean values parsed
+
+ Raises:
+ ValueError: If text is not a valid boolean.
+ """
+ if text in ('true', 't', '1'):
+ return True
+ elif text in ('false', 'f', '0'):
+ return False
+ else:
+ raise ValueError('Expected "true" or "false".')
+
+
+def ParseEnum(field, value):
+ """Parse an enum value.
+
+ The value can be specified by a number (the enum value), or by
+ a string literal (the enum name).
+
+ Args:
+ field: Enum field descriptor.
+ value: String value.
+
+ Returns:
+ Enum value number.
+
+ Raises:
+ ValueError: If the enum value could not be parsed.
+ """
+ enum_descriptor = field.enum_type
+ try:
+ number = int(value, 0)
+ except ValueError:
+ # Identifier.
+ enum_value = enum_descriptor.values_by_name.get(value, None)
+ if enum_value is None:
+ raise ValueError(
+ 'Enum type "%s" has no value named %s.' % (
+ enum_descriptor.full_name, value))
+ else:
+ # Numeric value.
+ enum_value = enum_descriptor.values_by_number.get(number, None)
+ if enum_value is None:
+ raise ValueError(
+ 'Enum type "%s" has no value with number %d.' % (
+ enum_descriptor.full_name, number))
+ return enum_value.number
diff --git a/python/mox.py b/python/mox.py
index ce80ba50..257468e5 100755
--- a/python/mox.py
+++ b/python/mox.py
@@ -31,7 +31,7 @@ If an unexpected method (or an expected method with unexpected
parameters) is called, then an exception will be raised.
Once you are done interacting with the mock, you need to verify that
-all the expected interactions occured. (Maybe your code exited
+all the expected interactions occurred. (Maybe your code exited
prematurely without calling some cleanup method!) The verify phase
ensures that every expected method was called; otherwise, an exception
will be raised.
diff --git a/python/setup.py b/python/setup.py
index 5df0df89..c18818e2 100755
--- a/python/setup.py
+++ b/python/setup.py
@@ -1,22 +1,39 @@
#! /usr/bin/python
#
# See README for usage instructions.
+import sys
+import os
+import subprocess
# We must use setuptools, not distutils, because we need to use the
# namespace_packages option for the "google" package.
-from ez_setup import use_setuptools
-use_setuptools()
-
-from setuptools import setup, Extension
+try:
+ from setuptools import setup, Extension
+except ImportError:
+ try:
+ from ez_setup import use_setuptools
+ use_setuptools()
+ from setuptools import setup, Extension
+ except ImportError:
+ sys.stderr.write(
+ "Could not import setuptools; make sure you have setuptools or "
+ "ez_setup installed.\n")
+ raise
+from distutils.command.clean import clean as _clean
+if sys.version_info[0] >= 3:
+ # Python 3
+ from distutils.command.build_py import build_py_2to3 as _build_py
+else:
+ # Python 2
+ from distutils.command.build_py import build_py as _build_py
from distutils.spawn import find_executable
-import sys
-import os
-import subprocess
maintainer_email = "protobuf@googlegroups.com"
# Find the Protocol Compiler.
-if os.path.exists("../src/protoc"):
+if 'PROTOC' in os.environ and os.path.exists(os.environ['PROTOC']):
+ protoc = os.environ['PROTOC']
+elif os.path.exists("../src/protoc"):
protoc = "../src/protoc"
elif os.path.exists("../src/protoc.exe"):
protoc = "../src/protoc.exe"
@@ -27,6 +44,18 @@ elif os.path.exists("../vsprojects/Release/protoc.exe"):
else:
protoc = find_executable("protoc")
+def GetVersion():
+ """Gets the version from google/protobuf/__init__.py
+
+ Do not import google.protobuf.__init__ directly, because an installed protobuf
+ library may be loaded instead.
+
+ """
+ with open(os.path.join('google', 'protobuf', '__init__.py')) as version_file:
+ exec(version_file.read())
+ return __version__
+
+
def generate_proto(source):
"""Invokes the Protocol Compiler to generate a _pb2.py from the given
.proto file. Does nothing if the output already exists and is newer than
@@ -34,14 +63,14 @@ def generate_proto(source):
output = source.replace(".proto", "_pb2.py").replace("../src/", "")
- if not os.path.exists(source):
- print "Can't find required file: " + source
- sys.exit(-1)
-
if (not os.path.exists(output) or
(os.path.exists(source) and
os.path.getmtime(source) > os.path.getmtime(output))):
- print "Generating %s..." % output
+ print ("Generating %s..." % output)
+
+ if not os.path.exists(source):
+ sys.stderr.write("Can't find required file: %s\n" % source)
+ sys.exit(-1)
if protoc == None:
sys.stderr.write(
@@ -53,75 +82,90 @@ def generate_proto(source):
if subprocess.call(protoc_command) != 0:
sys.exit(-1)
-def MakeTestSuite():
- # This is apparently needed on some systems to make sure that the tests
- # work even if a previous version is already installed.
- if 'google' in sys.modules:
- del sys.modules['google']
-
+def GenerateUnittestProtos():
generate_proto("../src/google/protobuf/unittest.proto")
generate_proto("../src/google/protobuf/unittest_custom_options.proto")
generate_proto("../src/google/protobuf/unittest_import.proto")
+ generate_proto("../src/google/protobuf/unittest_import_public.proto")
generate_proto("../src/google/protobuf/unittest_mset.proto")
generate_proto("../src/google/protobuf/unittest_no_generic_services.proto")
+ generate_proto("../src/google/protobuf/unittest_proto3_arena.proto")
+ generate_proto("google/protobuf/internal/descriptor_pool_test1.proto")
+ generate_proto("google/protobuf/internal/descriptor_pool_test2.proto")
+ generate_proto("google/protobuf/internal/test_bad_identifiers.proto")
+ generate_proto("google/protobuf/internal/missing_enum_values.proto")
generate_proto("google/protobuf/internal/more_extensions.proto")
+ generate_proto("google/protobuf/internal/more_extensions_dynamic.proto")
generate_proto("google/protobuf/internal/more_messages.proto")
-
- import unittest
- import google.protobuf.internal.generator_test as generator_test
- import google.protobuf.internal.descriptor_test as descriptor_test
- import google.protobuf.internal.reflection_test as reflection_test
- import google.protobuf.internal.service_reflection_test \
- as service_reflection_test
- import google.protobuf.internal.text_format_test as text_format_test
- import google.protobuf.internal.wire_format_test as wire_format_test
-
- loader = unittest.defaultTestLoader
- suite = unittest.TestSuite()
- for test in [ generator_test,
- descriptor_test,
- reflection_test,
- service_reflection_test,
- text_format_test,
- wire_format_test ]:
- suite.addTest(loader.loadTestsFromModule(test))
-
- return suite
-
-if __name__ == '__main__':
- # TODO(kenton): Integrate this into setuptools somehow?
- if len(sys.argv) >= 2 and sys.argv[1] == "clean":
- # Delete generated _pb2.py files and .pyc files in the code tree.
+ generate_proto("google/protobuf/internal/factory_test1.proto")
+ generate_proto("google/protobuf/internal/factory_test2.proto")
+ generate_proto("google/protobuf/internal/import_test_package/inner.proto")
+ generate_proto("google/protobuf/internal/import_test_package/outer.proto")
+ generate_proto("google/protobuf/pyext/python.proto")
+
+class clean(_clean):
+ def run(self):
+ # Delete generated files in the code tree.
for (dirpath, dirnames, filenames) in os.walk("."):
for filename in filenames:
filepath = os.path.join(dirpath, filename)
if filepath.endswith("_pb2.py") or filepath.endswith(".pyc") or \
- filepath.endswith(".so") or filepath.endswith(".o"):
+ filepath.endswith(".so") or filepath.endswith(".o") or \
+ filepath.endswith('google/protobuf/compiler/__init__.py'):
os.remove(filepath)
- else:
+ # _clean is an old-style class, so super() doesn't work.
+ _clean.run(self)
+
+class build_py(_build_py):
+ def run(self):
# Generate necessary .proto file if it doesn't exist.
- # TODO(kenton): Maybe we should hook this into a distutils command?
generate_proto("../src/google/protobuf/descriptor.proto")
generate_proto("../src/google/protobuf/compiler/plugin.proto")
+ GenerateUnittestProtos()
+
+ # Make sure google.protobuf/** are valid packages.
+ for path in ['', 'internal/', 'compiler/', 'pyext/']:
+ try:
+ open('google/protobuf/%s__init__.py' % path, 'a').close()
+ except EnvironmentError:
+ pass
+ # _build_py is an old-style class, so super() doesn't work.
+ _build_py.run(self)
+ # TODO(mrovner): Subclass to run 2to3 on some files only.
+ # Tracing what https://wiki.python.org/moin/PortingPythonToPy3k's "Approach 2"
+ # section on how to get 2to3 to run on source files during install under
+ # Python 3. This class seems like a good place to put logic that calls
+ # python3's distutils.util.run_2to3 on the subset of the files we have in our
+ # release that are subject to conversion.
+ # See code reference in previous code review.
+if __name__ == '__main__':
ext_module_list = []
-
- # C++ implementation extension
- if os.getenv("PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION", "python") == "cpp":
- print "Using EXPERIMENTAL C++ Implmenetation."
+ cpp_impl = '--cpp_implementation'
+ if cpp_impl in sys.argv:
+ sys.argv.remove(cpp_impl)
+ # C++ implementation extension
ext_module_list.append(Extension(
- "google.protobuf.internal._net_proto2___python",
- [ "google/protobuf/pyext/python_descriptor.cc",
- "google/protobuf/pyext/python_protobuf.cc",
- "google/protobuf/pyext/python-proto2.cc" ],
- include_dirs = [ "." ],
- libraries = [ "protobuf" ]))
+ "google.protobuf.pyext._message",
+ [ "google/protobuf/pyext/descriptor.cc",
+ "google/protobuf/pyext/descriptor_containers.cc",
+ "google/protobuf/pyext/descriptor_pool.cc",
+ "google/protobuf/pyext/message.cc",
+ "google/protobuf/pyext/extension_dict.cc",
+ "google/protobuf/pyext/repeated_scalar_container.cc",
+ "google/protobuf/pyext/repeated_composite_container.cc" ],
+ define_macros=[('GOOGLE_PROTOBUF_HAS_ONEOF', '1')],
+ include_dirs = [ ".", "..", "../src"],
+ libraries = [ "protobuf" ],
+ library_dirs = [ '../src/.libs' ],
+ ))
+ os.environ['PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION'] = 'cpp'
setup(name = 'protobuf',
- version = '2.4.1',
+ version = GetVersion(),
packages = [ 'google' ],
namespace_packages = [ 'google' ],
- test_suite = 'setup.MakeTestSuite',
+ google_test_dir = "google/protobuf/internal",
# Must list modules explicitly so that we don't install tests.
py_modules = [
'google.protobuf.internal.api_implementation',
@@ -129,6 +173,7 @@ if __name__ == '__main__':
'google.protobuf.internal.cpp_message',
'google.protobuf.internal.decoder',
'google.protobuf.internal.encoder',
+ 'google.protobuf.internal.enum_type_wrapper',
'google.protobuf.internal.message_listener',
'google.protobuf.internal.python_message',
'google.protobuf.internal.type_checkers',
@@ -137,12 +182,26 @@ if __name__ == '__main__':
'google.protobuf.descriptor_pb2',
'google.protobuf.compiler.plugin_pb2',
'google.protobuf.message',
+ 'google.protobuf.descriptor_database',
+ 'google.protobuf.descriptor_pool',
+ 'google.protobuf.message_factory',
+ 'google.protobuf.proto_builder',
+ 'google.protobuf.pyext.cpp_message',
'google.protobuf.reflection',
'google.protobuf.service',
'google.protobuf.service_reflection',
- 'google.protobuf.text_format' ],
+ 'google.protobuf.symbol_database',
+ 'google.protobuf.text_encoding',
+ 'google.protobuf.text_format'],
+ cmdclass = { 'clean': clean, 'build_py': build_py },
+ install_requires = ['setuptools'],
+ # TODO: Restore dependency once a Python 3 compatible google-apputils
+ # is released.
+ setup_requires = (['google-apputils']
+ if sys.version_info[0] < 3 else
+ []),
ext_modules = ext_module_list,
- url = 'http://code.google.com/p/protobuf/',
+ url = 'https://developers.google.com/protocol-buffers/',
maintainer = maintainer_email,
maintainer_email = 'protobuf@googlegroups.com',
license = 'New BSD License',