Repository: ethereum/pyrlp

Issue:
LazyList.__getitem__ assumes i is a number but it can be a slice
LazyList's __getitem__ implementation [assumes **i** is always a number](https://github.com/ethereum/pyrlp/blob/develop/rlp/lazy.py#L108), but it can [be a slice as well](https://docs.python.org/2/reference/datamodel.html#object.__getitem__)

The fix is trivial, but I want to investigate it further because it's not clear to me how that deserialize() call would cause __getitem__ to raise an IndexError, since it [checks the list's length before accessing the first element](https://github.com/ethereum/pyrlp/blob/develop/rlp/sedes/big_endian_int.py#L38)

Here's an example traceback I got from running the hive consensus simulator against pyethapp

```
TypeErrorTraceback (most recent call last)
<ipython-input-1-59579d93b561> in <module>()
----> 1 from importblock import Importer; Importer(eth).run()

/importblock.py in run(self)
     22             data = open('/blocks/' + block, 'r').read()
     23             block_data = rlp.decode_lazy(data)
---> 24             header = BlockHeader.deserialize(block_data[0])
     25             transactions = rlp.sedes.CountableList(Transaction).deserialize(block_data[1])
     26             uncles = rlp.sedes.CountableList(BlockHeader).deserialize(block_data[2])

/usr/lib/python2.7/site-packages/rlp-0.5.1-py2.7.egg/rlp/sedes/lists.pyc in deserialize(cls, serial, exclude, mutable, **kwargs)
    243     def deserialize(cls, serial, exclude=None, mutable=False, **kwargs):
    244         try:
--> 245             values = cls.get_sedes().deserialize(serial)
    246         except ListDeserializationError as e:
    247             raise ObjectDeserializationError(serial=serial, sedes=cls, list_exception=e)

/usr/lib/python2.7/site-packages/rlp-0.5.1-py2.7.egg/rlp/sedes/lists.pyc in deserialize(self, serial)
     81             if not (sedes_consumed or elements_consumed):
     82                 try:
---> 83                     result.append(sedes.deserialize(element))
     84                 except DeserializationError as e:
     85                     raise ListDeserializationError(serial=serial, element_exception=e, index=index)

/usr/lib/python2.7/site-packages/rlp-0.5.1-py2.7.egg/rlp/sedes/big_endian_int.pyc in deserialize(self, serial)
     36             raise DeserializationError('Invalid serialization (wrong size)',
     37                                        serial)
---> 38         if self.l is None and len(serial) > 0 and serial[0:1] == ascii_chr(0):
     39             raise DeserializationError('Invalid serialization (not minimal '
     40                                        'length)', serial)

/usr/lib/python2.7/site-packages/rlp-0.5.1-py2.7.egg/rlp/lazy.pyc in __getitem__(self, i)
    106         except StopIteration:
    107             assert self.index == self.end
--> 108             raise IndexError('Index %d out of range' % i)
    109         return self._elements[i]
    110 
```


Discussion:

Patch:
diff --git a/rlp/lazy.py b/rlp/lazy.py
index e12583d..e9a0b14 100644
--- a/rlp/lazy.py
+++ b/rlp/lazy.py
@@ -100,13 +100,26 @@ class LazyList(Sequence):
         return item
 
     def __getitem__(self, i):
+        if isinstance(i, slice):
+            if i.step is not None:
+                raise TypeError("Step not supported")
+            start = i.start
+            stop = i.stop
+        else:
+            start = i
+            stop = i + 1
+
         try:
-            while len(self._elements) <= i:
-                self.next()
+            while len(self._elements) < stop:
+                e = self.next()
         except StopIteration:
             assert self.index == self.end
-            raise IndexError('Index %d out of range' % i)
-        return self._elements[i]
+            raise IndexError('Index %s out of range' % i)
+
+        if isinstance(i, slice):
+            return self._elements[start:stop]
+        else:
+            return self._elements[start]
 
     def __len__(self):
         if not self._len:


Test patch:
diff --git a/tests/test_lazy.py b/tests/test_lazy.py
index bf9d478..a969483 100644
--- a/tests/test_lazy.py
+++ b/tests/test_lazy.py
@@ -38,6 +38,17 @@ def test_string():
             rlp.peek(rlp.encode(s), [0])
 
 
+def test_list_getitem():
+    l = rlp.decode_lazy(rlp.encode([1,2,3]), big_endian_int)
+    assert isinstance(l, rlp.lazy.LazyList)
+    assert l[0] == 1
+    assert l[1] == 2
+    assert l[2] == 3
+    assert l[0:3] == [1,2,3]
+    assert l[0:2] == [1,2]
+    assert l[0:1] == [1]
+
+
 def test_nested_list():
     l = ((), (b'a'), (b'b', b'c', b'd'))
     dec = lambda: rlp.decode_lazy(rlp.encode(l))