From 87e64e1ceeff224043078e7e31fe0b1a119c8b1a Mon Sep 17 00:00:00 2001 From: "pesho.petrov" Date: Wed, 24 Dec 2008 01:07:22 +0000 Subject: [PATCH] Adding slicing support for repeated scalar fields and get/delete slice for composite fields. --- CHANGES.txt | 6 ++ CONTRIBUTORS.txt | 2 + python/google/protobuf/internal/containers.py | 51 ++++++++++--- .../protobuf/internal/reflection_test.py | 74 ++++++++++++++----- 4 files changed, 106 insertions(+), 27 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 427d3d26cd..7cc10e12be 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 593b3f863f..e212467e3d 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -53,3 +53,5 @@ Non-Google patch contributors: * Tru64 support. Monty Taylor * Solaris 10 + Sun Studio fix. + Alek Storm + * 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 982badc8c5..14fe863e01 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 c2ca5132af..14062762e5 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')