Browse Source

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

pesho.petrov 17 years ago
parent
commit
87e64e1cee

+ 6 - 0
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

+ 2 - 0
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.

+ 41 - 10
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.

+ 57 - 17
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')