[pypy-commit] pypy reflex-support: pythonized indexing and slicing for std::vector-like objects

wlav noreply at buildbot.pypy.org
Fri Mar 16 23:03:25 CET 2012


Author: Wim Lavrijsen <WLavrijsen at lbl.gov>
Branch: reflex-support
Changeset: r53735:897bb52ddf19
Date: 2012-03-16 02:26 -0700
http://bitbucket.org/pypy/pypy/changeset/897bb52ddf19/

Log:	pythonized indexing and slicing for std::vector-like objects

diff --git a/pypy/module/cppyy/pythonify.py b/pypy/module/cppyy/pythonify.py
--- a/pypy/module/cppyy/pythonify.py
+++ b/pypy/module/cppyy/pythonify.py
@@ -280,6 +280,26 @@
     return scope
 
 
+# pythonization by decoration (move to their own file?)
+import types
+
+def python_style_getitem(self, idx):
+    # python-style indexing: check for size and allow indexing from the back
+    sz = len(self)
+    if idx < 0: idx = sz + idx
+    if idx < sz:
+        return self._getitem__unchecked(idx)
+    raise IndexError('index out of range: %d requested for %s of size %d' % (idx, str(self), sz))
+
+def python_style_sliceable_getitem(self, slice_or_idx):
+    if type(slice_or_idx) == types.SliceType:
+        nseq = self.__class__()
+        nseq += [python_style_getitem(self, i) \
+                    for i in range(*slice_or_idx.indices(len(self)))]
+        return nseq
+    else:
+        return python_style_getitem(self, slice_or_idx)
+
 _pythonizations = {}
 def _pythonize(pyclass):
 
@@ -290,20 +310,39 @@
 
     # map size -> __len__ (generally true for STL)
     if hasattr(pyclass, 'size') and \
-            not hasattr(pyclass,'__len__') and callable(pyclass.size):
+            not hasattr(pyclass, '__len__') and callable(pyclass.size):
         pyclass.__len__ = pyclass.size
 
+    # map push_back -> __iadd__ (generally true for STL)
+    if hasattr(pyclass, 'push_back') and not hasattr(pyclass, '__iadd__'):
+        def __iadd__(self, ll):
+            [self.push_back(x) for x in ll]
+            return self
+        pyclass.__iadd__ = __iadd__
+
     # map begin()/end() protocol to iter protocol
     if hasattr(pyclass, 'begin') and hasattr(pyclass, 'end'):
-        def __iter__(self):
-            iter = self.begin()
+        try:
             # TODO: make gnu-independent
-            while gbl.__gnu_cxx.__ne__(iter, self.end()):
-                yield iter.__deref__()
-                iter.__preinc__()
-            iter.destruct()
-            raise StopIteration
-        pyclass.__iter__ = __iter__
+            ne = gbl.__gnu_cxx.__ne__
+            def __iter__(self):
+                iter = self.begin()
+                while gbl.__gnu_cxx.__ne__(iter, self.end()):
+                    yield iter.__deref__()
+                    iter.__preinc__()
+                iter.destruct()
+                raise StopIteration
+            pyclass.__iter__ = __iter__
+        except AttributeError:
+            pass
+
+    # combine __getitem__ and __len__ to make a pythonized __getitem__
+    if hasattr(pyclass, '__getitem__') and hasattr(pyclass, '__len__'):
+        pyclass._getitem__unchecked = pyclass.__getitem__
+        if hasattr(pyclass, '__setitem__') and hasattr(pyclass, '__iadd__'):
+            pyclass.__getitem__ = python_style_sliceable_getitem
+        else:
+            pyclass.__getitem__ = python_style_getitem
 
     # string comparisons (note: CINT backend requires the simple name 'string')
     if pyclass.__name__ == 'std::basic_string<char>' or pyclass.__name__ == 'string':
diff --git a/pypy/module/cppyy/test/test_stltypes.py b/pypy/module/cppyy/test/test_stltypes.py
--- a/pypy/module/cppyy/test/test_stltypes.py
+++ b/pypy/module/cppyy/test/test_stltypes.py
@@ -131,6 +131,65 @@
 
         v.destruct()
 
+    def test05_push_back_iterables_with_iadd(self):
+        """Test usage of += of iterable on push_back-able container"""
+
+        import cppyy
+
+        v = cppyy.gbl.std.vector(int)()
+
+        v += [1, 2, 3]
+        assert len(v) == 3
+        assert v[0] == 1
+        assert v[1] == 2
+        assert v[2] == 3
+
+        v += (4, 5, 6)
+        assert len(v) == 6
+        assert v[3] == 4
+        assert v[4] == 5
+        assert v[5] == 6
+
+        raises(TypeError, v.__iadd__, (7, '8'))  # string shouldn't pass
+        assert len(v) == 7   # TODO: decide whether this should roll-back
+
+        v2 = cppyy.gbl.std.vector(int)()
+        v2 += [8, 9]
+        assert len(v2) == 2
+        assert v2[0] == 8
+        assert v2[1] == 9
+
+        v += v2
+        assert len(v) == 9
+        assert v[6] == 7
+        assert v[7] == 8
+        assert v[8] == 9
+
+    def test06_vector_indexing(self):
+        """Test python-style indexing to an std::vector<int>"""
+
+        import cppyy
+
+        v = cppyy.gbl.std.vector(int)()
+
+        for i in range(self.N):
+            v.push_back(i)
+
+        raises(IndexError, 'v[self.N]')
+        raises(IndexError, 'v[self.N+1]')
+
+        assert v[-1] == self.N-1
+        assert v[-2] == self.N-2
+
+        assert len(v[0:0]) == 0
+        assert v[1:2][0] == v[1]
+
+        v2 = v[2:-1]
+        assert len(v2) == self.N-3     # 2 off from start, 1 from end
+        assert v2[0] == v[2]
+        assert v2[-1] == v[-2]
+        assert v2[self.N-4] == v[-2]
+
 
 class AppTestSTLSTRING:
     def setup_class(cls):


More information about the pypy-commit mailing list