Author: Armin Rigo <[email protected]>
Branch:
Changeset: r50378:0808b0899c34
Date: 2011-12-11 13:22 +0100
http://bitbucket.org/pypy/pypy/changeset/0808b0899c34/
Log: Merge the SpecialisedTuples branch, started by Mark W. P.
Add a number of interp-level classes for 'tuple', specialized to
some small number of items, e.g. (int, int), (int, object), etc.
It should be a win both memory-wise and speed-wise, as it reduces a
lot the number of reads and checks done to access tuple elements.
diff --git a/pypy/config/pypyoption.py b/pypy/config/pypyoption.py
--- a/pypy/config/pypyoption.py
+++ b/pypy/config/pypyoption.py
@@ -252,6 +252,10 @@
"use small tuples",
default=False),
+ BoolOption("withspecialisedtuple",
+ "use specialised tuples",
+ default=False),
+
BoolOption("withrope", "use ropes as the string implementation",
default=False,
requires=[("objspace.std.withstrslice", False),
@@ -365,6 +369,7 @@
config.objspace.std.suggest(optimized_list_getitem=True)
config.objspace.std.suggest(getattributeshortcut=True)
config.objspace.std.suggest(newshortcut=True)
+ config.objspace.std.suggest(withspecialisedtuple=True)
#if not IS_64_BITS:
# config.objspace.std.suggest(withsmalllong=True)
diff --git a/pypy/jit/tl/pypyjit_demo.py b/pypy/jit/tl/pypyjit_demo.py
--- a/pypy/jit/tl/pypyjit_demo.py
+++ b/pypy/jit/tl/pypyjit_demo.py
@@ -2,13 +2,15 @@
pypyjit.set_param(threshold=200)
+def g(*args):
+ return len(args)
+
def f(n):
- pairs = [(0.0, 1.0), (2.0, 3.0)] * n
- mag = 0
- for (x1, x2) in pairs:
- dx = x1 - x2
- mag += ((dx * dx ) ** (-1.5))
- return n
+ s = 0
+ for i in range(n):
+ l = [i, n, 2]
+ s += g(*l)
+ return s
try:
print f(301)
diff --git a/pypy/module/cpyext/methodobject.py
b/pypy/module/cpyext/methodobject.py
--- a/pypy/module/cpyext/methodobject.py
+++ b/pypy/module/cpyext/methodobject.py
@@ -14,7 +14,6 @@
METH_VARARGS, build_type_checkers, PyObjectFields, bootstrap_function)
from pypy.module.cpyext.pyerrors import PyErr_Occurred
from pypy.rlib.objectmodel import we_are_translated
-from pypy.objspace.std.tupleobject import W_TupleObject
PyCFunction_typedef = rffi.COpaquePtr(typedef='PyCFunction')
PyCFunction = lltype.Ptr(lltype.FuncType([PyObject, PyObject], PyObject))
diff --git a/pypy/module/cpyext/sequence.py b/pypy/module/cpyext/sequence.py
--- a/pypy/module/cpyext/sequence.py
+++ b/pypy/module/cpyext/sequence.py
@@ -42,11 +42,11 @@
which case o is returned. Use PySequence_Fast_GET_ITEM() to access the
members of the result. Returns NULL on failure. If the object is not a
sequence, raises TypeError with m as the message text."""
- if (space.is_true(space.isinstance(w_obj, space.w_list)) or
- space.is_true(space.isinstance(w_obj, space.w_tuple))):
+ if (isinstance(w_obj, listobject.W_ListObject) or
+ isinstance(w_obj, tupleobject.W_TupleObject)):
return w_obj
try:
- return space.newtuple(space.fixedview(w_obj))
+ return tupleobject.W_TupleObject(space.fixedview(w_obj))
except OperationError:
raise OperationError(space.w_TypeError, space.wrap(rffi.charp2str(m)))
diff --git a/pypy/module/cpyext/tupleobject.py
b/pypy/module/cpyext/tupleobject.py
--- a/pypy/module/cpyext/tupleobject.py
+++ b/pypy/module/cpyext/tupleobject.py
@@ -6,13 +6,12 @@
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")
@cpython_api([Py_ssize_t], PyObject)
def PyTuple_New(space, size):
- return space.newtuple([space.w_None] * size)
+ return W_TupleObject([space.w_None] * size)
@cpython_api([PyObject, Py_ssize_t, PyObject], rffi.INT_real, error=-1)
def PyTuple_SetItem(space, w_t, pos, w_obj):
@@ -24,12 +23,12 @@
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
+ # this function checks that w_t is really a W_TupleObject. It
+ # should only ever be called with a freshly built tuple from
+ # PyTuple_New(), which always return a W_TupleObject, even if there
+ # are also other implementations of tuples.
+ assert isinstance(w_t, W_TupleObject)
+ w_t.wrappeditems[pos] = w_obj
@cpython_api([PyObject, Py_ssize_t], PyObject)
def PyTuple_GetItem(space, w_t, pos):
diff --git a/pypy/objspace/std/model.py b/pypy/objspace/std/model.py
--- a/pypy/objspace/std/model.py
+++ b/pypy/objspace/std/model.py
@@ -15,6 +15,7 @@
_registered_implementations.add(implcls)
option_to_typename = {
+ "withspecialisedtuple" :
["specialisedtupleobject.W_SpecialisedTupleObject"],
"withsmalltuple" : ["smalltupleobject.W_SmallTupleObject"],
"withsmallint" : ["smallintobject.W_SmallIntObject"],
"withsmalllong" : ["smalllongobject.W_SmallLongObject"],
@@ -261,6 +262,11 @@
self.typeorder[smalltupleobject.W_SmallTupleObject] += [
(tupleobject.W_TupleObject,
smalltupleobject.delegate_SmallTuple2Tuple)]
+ if config.objspace.std.withspecialisedtuple:
+ from pypy.objspace.std import specialisedtupleobject
+ self.typeorder[specialisedtupleobject.W_SpecialisedTupleObject] +=
[
+ (tupleobject.W_TupleObject,
specialisedtupleobject.delegate_SpecialisedTuple2Tuple)]
+
# put W_Root everywhere
self.typeorder[W_Root] = []
for type in self.typeorder:
diff --git a/pypy/objspace/std/objspace.py b/pypy/objspace/std/objspace.py
--- a/pypy/objspace/std/objspace.py
+++ b/pypy/objspace/std/objspace.py
@@ -29,7 +29,7 @@
from pypy.objspace.std.sliceobject import W_SliceObject
from pypy.objspace.std.smallintobject import W_SmallIntObject
from pypy.objspace.std.stringobject import W_StringObject
-from pypy.objspace.std.tupleobject import W_TupleObject
+from pypy.objspace.std.tupleobject import W_AbstractTupleObject
from pypy.objspace.std.typeobject import W_TypeObject
# types
@@ -391,8 +391,8 @@
self.wrap("expected length %d, got %d" % (expected, got)))
def unpackiterable(self, w_obj, expected_length=-1):
- if isinstance(w_obj, W_TupleObject):
- t = w_obj.wrappeditems[:]
+ if isinstance(w_obj, W_AbstractTupleObject):
+ t = w_obj.getitems_copy()
elif isinstance(w_obj, W_ListObject):
t = w_obj.getitems_copy()
else:
@@ -405,8 +405,8 @@
def fixedview(self, w_obj, expected_length=-1, unroll=False):
""" Fast paths
"""
- if isinstance(w_obj, W_TupleObject):
- t = w_obj.wrappeditems
+ if isinstance(w_obj, W_AbstractTupleObject):
+ t = w_obj.tolist()
elif isinstance(w_obj, W_ListObject):
if unroll:
t = w_obj.getitems_unroll()
@@ -430,8 +430,8 @@
def listview(self, w_obj, expected_length=-1):
if isinstance(w_obj, W_ListObject):
t = w_obj.getitems()
- elif isinstance(w_obj, W_TupleObject):
- t = w_obj.wrappeditems[:]
+ elif isinstance(w_obj, W_AbstractTupleObject):
+ t = w_obj.getitems_copy()
else:
return ObjSpace.unpackiterable(self, w_obj, expected_length)
if expected_length != -1 and len(t) != expected_length:
diff --git a/pypy/objspace/std/smalltupleobject.py
b/pypy/objspace/std/smalltupleobject.py
--- a/pypy/objspace/std/smalltupleobject.py
+++ b/pypy/objspace/std/smalltupleobject.py
@@ -9,13 +9,14 @@
from pypy.interpreter import gateway
from pypy.rlib.debug import make_sure_not_resized
from pypy.rlib.unroll import unrolling_iterable
+from pypy.tool.sourcetools import func_with_new_name
from pypy.objspace.std.tupleobject import W_AbstractTupleObject, W_TupleObject
class W_SmallTupleObject(W_AbstractTupleObject):
from pypy.objspace.std.tupletype import tuple_typedef as typedef
- def tolist(self):
- raise NotImplementedError
+ #def tolist(self): --- inherited from W_AbstractTupleObject
+ # raise NotImplementedError
def length(self):
raise NotImplementedError
@@ -51,6 +52,9 @@
l[i] = getattr(self, 'w_value%s' % i)
return l
+ # same source code, but builds and returns a resizable list
+ getitems_copy = func_with_new_name(tolist, 'getitems_copy')
+
def length(self):
return n
diff --git a/pypy/objspace/std/specialisedtupleobject.py
b/pypy/objspace/std/specialisedtupleobject.py
new file mode 100644
--- /dev/null
+++ b/pypy/objspace/std/specialisedtupleobject.py
@@ -0,0 +1,302 @@
+from pypy.interpreter.error import OperationError
+from pypy.objspace.std.model import registerimplementation
+from pypy.objspace.std.register_all import register_all
+from pypy.objspace.std.multimethod import FailedToImplement
+from pypy.objspace.std.tupleobject import W_AbstractTupleObject
+from pypy.objspace.std.tupleobject import W_TupleObject
+from pypy.objspace.std.sliceobject import W_SliceObject, normalize_simple_slice
+from pypy.rlib.rarithmetic import intmask
+from pypy.rlib.objectmodel import compute_hash
+from pypy.rlib.unroll import unrolling_iterable
+from pypy.tool.sourcetools import func_with_new_name
+
+class NotSpecialised(Exception):
+ pass
+
+class W_SpecialisedTupleObject(W_AbstractTupleObject):
+ from pypy.objspace.std.tupletype import tuple_typedef as typedef
+ __slots__ = []
+
+ def __repr__(self):
+ """ representation for debugging purposes """
+ reprlist = [repr(item) for item in self._to_unwrapped_list()]
+ return "%s(%s)" % (self.__class__.__name__, ', '.join(reprlist))
+
+ #def tolist(self): --- inherited from W_AbstractTupleObject
+ # raise NotImplementedError
+
+ def _to_unwrapped_list(self):
+ "NOT_RPYTHON"
+ 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(self, space):
+ return tuple(self._to_unwrapped_list())
+
+ def delegating(self):
+ pass # for tests only
+
+
+def make_specialised_class(typetuple):
+ assert type(typetuple) == tuple
+
+ nValues = len(typetuple)
+ iter_n = unrolling_iterable(range(nValues))
+
+ class cls(W_SpecialisedTupleObject):
+ def __init__(self, space, *values_w):
+ self.space = space
+ assert len(values_w) == nValues
+ for i in iter_n:
+ w_obj = values_w[i]
+ val_type = typetuple[i]
+ if val_type == int:
+ unwrapped = space.int_w(w_obj)
+ elif val_type == float:
+ unwrapped = space.float_w(w_obj)
+ elif val_type == str:
+ unwrapped = space.str_w(w_obj)
+ elif val_type == object:
+ unwrapped = w_obj
+ else:
+ raise AssertionError
+ setattr(self, 'value%s' % i, unwrapped)
+
+ def length(self):
+ return nValues
+
+ def tolist(self):
+ list_w = [None] * nValues
+ for i in iter_n:
+ value = getattr(self, 'value%s' % i)
+ if typetuple[i] != object:
+ value = self.space.wrap(value)
+ list_w[i] = value
+ return list_w
+
+ # same source code, but builds and returns a resizable list
+ getitems_copy = func_with_new_name(tolist, 'getitems_copy')
+
+ def _to_unwrapped_list(self):
+ "NOT_RPYTHON"
+ list_w = [None] * nValues
+ for i in iter_n:
+ value = getattr(self, 'value%s' % i)
+ if typetuple[i] == object:
+ value = self.space.unwrap(value)
+ list_w[i] = value
+ return list_w
+
+ def hash(self, space):
+ # XXX duplicate logic from tupleobject.py
+ mult = 1000003
+ x = 0x345678
+ z = nValues
+ for i in iter_n:
+ value = getattr(self, 'value%s' % i)
+ if typetuple[i] == object:
+ y = space.int_w(space.hash(value))
+ elif typetuple[i] == float:
+ # get the correct hash for float which is an
+ # integer & other less frequent cases
+ from pypy.objspace.std.floatobject import _hash_float
+ y = _hash_float(space, value)
+ else:
+ y = compute_hash(value)
+ x = (x ^ y) * mult
+ z -= 1
+ mult += 82520 + z + z
+ x += 97531
+ return space.wrap(intmask(x))
+
+ def _eq(self, w_other):
+ if not isinstance(w_other, cls):
+ # if we are not comparing same types, give up
+ raise FailedToImplement
+ for i in iter_n:
+ myval = getattr(self, 'value%s' % i)
+ otherval = getattr(w_other, 'value%s' % i)
+ if typetuple[i] == object:
+ if not self.space.eq_w(myval, otherval):
+ return False
+ else:
+ if myval != otherval:
+ return False
+ else:
+ return True
+
+ def eq(self, space, w_other):
+ return space.newbool(self._eq(w_other))
+
+ def ne(self, space, w_other):
+ return space.newbool(not self._eq(w_other))
+
+## def _compare(self, compare_op, w_other):
+## if not isinstance(w_other, cls):
+## raise FailedToImplement
+## ncmp = min(self.length(), w_other.length())
+## for i in iter_n:
+## if typetuple[i] == Any:#like space.eq on wrapped or two
params?
+## raise FailedToImplement
+## if ncmp > i:
+## l_val = getattr(self, 'value%s' % i)
+## r_val = getattr(w_other, 'value%s' % i)
+## if l_val != r_val:
+## return compare_op(l_val, r_val)
+## return compare_op(self.length(), w_other.length())
+
+ def getitem(self, index):
+ for i in iter_n:
+ if index == i:
+ value = getattr(self, 'value%s' % i)
+ if typetuple[i] != object:
+ value = self.space.wrap(value)
+ return value
+ raise IndexError
+
+ cls.__name__ = ('W_SpecialisedTupleObject_' +
+ ''.join([t.__name__[0] for t in typetuple]))
+ _specialisations.append(cls)
+ return cls
+
+# ---------- current specialized versions ----------
+
+_specialisations = []
+Cls_ii = make_specialised_class((int, int))
+Cls_is = make_specialised_class((int, str))
+Cls_io = make_specialised_class((int, object))
+Cls_si = make_specialised_class((str, int))
+Cls_ss = make_specialised_class((str, str))
+Cls_so = make_specialised_class((str, object))
+Cls_oi = make_specialised_class((object, int))
+Cls_os = make_specialised_class((object, str))
+Cls_oo = make_specialised_class((object, object))
+Cls_ff = make_specialised_class((float, float))
+Cls_ooo = make_specialised_class((object, object, object))
+
+def makespecialisedtuple(space, list_w):
+ if len(list_w) == 2:
+ w_arg1, w_arg2 = list_w
+ w_type1 = space.type(w_arg1)
+ w_type2 = space.type(w_arg2)
+ #
+ if w_type1 is space.w_int:
+ if w_type2 is space.w_int:
+ return Cls_ii(space, w_arg1, w_arg2)
+ elif w_type2 is space.w_str:
+ return Cls_is(space, w_arg1, w_arg2)
+ else:
+ return Cls_io(space, w_arg1, w_arg2)
+ #
+ elif w_type1 is space.w_str:
+ if w_type2 is space.w_int:
+ return Cls_si(space, w_arg1, w_arg2)
+ elif w_type2 is space.w_str:
+ return Cls_ss(space, w_arg1, w_arg2)
+ else:
+ return Cls_so(space, w_arg1, w_arg2)
+ #
+ elif w_type1 is space.w_float and w_type2 is space.w_float:
+ return Cls_ff(space, w_arg1, w_arg2)
+ #
+ else:
+ if w_type2 is space.w_int:
+ return Cls_oi(space, w_arg1, w_arg2)
+ elif w_type2 is space.w_str:
+ return Cls_os(space, w_arg1, w_arg2)
+ else:
+ return Cls_oo(space, w_arg1, w_arg2)
+ #
+ elif len(list_w) == 3:
+ return Cls_ooo(space, list_w[0], list_w[1], list_w[2])
+ else:
+ raise NotSpecialised
+
+# ____________________________________________________________
+
+registerimplementation(W_SpecialisedTupleObject)
+
+def delegate_SpecialisedTuple2Tuple(space, w_specialised):
+ w_specialised.delegating()
+ return W_TupleObject(w_specialised.tolist())
+
+def len__SpecialisedTuple(space, w_tuple):
+ return space.wrap(w_tuple.length())
+
+def getitem__SpecialisedTuple_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__SpecialisedTuple_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_specialisedtuple_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__SpecialisedTuple_ANY(space, w_tuple, w_times):
+ return mul_specialisedtuple_times(space, w_tuple, w_times)
+
+def mul__ANY_SpecialisedTuple(space, w_times, w_tuple):
+ return mul_specialisedtuple_times(space, w_tuple, w_times)
+
+def eq__SpecialisedTuple_SpecialisedTuple(space, w_tuple1, w_tuple2):
+ return w_tuple1.eq(space, w_tuple2)
+
+def ne__SpecialisedTuple_SpecialisedTuple(space, w_tuple1, w_tuple2):
+ return w_tuple1.ne(space, w_tuple2)
+
+##from operator import lt, le, ge, gt
+
+##def lt__SpecialisedTuple_SpecialisedTuple(space, w_tuple1, w_tuple2):
+## return space.newbool(w_tuple1._compare(lt, w_tuple2))
+
+##def le__SpecialisedTuple_SpecialisedTuple(space, w_tuple1, w_tuple2):
+## return space.newbool(w_tuple1._compare(le, w_tuple2))
+
+##def ge__SpecialisedTuple_SpecialisedTuple(space, w_tuple1, w_tuple2):
+## return space.newbool(w_tuple1._compare(ge, w_tuple2))
+
+##def gt__SpecialisedTuple_SpecialisedTuple(space, w_tuple1, w_tuple2):
+## return space.newbool(w_tuple1._compare(gt, w_tuple2))
+
+def hash__SpecialisedTuple(space, w_tuple):
+ return w_tuple.hash(space)
+
+from pypy.objspace.std import tupletype
+register_all(vars(), tupletype)
diff --git a/pypy/objspace/std/test/test_specialisedtupleobject.py
b/pypy/objspace/std/test/test_specialisedtupleobject.py
new file mode 100644
--- /dev/null
+++ b/pypy/objspace/std/test/test_specialisedtupleobject.py
@@ -0,0 +1,234 @@
+import py, sys
+from pypy.objspace.std.tupleobject import W_TupleObject
+from pypy.objspace.std.specialisedtupleobject import W_SpecialisedTupleObject
+from pypy.objspace.std.specialisedtupleobject import _specialisations
+from pypy.interpreter.error import OperationError
+from pypy.conftest import gettestobjspace, option
+from pypy.objspace.std.test import test_tupleobject
+from pypy.interpreter import gateway
+
+
+for cls in _specialisations:
+ globals()[cls.__name__] = cls
+
+
+class TestW_SpecialisedTupleObject():
+
+ def setup_class(cls):
+ cls.space = gettestobjspace(**{"objspace.std.withspecialisedtuple":
True})
+
+ def test_isspecialisedtupleobjectintint(self):
+ w_tuple = self.space.newtuple([self.space.wrap(1), self.space.wrap(2)])
+ assert isinstance(w_tuple, W_SpecialisedTupleObject_ii)
+
+ def test_isnotspecialisedtupleobject(self):
+ w_tuple = self.space.newtuple([self.space.wrap({})])
+ assert not isinstance(w_tuple, W_SpecialisedTupleObject)
+
+ def test_specialisedtupleclassname(self):
+ w_tuple = self.space.newtuple([self.space.wrap(1), self.space.wrap(2)])
+ assert w_tuple.__class__.__name__ == 'W_SpecialisedTupleObject_ii'
+
+ def test_hash_against_normal_tuple(self):
+ N_space = gettestobjspace(**{"objspace.std.withspecialisedtuple":
False})
+ S_space = gettestobjspace(**{"objspace.std.withspecialisedtuple":
True})
+
+ def hash_test(values):
+ N_values_w = [N_space.wrap(value) for value in values]
+ S_values_w = [S_space.wrap(value) for value in values]
+ N_w_tuple = N_space.newtuple(N_values_w)
+ S_w_tuple = S_space.newtuple(S_values_w)
+
+ assert isinstance(S_w_tuple, W_SpecialisedTupleObject)
+ assert isinstance(N_w_tuple, W_TupleObject)
+ assert not N_space.is_true(N_space.eq(N_w_tuple, S_w_tuple))
+ assert S_space.is_true(S_space.eq(N_w_tuple, S_w_tuple))
+ assert S_space.is_true(S_space.eq(N_space.hash(N_w_tuple),
S_space.hash(S_w_tuple)))
+
+ hash_test([1,2])
+ hash_test([1.5,2.8])
+ hash_test([1.0,2.0])
+ hash_test(['arbitrary','strings'])
+ hash_test([1,(1,2,3,4)])
+ hash_test([1,(1,2)])
+ hash_test([1,('a',2)])
+ hash_test([1,()])
+ hash_test([1,2,3])
+
+
+class AppTestW_SpecialisedTupleObject:
+
+ def setup_class(cls):
+ cls.space = gettestobjspace(**{"objspace.std.withspecialisedtuple":
True})
+ def forbid_delegation(space, w_tuple):
+ def delegation_forbidden():
+ # haaaack
+ co = sys._getframe(2).f_code
+ if co.co_name.startswith('_mm_repr_tuple'):
+ return
+ raise OperationError(space.w_ReferenceError, w_tuple)
+ w_tuple.delegating = delegation_forbidden
+ return w_tuple
+ if option.runappdirect:
+ cls.w_forbid_delegation = lambda self, x: x
+ cls.test_delegation = lambda self: skip("runappdirect")
+ else:
+ cls.w_forbid_delegation = cls.space.wrap(
+ gateway.interp2app(forbid_delegation))
+
+ def w_isspecialised(self, obj, expected=''):
+ import __pypy__
+ r = __pypy__.internal_repr(obj)
+ print obj, '==>', r, ' (expected: %r)' % expected
+ return ("SpecialisedTupleObject" + expected) in r
+
+ def test_createspecialisedtuple(self):
+ spec = {int: 'i',
+ float: 'f',
+ str: 's',
+ list: 'o'}
+ #
+ for x in [42, 4.2, "foo", []]:
+ for y in [43, 4.3, "bar", []]:
+ expected1 = spec[type(x)]
+ expected2 = spec[type(y)]
+ if (expected1 == 'f') ^ (expected2 == 'f'):
+ if expected1 == 'f': expected1 = 'o'
+ if expected2 == 'f': expected2 = 'o'
+ obj = (x, y)
+ assert self.isspecialised(obj, '_' + expected1 + expected2)
+ #
+ obj = (1, 2, 3)
+ assert self.isspecialised(obj, '_ooo')
+
+ def test_delegation(self):
+ t = self.forbid_delegation((42, 43))
+ raises(ReferenceError, t.__getslice__, 0, 1)
+
+ def test_len(self):
+ t = self.forbid_delegation((42,43))
+ assert len(t) == 2
+
+ def test_notspecialisedtuple(self):
+ assert not self.isspecialised((42,43,44,45))
+ assert not self.isspecialised((1.5,))
+
+ def test_slicing_to_specialised(self):
+ t = (1, 2, 3)
+ assert self.isspecialised(t[0:2])
+ t = (1, '2', 3)
+ assert self.isspecialised(t[0:5:2])
+
+ def test_adding_to_specialised(self):
+ t = (1,)
+ assert self.isspecialised(t + (2,))
+
+ def test_multiply_to_specialised(self):
+ t = (1,)
+ assert self.isspecialised(t * 2)
+
+ def test_slicing_from_specialised(self):
+ t = (1, 2, 3)
+ assert t[0:2:1] == (1, 2)
+
+ def test_eq_no_delegation(self):
+ t = (1,)
+ a = self.forbid_delegation(t + (2,))
+ b = (1, 2)
+ assert a == b
+
+ c = (2, 1)
+ assert not a == c
+
+ def test_eq_can_delegate(self):
+ a = (1,2)
+ b = (1,3,2)
+ assert not a == b
+
+ values = [2, 2L, 2.0, 1, 1L, 1.0]
+ for x in values:
+ for y in values:
+ assert ((1,2) == (x,y)) == (1 == x and 2 == y)
+
+ def test_neq(self):
+ a = self.forbid_delegation((1,2))
+ b = (1,)
+ b = b+(2,)
+ assert not a != b
+
+ c = (1,3)
+ assert a != c
+
+ def test_ordering(self):
+ a = (1,2) #self.forbid_delegation((1,2)) --- code commented out
+ assert a < (2,2)
+ assert a < (1,3)
+ assert not a < (1,2)
+
+ assert a <= (2,2)
+ assert a <= (1,2)
+ assert not a <= (1,1)
+
+ assert a >= (0,2)
+ assert a >= (1,2)
+ assert not a >= (1,3)
+
+ assert a > (0,2)
+ assert a > (1,1)
+ assert not a > (1,3)
+
+ assert (2,2) > a
+ assert (1,3) > a
+ assert not (1,2) > a
+
+ assert (2,2) >= a
+ assert (1,2) >= a
+ assert not (1,1) >= a
+
+ assert (0,2) <= a
+ assert (1,2) <= a
+ assert not (1,3) <= a
+
+ assert (0,2) < a
+ assert (1,1) < a
+ assert not (1,3) < a
+
+ def test_hash(self):
+ a = (1,2)
+ b = (1,)
+ b += (2,) # else a and b refer to same constant
+ assert hash(a) == hash(b)
+
+ c = (2,4)
+ assert hash(a) != hash(c)
+
+ assert hash(a) == hash((1L, 2L)) == hash((1.0, 2.0)) == hash((1.0, 2L))
+
+ def test_getitem(self):
+ t = self.forbid_delegation((5,3))
+ assert (t)[0] == 5
+ assert (t)[1] == 3
+ assert (t)[-1] == 3
+ assert (t)[-2] == 5
+ raises(IndexError, "t[2]")
+ raises(IndexError, "t[-3]")
+
+ def test_three_tuples(self):
+ b = self.forbid_delegation((1, 2, 3))
+ c = (1,)
+ d = c + (2, 3)
+ assert self.isspecialised(d)
+ assert b == d
+
+ def test_mongrel(self):
+ a = self.forbid_delegation((1, 2.2, '333'))
+ assert self.isspecialised(a)
+ assert len(a) == 3
+ assert a[0] == 1 and a[1] == 2.2 and a[2] == '333'
+ b = ('333',)
+ assert a == (1, 2.2,) + b
+ assert not a != (1, 2.2) + b
+
+
+class AppTestAll(test_tupleobject.AppTestW_TupleObject):
+ pass
diff --git a/pypy/objspace/std/test/test_tupleobject.py
b/pypy/objspace/std/test/test_tupleobject.py
--- a/pypy/objspace/std/test/test_tupleobject.py
+++ b/pypy/objspace/std/test/test_tupleobject.py
@@ -280,6 +280,8 @@
assert () * 10 == ()
assert (5,) * 3 == (5,5,5)
assert (5,2) * 2 == (5,2,5,2)
+
+ def test_mul_identity(self):
t = (1,2,3)
assert (t * 1) is t
diff --git a/pypy/objspace/std/tupleobject.py b/pypy/objspace/std/tupleobject.py
--- a/pypy/objspace/std/tupleobject.py
+++ b/pypy/objspace/std/tupleobject.py
@@ -12,6 +12,15 @@
class W_AbstractTupleObject(W_Object):
__slots__ = ()
+ def tolist(self):
+ "Returns the items, as a fixed-size list."
+ raise NotImplementedError
+
+ def getitems_copy(self):
+ "Returns a copy of the items, as a resizable list."
+ raise NotImplementedError
+
+
class W_TupleObject(W_AbstractTupleObject):
from pypy.objspace.std.tupletype import tuple_typedef as typedef
_immutable_fields_ = ['wrappeditems[*]']
@@ -29,6 +38,12 @@
items = [space.unwrap(w_item) for w_item in w_tuple.wrappeditems]
return tuple(items)
+ def tolist(self):
+ return self.wrappeditems
+
+ def getitems_copy(self):
+ return self.wrappeditems[:] # returns a resizable list
+
registerimplementation(W_TupleObject)
diff --git a/pypy/objspace/std/tupletype.py b/pypy/objspace/std/tupletype.py
--- a/pypy/objspace/std/tupletype.py
+++ b/pypy/objspace/std/tupletype.py
@@ -5,6 +5,14 @@
def wraptuple(space, list_w):
from pypy.objspace.std.tupleobject import W_TupleObject
+
+ if space.config.objspace.std.withspecialisedtuple:
+ from specialisedtupleobject import makespecialisedtuple, NotSpecialised
+ try:
+ return makespecialisedtuple(space, list_w)
+ except NotSpecialised:
+ pass
+
if space.config.objspace.std.withsmalltuple:
from pypy.objspace.std.smalltupleobject import W_SmallTupleObject2
from pypy.objspace.std.smalltupleobject import W_SmallTupleObject3
diff --git a/pypy/tool/pytest/appsupport.py b/pypy/tool/pytest/appsupport.py
--- a/pypy/tool/pytest/appsupport.py
+++ b/pypy/tool/pytest/appsupport.py
@@ -63,7 +63,10 @@
exec_ = eval
def repr(self, w_value):
- return self.space.unwrap(self.space.repr(w_value))
+ try:
+ return self.space.unwrap(self.space.repr(w_value))
+ except Exception, e:
+ return "<Sorry, exception while trying to do repr, %r>"%e
def is_true(self, w_value):
return self.space.is_true(w_value)
_______________________________________________
pypy-commit mailing list
[email protected]
http://mail.python.org/mailman/listinfo/pypy-commit