Adding slicing support for repeated scalar fields and get/delete slice for composite fields.

pull/3335/head
pesho.petrov 16 years ago
parent d41578239c
commit 87e64e1cee
  1. 6
      CHANGES.txt
  2. 2
      CONTRIBUTORS.txt
  3. 51
      python/google/protobuf/internal/containers.py
  4. 74
      python/google/protobuf/internal/reflection_test.py

@ -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

@ -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.

@ -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.

@ -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')

Loading…
Cancel
Save