aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJosh Haberman <jhaberman@gmail.com>2015-02-25 20:17:32 -0800
committerJosh Haberman <jhaberman@gmail.com>2015-02-25 20:17:32 -0800
commit0b70a43736fe070bee49141d493c74079ea68f97 (patch)
tree678b53cbc408c7520d893a8e0f705103c9f05608
parentada65567852b96fdb4d070c0c3f86ca7b77824f9 (diff)
downloadprotobuf-0b70a43736fe070bee49141d493c74079ea68f97.tar.gz
protobuf-0b70a43736fe070bee49141d493c74079ea68f97.tar.bz2
protobuf-0b70a43736fe070bee49141d493c74079ea68f97.zip
Fixes for Python/C++ implementation in open-source:
* Rosy hack doesn't apply (that test should be removed for the open-source release). * Added our own copy of parameterized.py (the open-source version of Google Apputils doesn't contain it). * The C++ Descriptor object didn't implement extension_ranges. * Had to implement a hack around returning EncodeError, to work around the module-loading behavior of the test runner.
-rw-r--r--Makefile.am9
-rwxr-xr-xpython/google/protobuf/internal/_parameterized.py438
-rwxr-xr-xpython/google/protobuf/internal/message_test.py5
-rwxr-xr-xpython/google/protobuf/internal/reflection_test.py10
-rwxr-xr-xpython/google/protobuf/internal/text_format_test.py4
-rwxr-xr-xpython/google/protobuf/message.py1
-rw-r--r--python/google/protobuf/pyext/descriptor.cc15
-rw-r--r--python/google/protobuf/pyext/message.cc26
-rw-r--r--python/google/protobuf/pyext/repeated_composite_container.cc4
-rw-r--r--python/google/protobuf/pyext/repeated_scalar_container.cc4
-rw-r--r--src/Makefile.am1
11 files changed, 490 insertions, 27 deletions
diff --git a/Makefile.am b/Makefile.am
index 1c64b710..de8ad96d 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -197,7 +197,6 @@ javanano_EXTRA_DIST=
python_EXTRA_DIST= \
python/google/protobuf/internal/api_implementation.cc \
python/google/protobuf/internal/api_implementation.py \
- python/google/protobuf/internal/api_implementation_default_test.py \
python/google/protobuf/internal/containers.py \
python/google/protobuf/internal/cpp_message.py \
python/google/protobuf/internal/decoder.py \
@@ -221,6 +220,7 @@ python_EXTRA_DIST= \
python/google/protobuf/internal/more_extensions.proto \
python/google/protobuf/internal/more_extensions_dynamic.proto \
python/google/protobuf/internal/more_messages.proto \
+ python/google/protobuf/internal/_parameterized.py \
python/google/protobuf/internal/proto_builder_test.py \
python/google/protobuf/internal/python_message.py \
python/google/protobuf/internal/reflection_test.py \
@@ -242,16 +242,17 @@ python_EXTRA_DIST= \
python/google/protobuf/pyext/cpp_message.py \
python/google/protobuf/pyext/descriptor.h \
python/google/protobuf/pyext/descriptor.cc \
- python/google/protobuf/pyext/descriptor_cpp2_test.py \
+ python/google/protobuf/pyext/descriptor_pool.h \
+ python/google/protobuf/pyext/descriptor_pool.cc \
+ python/google/protobuf/pyext/descriptor_containers.h \
+ python/google/protobuf/pyext/descriptor_containers.cc \
python/google/protobuf/pyext/extension_dict.h \
python/google/protobuf/pyext/extension_dict.cc \
python/google/protobuf/pyext/message.h \
python/google/protobuf/pyext/message.cc \
- python/google/protobuf/pyext/message_factory_cpp2_test.py \
python/google/protobuf/pyext/proto2_api_test.proto \
python/google/protobuf/pyext/python.proto \
python/google/protobuf/pyext/python_protobuf.h \
- python/google/protobuf/pyext/reflection_cpp2_generated_test.py \
python/google/protobuf/pyext/repeated_composite_container.h \
python/google/protobuf/pyext/repeated_composite_container.cc \
python/google/protobuf/pyext/repeated_scalar_container.h \
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/message_test.py b/python/google/protobuf/internal/message_test.py
index 7ab814cf..4d4de510 100755
--- a/python/google/protobuf/internal/message_test.py
+++ b/python/google/protobuf/internal/message_test.py
@@ -51,10 +51,10 @@ import sys
import unittest
from google.apputils import basetest
-from google.apputils.pybase 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 _parameterized
from google.protobuf.internal import test_util
from google.protobuf import message
@@ -72,7 +72,7 @@ def IsNegInf(val):
return isinf(val) and (val < 0)
-@parameterized.Parameters(
+@_parameterized.Parameters(
(unittest_pb2),
(unittest_proto3_arena_pb2))
class MessageTest(basetest.TestCase):
@@ -982,7 +982,6 @@ class Proto2Test(basetest.TestCase):
# 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.
diff --git a/python/google/protobuf/internal/reflection_test.py b/python/google/protobuf/internal/reflection_test.py
index 8ac76c63..a62d9845 100755
--- a/python/google/protobuf/internal/reflection_test.py
+++ b/python/google/protobuf/internal/reflection_test.py
@@ -1803,16 +1803,6 @@ class ReflectionTest(basetest.TestCase):
self.assertRaises(TypeError,
unittest_pb2.TestAllTypes().__getattribute__, 42)
- @basetest.unittest.skipIf(
- api_implementation.Type() != 'cpp' or api_implementation.Version() != 2,
- 'CPPv2-specific test')
- def testRosyHack(self):
- from google.protobuf.pyext import _message
- from google3.gdata.rosy.proto import core_api2_pb2
- from google3.gdata.rosy.proto import core_pb2
- self.assertEqual(_message.Message, core_pb2.PageSelection.__base__)
- self.assertEqual(_message.Message, core_api2_pb2.PageSelection.__base__)
-
# Since we had so many tests for protocol buffer equality, we broke these out
# into separate TestCase classes.
diff --git a/python/google/protobuf/internal/text_format_test.py b/python/google/protobuf/internal/text_format_test.py
index 68ab9659..b307620d 100755
--- a/python/google/protobuf/internal/text_format_test.py
+++ b/python/google/protobuf/internal/text_format_test.py
@@ -37,12 +37,12 @@ __author__ = 'kenton@google.com (Kenton Varda)'
import re
from google.apputils import basetest
-from google.apputils.pybase 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 _parameterized
from google.protobuf.internal import test_util
from google.protobuf import text_format
@@ -72,7 +72,7 @@ class TextFormatBase(basetest.TestCase):
return text
-@parameterized.Parameters(
+@_parameterized.Parameters(
(unittest_pb2),
(unittest_proto3_arena_pb2))
class TextFormatTest(TextFormatBase):
diff --git a/python/google/protobuf/message.py b/python/google/protobuf/message.py
index 88ed9f4c..de2f5697 100755
--- a/python/google/protobuf/message.py
+++ b/python/google/protobuf/message.py
@@ -36,7 +36,6 @@
__author__ = 'robinson@google.com (Will Robinson)'
-
class Error(Exception): pass
class DecodeError(Error): pass
class EncodeError(Error): pass
diff --git a/python/google/protobuf/pyext/descriptor.cc b/python/google/protobuf/pyext/descriptor.cc
index d9b90ddb..6890cd04 100644
--- a/python/google/protobuf/pyext/descriptor.cc
+++ b/python/google/protobuf/pyext/descriptor.cc
@@ -433,6 +433,20 @@ static PyObject* IsExtendable(PyBaseDescriptor *self, void *closure) {
}
}
+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();
@@ -512,6 +526,7 @@ static PyGetSetDef Getters[] = {
{ 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},
diff --git a/python/google/protobuf/pyext/message.cc b/python/google/protobuf/pyext/message.cc
index 137f5d5f..c48f94c3 100644
--- a/python/google/protobuf/pyext/message.cc
+++ b/python/google/protobuf/pyext/message.cc
@@ -1264,7 +1264,27 @@ static PyObject* SerializeToString(CMessage* self, PyObject* args) {
if (joined == NULL) {
return NULL;
}
- PyErr_Format(EncodeError_class, "Message %s is missing required fields: %s",
+
+ // 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;
}
@@ -2284,8 +2304,8 @@ int SetAttr(CMessage* self, PyObject* name, PyObject* value) {
PyTypeObject CMessage_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
- "google.protobuf.internal."
- "cpp._message.CMessage", // tp_name
+ "google.protobuf."
+ "pyext._message.CMessage", // tp_name
sizeof(CMessage), // tp_basicsize
0, // tp_itemsize
(destructor)cmessage::Dealloc, // tp_dealloc
diff --git a/python/google/protobuf/pyext/repeated_composite_container.cc b/python/google/protobuf/pyext/repeated_composite_container.cc
index 49ef7991..abc859d7 100644
--- a/python/google/protobuf/pyext/repeated_composite_container.cc
+++ b/python/google/protobuf/pyext/repeated_composite_container.cc
@@ -732,8 +732,8 @@ static PyMethodDef Methods[] = {
PyTypeObject RepeatedCompositeContainer_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
- "google.protobuf.internal."
- "cpp._message.RepeatedCompositeContainer", // tp_name
+ "google.protobuf.pyext."
+ "_message.RepeatedCompositeContainer", // tp_name
sizeof(RepeatedCompositeContainer), // tp_basicsize
0, // tp_itemsize
(destructor)repeated_composite_container::Dealloc, // tp_dealloc
diff --git a/python/google/protobuf/pyext/repeated_scalar_container.cc b/python/google/protobuf/pyext/repeated_scalar_container.cc
index 9f8075b2..17474598 100644
--- a/python/google/protobuf/pyext/repeated_scalar_container.cc
+++ b/python/google/protobuf/pyext/repeated_scalar_container.cc
@@ -769,8 +769,8 @@ static PyMethodDef Methods[] = {
PyTypeObject RepeatedScalarContainer_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
- "google.protobuf.internal."
- "cpp._message.RepeatedScalarContainer", // tp_name
+ "google.protobuf."
+ "pyext._message.RepeatedScalarContainer", // tp_name
sizeof(RepeatedScalarContainer), // tp_basicsize
0, // tp_itemsize
(destructor)repeated_scalar_container::Dealloc, // tp_dealloc
diff --git a/src/Makefile.am b/src/Makefile.am
index 0f0bd089..deb0f8a9 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -307,6 +307,7 @@ EXTRA_DIST = \
google/protobuf/io/gzip_stream_unittest.sh \
google/protobuf/testdata/golden_message \
google/protobuf/testdata/golden_message_oneof_implemented \
+ google/protobuf/testdata/golden_message_proto3 \
google/protobuf/testdata/golden_packed_fields_message \
google/protobuf/testdata/bad_utf8_string \
google/protobuf/testdata/text_format_unittest_data.txt \