Commits

Carl Friedrich Bolz committed cba07b6 Merge

merge

Comments (0)

Files changed (11)

pypy/config/pypyoption.py

                    "(the empty string and potentially single-char strings)",
                    default=False),
 
+        BoolOption("withsmalltuple",
+                   "use small tuples",
+                   default=False),
+
         BoolOption("withrope", "use ropes as the string implementation",
                    default=False,
                    requires=[("objspace.std.withstrslice", False),

pypy/doc/config/objspace.std.withsmalltuple.txt

+Use small tuple objects for sizes from 1 to 3

pypy/module/cpyext/methodobject.py

                 rffi.charp2str(self.ml.c_ml_name) + "() takes no keyword arguments"))
 
         func = rffi.cast(PyCFunction, self.ml.c_ml_meth)
+        length = space.int_w(space.len(w_args))
         if flags & METH_KEYWORDS:
             func = rffi.cast(PyCFunctionKwArgs, self.ml.c_ml_meth)
             return generic_cpy_call(space, func, w_self, w_args, w_kw)
         elif flags & METH_NOARGS:
-            if len(w_args.wrappeditems) == 0:
+            if length == 0:
                 return generic_cpy_call(space, func, w_self, None)
             raise OperationError(space.w_TypeError, space.wrap(
                 rffi.charp2str(self.ml.c_ml_name) + "() takes no arguments"))
         elif flags & METH_O:
-            assert isinstance(w_args, W_TupleObject)
-            if len(w_args.wrappeditems) != 1:
+            if length != 1:
                 raise OperationError(space.w_TypeError,
                         space.wrap("%s() takes exactly one argument (%d given)" %  (
                         rffi.charp2str(self.ml.c_ml_name), 
-                        len(w_args.wrappeditems))))
-            w_arg = w_args.wrappeditems[0]
+                        length)))
+            w_arg = space.getitem(w_args, space.wrap(0))
             return generic_cpy_call(space, func, w_self, w_arg)
         elif flags & METH_VARARGS:
             return generic_cpy_call(space, func, w_self, w_args)
         else: # METH_OLDARGS, the really old style
-            size = len(w_args.wrappeditems)
+            size = length
             if size == 1:
-                w_arg = w_args.wrappeditems[0]
+                w_arg = space.getitem(w_args, space.wrap(0))
             elif size == 0:
                 w_arg = None
             else:

pypy/module/cpyext/test/test_tupleobject.py

 from pypy.module.cpyext.pyobject import PyObject, PyObjectP, make_ref, from_ref
 from pypy.module.cpyext.test.test_api import BaseApiTest
 from pypy.rpython.lltypesystem import rffi, lltype
+from pypy.conftest import gettestobjspace
 
 class TestTupleObject(BaseApiTest):
+
     def test_tupleobject(self, space, api):
         assert not api.PyTuple_Check(space.w_None)
         assert api.PyTuple_SetItem(space.w_None, 0, space.w_None) == -1
         ar[0] = rffi.cast(PyObject, make_ref(space, py_tuple))
         api._PyTuple_Resize(ar, 2)
         py_tuple = from_ref(space, ar[0])
-        assert len(py_tuple.wrappeditems) == 2
+        assert space.int_w(space.len(py_tuple)) == 2
         
         api._PyTuple_Resize(ar, 10)
         py_tuple = from_ref(space, ar[0])
-        assert len(py_tuple.wrappeditems) == 10
+        assert space.int_w(space.len(py_tuple)) == 10
         
         api.Py_DecRef(ar[0])
         lltype.free(ar, flavor='raw')
+
+    def test_setitem(self, space, api):
+        atuple = space.newtuple([space.wrap(0), space.wrap("hello")])
+        assert api.PyTuple_Size(atuple) == 2
+        assert space.eq_w(space.getitem(atuple, space.wrap(0)), space.wrap(0))
+        assert space.eq_w(space.getitem(atuple, space.wrap(1)), space.wrap("hello"))
+        w_obj = space.wrap(1)
+        api.Py_IncRef(w_obj)
+        api.PyTuple_SetItem(atuple, 1, w_obj)
+        assert api.PyTuple_Size(atuple) == 2
+        assert space.eq_w(space.getitem(atuple, space.wrap(0)), space.wrap(0))
+        assert space.eq_w(space.getitem(atuple, space.wrap(1)), space.wrap(1))

pypy/module/cpyext/tupleobject.py

     borrow_from, make_ref, from_ref)
 from pypy.module.cpyext.pyerrors import PyErr_BadInternalCall
 from pypy.objspace.std.tupleobject import W_TupleObject
-
+from pypy.objspace.std.smalltupleobject import W_SmallTupleObject
 
 PyTuple_Check, PyTuple_CheckExact = build_type_checkers("Tuple")
 
     if not PyTuple_Check(space, w_t):
         # XXX this should also steal a reference, test it!!!
         PyErr_BadInternalCall(space)
-    assert isinstance(w_t, W_TupleObject)
-    w_t.wrappeditems[pos] = w_obj
+    _setitem_tuple(w_t, pos, w_obj)
     Py_DecRef(space, w_obj) # SetItem steals a reference!
     return 0
 
+def _setitem_tuple(w_t, pos, w_obj):
+    if isinstance(w_t, W_TupleObject):
+        w_t.wrappeditems[pos] = w_obj
+    elif isinstance(w_t, W_SmallTupleObject):
+        w_t.setitem(pos, w_obj)
+    else:
+        assert False
+
 @cpython_api([PyObject, Py_ssize_t], PyObject)
 def PyTuple_GetItem(space, w_t, pos):
     if not PyTuple_Check(space, w_t):
         PyErr_BadInternalCall(space)
-    assert isinstance(w_t, W_TupleObject)
-    w_obj = w_t.wrappeditems[pos]
+    w_obj = space.getitem(w_t, space.wrap(pos))
     return borrow_from(w_t, w_obj)
 
 @cpython_api([PyObject], Py_ssize_t, error=CANNOT_FAIL)
 def PyTuple_GET_SIZE(space, w_t):
     """Return the size of the tuple p, which must be non-NULL and point to a tuple;
     no error checking is performed. """
-    assert isinstance(w_t, W_TupleObject)
-    return len(w_t.wrappeditems)
+    return space.int_w(space.len(w_t))
 
 @cpython_api([PyObject], Py_ssize_t, error=-1)
 def PyTuple_Size(space, ref):
     py_tuple = from_ref(space, ref[0])
     if not PyTuple_Check(space, py_tuple):
         PyErr_BadInternalCall(space)
-    assert isinstance(py_tuple, W_TupleObject)
     py_newtuple = PyTuple_New(space, newsize)
     
     to_cp = newsize
-    oldsize = len(py_tuple.wrappeditems)
+    oldsize = space.int_w(space.len(py_tuple))
     if oldsize < newsize:
         to_cp = oldsize
     for i in range(to_cp):
-        py_newtuple.wrappeditems[i] = py_tuple.wrappeditems[i]
+        _setitem_tuple(py_newtuple, i, space.getitem(py_tuple, space.wrap(i)))
     Py_DecRef(space, ref[0])
     ref[0] = make_ref(space, py_newtuple)
     return 0

pypy/objspace/std/model.py

     _registered_implementations.add(implcls)
 
 option_to_typename = {
+    "withsmalltuple" : ["smalltupleobject.W_SmallTupleObject"],
     "withsmallint"   : ["smallintobject.W_SmallIntObject"],
     "withsmalllong"  : ["smalllongobject.W_SmallLongObject"],
     "withstrslice"   : ["strsliceobject.W_StringSliceObject"],
         from pypy.objspace.std import smallintobject
         from pypy.objspace.std import smalllongobject
         from pypy.objspace.std import tupleobject
+        from pypy.objspace.std import smalltupleobject
         from pypy.objspace.std import listobject
         from pypy.objspace.std import dictmultiobject
         from pypy.objspace.std import stringobject
                 (listobject.W_ListObject,
                                        rangeobject.delegate_range2list),
                 ]
+        if config.objspace.std.withsmalltuple:
+            self.typeorder[smalltupleobject.W_SmallTupleObject] += [
+                (tupleobject.W_TupleObject, smalltupleobject.delegate_SmallTuple2Tuple)]
 
         # put W_Root everywhere
         self.typeorder[W_Root] = []

pypy/objspace/std/objspace.py

         return newlong(self, val)
 
     def newtuple(self, list_w):
+        from pypy.objspace.std.tupletype import wraptuple
         assert isinstance(list_w, list)
         make_sure_not_resized(list_w)
-        return W_TupleObject(list_w)
+        return wraptuple(self, list_w)
 
     def newlist(self, list_w):
         return W_ListObject(list_w)

pypy/objspace/std/smalltupleobject.py

+from pypy.interpreter.error import OperationError
+from pypy.objspace.std.model import registerimplementation, W_Object
+from pypy.objspace.std.register_all import register_all
+from pypy.objspace.std.inttype import wrapint
+from pypy.objspace.std.multimethod import FailedToImplement
+from pypy.rlib.rarithmetic import intmask
+from pypy.objspace.std.sliceobject import W_SliceObject, normalize_simple_slice
+from pypy.objspace.std import slicetype
+from pypy.interpreter import gateway
+from pypy.rlib.debug import make_sure_not_resized
+from pypy.rlib.unroll import unrolling_iterable
+from pypy.objspace.std.tupleobject import W_TupleObject
+
+class W_SmallTupleObject(W_Object):
+    from pypy.objspace.std.tupletype import tuple_typedef as typedef
+
+    def tolist(self):
+        raise NotImplementedError
+
+    def length(self):
+        raise NotImplementedError
+
+    def getitem(self, index):
+        raise NotImplementedError
+
+    def hash(self, space):
+        raise NotImplementedError
+
+    def eq(self, space, w_other):
+        raise NotImplementedError
+
+    def setitem(self, index, w_item):
+        raise NotImplementedError
+
+    def unwrap(w_tuple, space):
+        items = [space.unwrap(w_item) for w_item in w_tuple.tolist()] # XXX generic mixed types unwrap
+        return tuple(items)
+
+def make_specialized_class(n):
+    iter_n = unrolling_iterable(range(n))
+    class cls(W_SmallTupleObject):
+
+        def __init__(self, values):
+            assert len(values) == n
+            for i in iter_n:
+                setattr(self, 'w_value%s' % i, values[i])
+
+        def tolist(self):
+            l = [None] * n
+            for i in iter_n:
+                l[i] = getattr(self, 'w_value%s' % i)
+            return l
+
+        def length(self):
+            return n
+
+        def getitem(self, index):
+            for i in iter_n:
+                if index == i:
+                    return getattr(self,'w_value%s' % i)
+            raise IndexError
+
+        def setitem(self, index, w_item):
+            for i in iter_n:
+                if index == i:
+                    setattr(self, 'w_value%s' % i, w_item)
+                    return
+            raise IndexError
+
+        def eq(self, space, w_other):
+            if self.length() != w_other.length():
+                return space.w_False
+            for i in iter_n:
+                item1 = self.getitem(i)
+                item2 = w_other.getitem(i)
+                if not space.eq_w(item1, item2):
+                    return space.w_False
+            return space.w_True
+
+        def hash(self, space):
+            mult = 1000003
+            x = 0x345678
+            z = self.length()
+            for i in iter_n:
+                w_item = self.getitem(i)
+                y = space.int_w(space.hash(w_item))
+                x = (x ^ y) * mult
+                z -= 1
+                mult += 82520 + z + z
+            x += 97531
+            return space.wrap(intmask(x))
+
+    cls.__name__ = "W_SmallTupleObject%s" % n
+    return cls
+
+W_SmallTupleObject2 = make_specialized_class(2)
+W_SmallTupleObject3 = make_specialized_class(3)
+W_SmallTupleObject4 = make_specialized_class(4)
+W_SmallTupleObject5 = make_specialized_class(5)
+W_SmallTupleObject6 = make_specialized_class(6)
+W_SmallTupleObject7 = make_specialized_class(7)
+W_SmallTupleObject8 = make_specialized_class(8)
+
+registerimplementation(W_SmallTupleObject)
+
+def delegate_SmallTuple2Tuple(space, w_small):
+    return W_TupleObject(w_small.tolist())
+
+def len__SmallTuple(space, w_tuple):
+    return space.wrap(w_tuple.length())
+
+def getitem__SmallTuple_ANY(space, w_tuple, w_index):
+    index = space.getindex_w(w_index, space.w_IndexError, "tuple index")
+    if index < 0:
+        index += w_tuple.length()
+    try:
+        return w_tuple.getitem(index)
+    except IndexError:
+        raise OperationError(space.w_IndexError,
+                             space.wrap("tuple index out of range"))
+
+def getitem__SmallTuple_Slice(space, w_tuple, w_slice):
+    length = w_tuple.length()
+    start, stop, step, slicelength = w_slice.indices4(space, length)
+    assert slicelength >= 0
+    subitems = [None] * slicelength
+    for i in range(slicelength):
+        subitems[i] = w_tuple.getitem(start)
+        start += step
+    return space.newtuple(subitems)
+
+def mul_smalltuple_times(space, w_tuple, w_times):
+    try:
+        times = space.getindex_w(w_times, space.w_OverflowError)
+    except OperationError, e:
+        if e.match(space, space.w_TypeError):
+            raise FailedToImplement
+        raise
+    if times == 1 and space.type(w_tuple) == space.w_tuple:
+        return w_tuple
+    items = w_tuple.tolist()
+    return space.newtuple(items * times)
+
+def mul__SmallTuple_ANY(space, w_tuple, w_times):
+    return mul_smalltuple_times(space, w_tuple, w_times)
+
+def mul__ANY_SmallTuple(space, w_times, w_tuple):
+    return mul_smalltuple_times(space, w_tuple, w_times)
+
+def eq__SmallTuple_SmallTuple(space, w_tuple1, w_tuple2):
+    return w_tuple1.eq(space, w_tuple2)
+
+def hash__SmallTuple(space, w_tuple):
+    return w_tuple.hash(space)
+
+from pypy.objspace.std import tupletype
+register_all(vars(), tupletype)

pypy/objspace/std/test/test_smalltupleobject.py

+from pypy.objspace.std.tupleobject import W_TupleObject
+from pypy.objspace.std.smalltupleobject import W_SmallTupleObject
+from pypy.interpreter.error import OperationError
+from pypy.objspace.std.test.test_tupleobject import AppTestW_TupleObject
+from pypy.conftest import gettestobjspace
+
+class AppTestW_SmallTupleObject(AppTestW_TupleObject):
+
+    def setup_class(cls):
+        cls.space = gettestobjspace(**{"objspace.std.withsmalltuple": True})
+        cls.w_issmall = cls.space.appexec([], """():
+            import __pypy__
+            def issmall(obj):
+                assert "SmallTuple" in __pypy__.internal_repr(obj)
+            return issmall
+        """)
+
+    def test_smalltuple(self):
+        self.issmall((1,2))
+        self.issmall((1,2,3))
+
+    def test_slicing_to_small(self):
+        self.issmall((1, 2, 3)[0:2])    # SmallTuple2
+        self.issmall((1, 2, 3)[0:2:1])
+
+        self.issmall((1, 2, 3, 4)[0:3])    # SmallTuple3
+        self.issmall((1, 2, 3, 4)[0:3:1])
+
+    def test_adding_to_small(self):
+        self.issmall((1,)+(2,))       # SmallTuple2
+        self.issmall((1,1)+(2,))      # SmallTuple3
+        self.issmall((1,)+(2,3))
+
+    def test_multiply_to_small(self):
+        self.issmall((1,)*2)
+        self.issmall((1,)*3)
+
+    def test_slicing_from_small(self):
+        assert (1,2)[0:1:1] == (1,)
+        assert (1,2,3)[0:2:1] == (1,2)
+
+    def test_eq(self):
+        a = (1,2,3)
+        b = (1,2,3)
+        assert a == b
+
+        c = (1,3,2)
+        assert a != c
+
+    def test_hash(self):
+        a = (1,2,3)
+        b = (1,2,3)
+        assert hash(a) == hash(b)
+
+        c = (1,3,2)
+        assert hash(a) != hash(c)
+
+class TestW_SmallTupleObject():
+
+    def setup_class(cls):
+        cls.space = gettestobjspace(**{"objspace.std.withsmalltuple": True})
+
+    def test_issmalltupleobject(self):
+        w_tuple = self.space.newtuple([self.space.wrap(1), self.space.wrap(2)])
+        assert isinstance(w_tuple, W_SmallTupleObject)
+
+    def test_hash_agains_normal_tuple(self):
+        normalspace = gettestobjspace(**{"objspace.std.withsmalltuple": False})
+        w_tuple = normalspace.newtuple([self.space.wrap(1), self.space.wrap(2)])
+
+        smallspace = gettestobjspace(**{"objspace.std.withsmalltuple": True})
+        w_smalltuple = smallspace.newtuple([self.space.wrap(1), self.space.wrap(2)])
+
+        assert isinstance(w_smalltuple, W_SmallTupleObject)
+        assert isinstance(w_tuple, W_TupleObject)
+        assert not normalspace.is_true(normalspace.eq(w_tuple, w_smalltuple))
+        assert smallspace.is_true(smallspace.eq(w_tuple, w_smalltuple))
+        assert smallspace.is_true(smallspace.eq(normalspace.hash(w_tuple), smallspace.hash(w_smalltuple)))
+
+    def test_setitem(self):
+        w_smalltuple = self.space.newtuple([self.space.wrap(1), self.space.wrap(2)])
+        w_smalltuple.setitem(0, self.space.wrap(5))
+        list_w = w_smalltuple.tolist()
+        assert len(list_w) == 2
+        assert self.space.eq_w(list_w[0], self.space.wrap(5))
+        assert self.space.eq_w(list_w[1], self.space.wrap(2))

pypy/objspace/std/tupleobject.py

     for i in range(slicelength):
         subitems[i] = items[start]
         start += step
-    return W_TupleObject(subitems)
+    return space.newtuple(subitems)
 
 def getslice__Tuple_ANY_ANY(space, w_tuple, w_start, w_stop):
     length = len(w_tuple.wrappeditems)
     start, stop = normalize_simple_slice(space, length, w_start, w_stop)
-    return W_TupleObject(w_tuple.wrappeditems[start:stop])
+    return space.newtuple(w_tuple.wrappeditems[start:stop])
 
 def contains__Tuple_ANY(space, w_tuple, w_obj):
     for w_item in w_tuple.wrappeditems:
 def add__Tuple_Tuple(space, w_tuple1, w_tuple2):
     items1 = w_tuple1.wrappeditems
     items2 = w_tuple2.wrappeditems
-    return W_TupleObject(items1 + items2)
+    return space.newtuple(items1 + items2)
 
 def mul_tuple_times(space, w_tuple, w_times):
     try:
     if times == 1 and space.type(w_tuple) == space.w_tuple:
         return w_tuple
     items = w_tuple.wrappeditems
-    return W_TupleObject(items * times)
+    return space.newtuple(items * times)
 
 def mul__Tuple_ANY(space, w_tuple, w_times):
     return mul_tuple_times(space, w_tuple, w_times)
     return intmask(x)
 
 def getnewargs__Tuple(space, w_tuple):
-    return space.newtuple([W_TupleObject(w_tuple.wrappeditems)])
+    return space.newtuple([space.newtuple(w_tuple.wrappeditems)])
 
 def tuple_count__Tuple_ANY(space, w_tuple, w_obj):
     count = 0

pypy/objspace/std/tupletype.py

 from pypy.objspace.std.register_all import register_all
 from pypy.objspace.std.stdtypedef import StdTypeDef, SMM
 
+def wraptuple(space, list_w):
+    from pypy.objspace.std.tupleobject import W_TupleObject
+    from pypy.objspace.std.smalltupleobject import W_SmallTupleObject2
+    from pypy.objspace.std.smalltupleobject import W_SmallTupleObject3
+    from pypy.objspace.std.smalltupleobject import W_SmallTupleObject4
+    from pypy.objspace.std.smalltupleobject import W_SmallTupleObject5
+    from pypy.objspace.std.smalltupleobject import W_SmallTupleObject6
+    from pypy.objspace.std.smalltupleobject import W_SmallTupleObject7
+    from pypy.objspace.std.smalltupleobject import W_SmallTupleObject8
+    if space.config.objspace.std.withsmalltuple:
+        if len(list_w) == 2:
+            return W_SmallTupleObject2(list_w)
+        if len(list_w) == 3:
+            return W_SmallTupleObject3(list_w)
+        if len(list_w) == 4:
+            return W_SmallTupleObject4(list_w)
+        if len(list_w) == 5:
+            return W_SmallTupleObject5(list_w)
+        if len(list_w) == 6:
+            return W_SmallTupleObject6(list_w)
+        if len(list_w) == 7:
+            return W_SmallTupleObject7(list_w)
+        if len(list_w) == 8:
+            return W_SmallTupleObject8(list_w)
+    return W_TupleObject(list_w)
 
 tuple_count = SMM("count", 2,
                   doc="count(obj) -> number of times obj appears in the tuple")
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.