aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES.txt6
-rw-r--r--CONTRIBUTORS.txt2
-rwxr-xr-xpython/google/protobuf/internal/containers.py51
-rwxr-xr-xpython/google/protobuf/internal/reflection_test.py74
4 files changed, 106 insertions, 27 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index 427d3d26..7cc10e12 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,3 +1,9 @@
+version 2.0.4:
+
+ Python
+ * Added slicing support for repeated scalar fields. Added slice retrieval and
+ removal of repeated composite fields.
+
2008-11-25 version 2.0.3:
protoc
diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt
index 593b3f86..e212467e 100644
--- a/CONTRIBUTORS.txt
+++ b/CONTRIBUTORS.txt
@@ -53,3 +53,5 @@ Non-Google patch contributors:
* Tru64 support.
Monty Taylor <monty.taylor@gmail.com>
* Solaris 10 + Sun Studio fix.
+ Alek Storm <alek.storm@gmail.com>
+ * Slicing support for repeated scalar fields for the Python API.
diff --git a/python/google/protobuf/internal/containers.py b/python/google/protobuf/internal/containers.py
index 982badc8..14fe863e 100755
--- a/python/google/protobuf/internal/containers.py
+++ b/python/google/protobuf/internal/containers.py
@@ -94,16 +94,20 @@ class RepeatedScalarFieldContainer(BaseContainer):
super(RepeatedScalarFieldContainer, self).__init__(message_listener)
self._type_checker = type_checker
- def append(self, elem):
- """Appends a scalar to the list. Similar to list.append()."""
- self._type_checker.CheckValue(elem)
- self._values.append(elem)
+ def append(self, value):
+ """Appends an item to the list. Similar to list.append()."""
+ self.insert(len(self._values), value)
+
+ 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._message_listener.ByteSizeDirty()
if len(self._values) == 1:
self._message_listener.TransitionToNonempty()
def remove(self, elem):
- """Removes a scalar from the list. Similar to list.remove()."""
+ """Removes an item from the list. Similar to list.remove()."""
self._values.remove(elem)
self._message_listener.ByteSizeDirty()
@@ -116,6 +120,27 @@ class RepeatedScalarFieldContainer(BaseContainer):
self._type_checker.CheckValue(value)
self._values[key] = value
+ def __getslice__(self, start, stop):
+ """Retrieves the subset of items from between the specified indices."""
+ return self._values[start:stop]
+
+ def __setslice__(self, start, stop, values):
+ """Sets the subset of items from between the specified indices."""
+ for value in values:
+ self._type_checker.CheckValue(value)
+ self._values[start:stop] = list(values)
+ self._message_listener.ByteSizeDirty()
+
+ def __delitem__(self, key):
+ """Deletes the item at the specified position."""
+ del self._values[key]
+ self._message_listener.ByteSizeDirty()
+
+ def __delslice__(self, start, stop):
+ """Deletes the subset of items from between the specified indices."""
+ del self._values[start:stop]
+ self._message_listener.ByteSizeDirty()
+
def __eq__(self, other):
"""Compares the current instance with another one."""
if self is other:
@@ -154,7 +179,6 @@ class RepeatedCompositeFieldContainer(BaseContainer):
self._message_descriptor = message_descriptor
def add(self):
- """Adds a new element to the list and returns it."""
new_element = self._message_descriptor._concrete_class()
new_element._SetListener(self._message_listener)
self._values.append(new_element)
@@ -162,10 +186,19 @@ class RepeatedCompositeFieldContainer(BaseContainer):
self._message_listener.TransitionToNonempty()
return new_element
+ def __getslice__(self, start, stop):
+ """Retrieves the subset of items from between the specified indices."""
+ return self._values[start:stop]
+
def __delitem__(self, key):
- """Deletes the element on the specified position."""
- self._message_listener.ByteSizeDirty()
+ """Deletes the item at the specified position."""
del self._values[key]
+ self._message_listener.ByteSizeDirty()
+
+ def __delslice__(self, start, stop):
+ """Deletes the subset of items from between the specified indices."""
+ del self._values[start:stop]
+ self._message_listener.ByteSizeDirty()
def __eq__(self, other):
"""Compares the current instance with another one."""
@@ -175,5 +208,3 @@ class RepeatedCompositeFieldContainer(BaseContainer):
raise TypeError('Can only compare repeated composite fields against '
'other repeated composite fields.')
return self._values == other._values
-
- # TODO(robinson): Implement, document, and test slicing support.
diff --git a/python/google/protobuf/internal/reflection_test.py b/python/google/protobuf/internal/reflection_test.py
index c2ca5132..14062762 100755
--- a/python/google/protobuf/internal/reflection_test.py
+++ b/python/google/protobuf/internal/reflection_test.py
@@ -56,7 +56,12 @@ from google.protobuf.internal import test_util
from google.protobuf.internal import decoder
-class RefectionTest(unittest.TestCase):
+class ReflectionTest(unittest.TestCase):
+
+ def assertIs(self, values, others):
+ self.assertEqual(len(values), len(others))
+ for i in range(len(values)):
+ self.assertTrue(values[i] is others[i])
def testSimpleHasBits(self):
# Test a scalar.
@@ -411,14 +416,17 @@ class RefectionTest(unittest.TestCase):
self.assertTrue(not proto.repeated_int32)
self.assertEqual(0, len(proto.repeated_int32))
- proto.repeated_int32.append(5);
- proto.repeated_int32.append(10);
+ proto.repeated_int32.append(5)
+ proto.repeated_int32.append(10)
+ proto.repeated_int32.append(15)
self.assertTrue(proto.repeated_int32)
- self.assertEqual(2, len(proto.repeated_int32))
+ self.assertEqual(3, len(proto.repeated_int32))
+
+ self.assertEqual([5, 10, 15], proto.repeated_int32)
- self.assertEqual([5, 10], proto.repeated_int32)
+ # Test single retrieval.
self.assertEqual(5, proto.repeated_int32[0])
- self.assertEqual(10, proto.repeated_int32[-1])
+ self.assertEqual(15, proto.repeated_int32[-1])
# Test out-of-bounds indices.
self.assertRaises(IndexError, proto.repeated_int32.__getitem__, 1234)
self.assertRaises(IndexError, proto.repeated_int32.__getitem__, -1234)
@@ -426,11 +434,36 @@ class RefectionTest(unittest.TestCase):
self.assertRaises(TypeError, proto.repeated_int32.__getitem__, 'foo')
self.assertRaises(TypeError, proto.repeated_int32.__getitem__, None)
+ # Test single assignment.
+ proto.repeated_int32[1] = 20
+ self.assertEqual([5, 20, 15], proto.repeated_int32)
+
+ # Test insertion.
+ proto.repeated_int32.insert(1, 25)
+ self.assertEqual([5, 25, 20, 15], proto.repeated_int32)
+
+ # Test slice retrieval.
+ proto.repeated_int32.append(30)
+ self.assertEqual([25, 20, 15], proto.repeated_int32[1:4])
+ self.assertEqual([5, 25, 20, 15, 30], proto.repeated_int32[:])
+
+ # Test slice assignment.
+ proto.repeated_int32[1:4] = [35, 40, 45]
+ self.assertEqual([5, 35, 40, 45, 30], proto.repeated_int32)
+
# Test that we can use the field as an iterator.
result = []
for i in proto.repeated_int32:
result.append(i)
- self.assertEqual([5, 10], result)
+ self.assertEqual([5, 35, 40, 45, 30], result)
+
+ # Test single deletion.
+ del proto.repeated_int32[2]
+ self.assertEqual([5, 35, 45, 30], proto.repeated_int32)
+
+ # Test slice deletion.
+ del proto.repeated_int32[2:]
+ self.assertEqual([5, 35], proto.repeated_int32)
# Test clearing.
proto.ClearField('repeated_int32')
@@ -474,8 +507,7 @@ class RefectionTest(unittest.TestCase):
m1 = proto.repeated_nested_message.add()
self.assertTrue(proto.repeated_nested_message)
self.assertEqual(2, len(proto.repeated_nested_message))
- self.assertTrue(m0 is proto.repeated_nested_message[0])
- self.assertTrue(m1 is proto.repeated_nested_message[1])
+ self.assertIs([m0, m1], proto.repeated_nested_message)
self.assertTrue(isinstance(m0, unittest_pb2.TestAllTypes.NestedMessage))
# Test out-of-bounds indices.
@@ -490,18 +522,26 @@ class RefectionTest(unittest.TestCase):
self.assertRaises(TypeError, proto.repeated_nested_message.__getitem__,
None)
+ # Test slice retrieval.
+ m2 = proto.repeated_nested_message.add()
+ m3 = proto.repeated_nested_message.add()
+ m4 = proto.repeated_nested_message.add()
+ self.assertIs([m1, m2, m3], proto.repeated_nested_message[1:4])
+ self.assertIs([m0, m1, m2, m3, m4], proto.repeated_nested_message[:])
+
# Test that we can use the field as an iterator.
result = []
for i in proto.repeated_nested_message:
result.append(i)
- self.assertEqual(2, len(result))
- self.assertTrue(m0 is result[0])
- self.assertTrue(m1 is result[1])
-
- # Test item deletion.
- del proto.repeated_nested_message[0]
- self.assertEqual(1, len(proto.repeated_nested_message))
- self.assertTrue(m1 is proto.repeated_nested_message[0])
+ self.assertIs([m0, m1, m2, m3, m4], result)
+
+ # Test single deletion.
+ del proto.repeated_nested_message[2]
+ self.assertIs([m0, m1, m3, m4], proto.repeated_nested_message)
+
+ # Test slice deletion.
+ del proto.repeated_nested_message[2:]
+ self.assertIs([m0, m1], proto.repeated_nested_message)
# Test clearing.
proto.ClearField('repeated_nested_message')