Author: Armin Rigo <[email protected]>
Branch:
Changeset: r61164:78787a1a14be
Date: 2013-02-13 10:24 +0100
http://bitbucket.org/pypy/pypy/changeset/78787a1a14be/
Log: Support cffi's "slicing" branch
diff --git a/pypy/module/_cffi_backend/cdataobj.py
b/pypy/module/_cffi_backend/cdataobj.py
--- a/pypy/module/_cffi_backend/cdataobj.py
+++ b/pypy/module/_cffi_backend/cdataobj.py
@@ -119,9 +119,12 @@
def getitem(self, w_index):
space = self.space
- i = space.getindex_w(w_index, space.w_IndexError)
- ctype = self.ctype._check_subscript_index(self, i)
- w_o = self._do_getitem(ctype, i)
+ if space.isinstance_w(w_index, space.w_slice):
+ w_o = self._do_getslice(w_index)
+ else:
+ i = space.getindex_w(w_index, space.w_IndexError)
+ ctype = self.ctype._check_subscript_index(self, i)
+ w_o = self._do_getitem(ctype, i)
keepalive_until_here(self)
return w_o
@@ -132,14 +135,100 @@
def setitem(self, w_index, w_value):
space = self.space
- i = space.getindex_w(w_index, space.w_IndexError)
- ctype = self.ctype._check_subscript_index(self, i)
- ctitem = ctype.ctitem
- ctitem.convert_from_object(
- rffi.ptradd(self._cdata, i * ctitem.size),
- w_value)
+ if space.isinstance_w(w_index, space.w_slice):
+ self._do_setslice(w_index, w_value)
+ else:
+ i = space.getindex_w(w_index, space.w_IndexError)
+ ctype = self.ctype._check_subscript_index(self, i)
+ ctitem = ctype.ctitem
+ ctitem.convert_from_object(
+ rffi.ptradd(self._cdata, i * ctitem.size),
+ w_value)
keepalive_until_here(self)
+ def _do_getslicearg(self, w_slice):
+ from pypy.module._cffi_backend.ctypeptr import W_CTypePointer
+ from pypy.objspace.std.sliceobject import W_SliceObject
+ assert isinstance(w_slice, W_SliceObject)
+ space = self.space
+ #
+ if space.is_w(w_slice.w_start, space.w_None):
+ raise OperationError(space.w_IndexError,
+ space.wrap("slice start must be specified"))
+ start = space.int_w(w_slice.w_start)
+ #
+ if space.is_w(w_slice.w_stop, space.w_None):
+ raise OperationError(space.w_IndexError,
+ space.wrap("slice stop must be specified"))
+ stop = space.int_w(w_slice.w_stop)
+ #
+ if not space.is_w(w_slice.w_step, space.w_None):
+ raise OperationError(space.w_IndexError,
+ space.wrap("slice with step not supported"))
+ #
+ if start > stop:
+ raise OperationError(space.w_IndexError,
+ space.wrap("slice start > stop"))
+ #
+ ctype = self.ctype._check_slice_index(self, start, stop)
+ assert isinstance(ctype, W_CTypePointer)
+ #
+ return ctype, start, stop - start
+
+ def _do_getslice(self, w_slice):
+ ctptr, start, length = self._do_getslicearg(w_slice)
+ #
+ space = self.space
+ ctarray = ctptr.cache_array_type
+ if ctarray is None:
+ from pypy.module._cffi_backend import newtype
+ ctarray = newtype.new_array_type(space, ctptr, space.w_None)
+ ctptr.cache_array_type = ctarray
+ #
+ p = rffi.ptradd(self._cdata, start * ctarray.ctitem.size)
+ return W_CDataSliced(space, p, ctarray, length)
+
+ def _do_setslice(self, w_slice, w_value):
+ ctptr, start, length = self._do_getslicearg(w_slice)
+ ctitem = ctptr.ctitem
+ ctitemsize = ctitem.size
+ cdata = rffi.ptradd(self._cdata, start * ctitemsize)
+ #
+ if isinstance(w_value, W_CData):
+ from pypy.module._cffi_backend import ctypearray
+ ctv = w_value.ctype
+ if (isinstance(ctv, ctypearray.W_CTypeArray) and
+ ctv.ctitem is ctitem and
+ w_value.get_array_length() == length):
+ # fast path: copying from exactly the correct type
+ s = w_value._cdata
+ for i in range(ctitemsize * length):
+ cdata[i] = s[i]
+ keepalive_until_here(w_value)
+ return
+ #
+ space = self.space
+ w_iter = space.iter(w_value)
+ for i in range(length):
+ try:
+ w_item = space.next(w_iter)
+ except OperationError, e:
+ if not e.match(space, space.w_StopIteration):
+ raise
+ raise operationerrfmt(space.w_ValueError,
+ "need %d values to unpack, got %d",
+ length, i)
+ ctitem.convert_from_object(cdata, w_item)
+ cdata = rffi.ptradd(cdata, ctitemsize)
+ try:
+ space.next(w_iter)
+ except OperationError, e:
+ if not e.match(space, space.w_StopIteration):
+ raise
+ else:
+ raise operationerrfmt(space.w_ValueError,
+ "got more than %d values to unpack", length)
+
def _add_or_sub(self, w_other, sign):
space = self.space
i = sign * space.getindex_w(w_other, space.w_OverflowError)
@@ -281,6 +370,22 @@
return self.structobj
+class W_CDataSliced(W_CData):
+ """Subclass with an explicit length, for slices."""
+ _attrs_ = ['length']
+ _immutable_fields_ = ['length']
+
+ def __init__(self, space, cdata, ctype, length):
+ W_CData.__init__(self, space, cdata, ctype)
+ self.length = length
+
+ def _repr_extra(self):
+ return "sliced length %d" % (self.length,)
+
+ def get_array_length(self):
+ return self.length
+
+
W_CData.typedef = TypeDef(
'CData',
__module__ = '_cffi_backend',
diff --git a/pypy/module/_cffi_backend/ctypearray.py
b/pypy/module/_cffi_backend/ctypearray.py
--- a/pypy/module/_cffi_backend/ctypearray.py
+++ b/pypy/module/_cffi_backend/ctypearray.py
@@ -76,6 +76,17 @@
self.name, i, w_cdata.get_array_length())
return self
+ def _check_slice_index(self, w_cdata, start, stop):
+ space = self.space
+ if start < 0:
+ raise OperationError(space.w_IndexError,
+ space.wrap("negative index not supported"))
+ if stop > w_cdata.get_array_length():
+ raise operationerrfmt(space.w_IndexError,
+ "index too large (expected %d <= %d)",
+ stop, w_cdata.get_array_length())
+ return self.ctptr
+
def convert_from_object(self, cdata, w_ob):
self.convert_array_from_object(cdata, w_ob)
diff --git a/pypy/module/_cffi_backend/ctypeobj.py
b/pypy/module/_cffi_backend/ctypeobj.py
--- a/pypy/module/_cffi_backend/ctypeobj.py
+++ b/pypy/module/_cffi_backend/ctypeobj.py
@@ -93,12 +93,18 @@
"not %s", self.name, expected,
space.type(w_got).getname(space))
- def _check_subscript_index(self, w_cdata, i):
+ def _cannot_index(self):
space = self.space
raise operationerrfmt(space.w_TypeError,
"cdata of type '%s' cannot be indexed",
self.name)
+ def _check_subscript_index(self, w_cdata, i):
+ raise self._cannot_index()
+
+ def _check_slice_index(self, w_cdata, start, stop):
+ raise self._cannot_index()
+
def string(self, cdataobj, maxlen):
space = self.space
raise operationerrfmt(space.w_TypeError,
diff --git a/pypy/module/_cffi_backend/ctypeptr.py
b/pypy/module/_cffi_backend/ctypeptr.py
--- a/pypy/module/_cffi_backend/ctypeptr.py
+++ b/pypy/module/_cffi_backend/ctypeptr.py
@@ -174,9 +174,10 @@
class W_CTypePointer(W_CTypePtrBase):
- _attrs_ = ['is_file']
+ _attrs_ = ['is_file', 'cache_array_type']
_immutable_fields_ = ['is_file']
kind = "pointer"
+ cache_array_type = None
def __init__(self, space, ctitem):
from pypy.module._cffi_backend import ctypearray
@@ -224,6 +225,9 @@
self.name)
return self
+ def _check_slice_index(self, w_cdata, start, stop):
+ return self
+
def add(self, cdata, i):
space = self.space
ctitem = self.ctitem
diff --git a/pypy/module/_cffi_backend/test/_backend_test_c.py
b/pypy/module/_cffi_backend/test/_backend_test_c.py
--- a/pypy/module/_cffi_backend/test/_backend_test_c.py
+++ b/pypy/module/_cffi_backend/test/_backend_test_c.py
@@ -1284,22 +1284,24 @@
def test_cast_to_enum():
BEnum = new_enum_type("foo", ('def', 'c', 'ab'), (0, 1, -20))
e = cast(BEnum, 0)
- assert repr(e) == "<cdata 'enum foo' 'def'>"
+ assert repr(e) == "<cdata 'enum foo' 0: def>"
+ assert repr(cast(BEnum, -42)) == "<cdata 'enum foo' -42>"
+ assert repr(cast(BEnum, -20)) == "<cdata 'enum foo' -20: ab>"
assert string(e) == 'def'
assert string(cast(BEnum, -20)) == 'ab'
- assert string(cast(BEnum, 'c')) == 'c'
- assert int(cast(BEnum, 'c')) == 1
- assert int(cast(BEnum, 'def')) == 0
+ assert int(cast(BEnum, 1)) == 1
+ assert int(cast(BEnum, 0)) == 0
assert int(cast(BEnum, -242 + 2**128)) == -242
- assert string(cast(BEnum, -242 + 2**128)) == '#-242'
- assert string(cast(BEnum, '#-20')) == 'ab'
- assert repr(cast(BEnum, '#-20')) == "<cdata 'enum foo' 'ab'>"
- assert repr(cast(BEnum, '#-21')) == "<cdata 'enum foo' '#-21'>"
+ assert string(cast(BEnum, -242 + 2**128)) == '-242'
+ #
+ BEnum = new_enum_type("bar", ('def', 'c', 'ab'), (0, 1, 20))
+ e = cast(BEnum, -1)
+ assert repr(e) == "<cdata 'enum bar' 4294967295>" # unsigned int
def test_enum_with_non_injective_mapping():
BEnum = new_enum_type("foo", ('ab', 'cd'), (7, 7))
e = cast(BEnum, 7)
- assert repr(e) == "<cdata 'enum foo' 'ab'>"
+ assert repr(e) == "<cdata 'enum foo' 7: ab>"
assert string(e) == 'ab'
def test_enum_in_struct():
@@ -1308,36 +1310,111 @@
BStructPtr = new_pointer_type(BStruct)
complete_struct_or_union(BStruct, [('a1', BEnum, -1)])
p = newp(BStructPtr, [-20])
- assert p.a1 == "ab"
- p = newp(BStructPtr, ["c"])
- assert p.a1 == "c"
+ assert p.a1 == -20
+ p = newp(BStructPtr, [12])
+ assert p.a1 == 12
e = py.test.raises(TypeError, newp, BStructPtr, [None])
- assert "must be a str or int, not NoneType" in str(e.value)
+ assert "an integer is required" in str(e.value)
+ py.test.raises(TypeError, 'p.a1 = "def"')
if sys.version_info < (3,):
- p.a1 = unicode("def")
- assert p.a1 == "def" and type(p.a1) is str
- py.test.raises(UnicodeEncodeError, "p.a1 = unichr(1234)")
BEnum2 = new_enum_type(unicode("foo"), (unicode('abc'),), (5,))
- assert string(cast(BEnum2, unicode('abc'))) == 'abc'
+ assert string(cast(BEnum2, 5)) == 'abc'
+ assert type(string(cast(BEnum2, 5))) is str
def test_enum_overflow():
- for ovf in (2**63, -2**63-1, 2**31, -2**31-1):
- e = py.test.raises(OverflowError, new_enum_type, "foo", ('a', 'b'),
- (5, ovf))
- assert str(e.value) == (
- "enum 'foo' declaration for 'b' does not fit an int")
+ max_uint = 2 ** (size_of_int()*8) - 1
+ max_int = max_uint // 2
+ max_ulong = 2 ** (size_of_long()*8) - 1
+ max_long = max_ulong // 2
+ # 'unsigned int' case
+ e = new_enum_type("foo", ('a', 'b'), (0, 3))
+ assert sizeof(e) == size_of_int()
+ assert int(cast(e, -1)) == max_uint # 'e' is unsigned
+ e = new_enum_type("foo", ('a', 'b'), (0, max_uint))
+ assert sizeof(e) == size_of_int()
+ assert int(cast(e, -1)) == max_uint
+ assert e.elements == {0: 'a', max_uint: 'b'}
+ assert e.relements == {'a': 0, 'b': max_uint}
+ # 'signed int' case
+ e = new_enum_type("foo", ('a', 'b'), (-1, max_int))
+ assert sizeof(e) == size_of_int()
+ assert int(cast(e, -1)) == -1
+ assert e.elements == {-1: 'a', max_int: 'b'}
+ assert e.relements == {'a': -1, 'b': max_int}
+ e = new_enum_type("foo", ('a', 'b'), (-max_int-1, max_int))
+ assert sizeof(e) == size_of_int()
+ assert int(cast(e, -1)) == -1
+ assert e.elements == {-max_int-1: 'a', max_int: 'b'}
+ assert e.relements == {'a': -max_int-1, 'b': max_int}
+ # 'unsigned long' case
+ e = new_enum_type("foo", ('a', 'b'), (0, max_long))
+ assert sizeof(e) == size_of_long()
+ assert int(cast(e, -1)) == max_ulong # 'e' is unsigned
+ e = new_enum_type("foo", ('a', 'b'), (0, max_ulong))
+ assert sizeof(e) == size_of_long()
+ assert int(cast(e, -1)) == max_ulong
+ assert e.elements == {0: 'a', max_ulong: 'b'}
+ assert e.relements == {'a': 0, 'b': max_ulong}
+ # 'signed long' case
+ e = new_enum_type("foo", ('a', 'b'), (-1, max_long))
+ assert sizeof(e) == size_of_long()
+ assert int(cast(e, -1)) == -1
+ assert e.elements == {-1: 'a', max_long: 'b'}
+ assert e.relements == {'a': -1, 'b': max_long}
+ e = new_enum_type("foo", ('a', 'b'), (-max_long-1, max_long))
+ assert sizeof(e) == size_of_long()
+ assert int(cast(e, -1)) == -1
+ assert e.elements == {-max_long-1: 'a', max_long: 'b'}
+ assert e.relements == {'a': -max_long-1, 'b': max_long}
+ # overflow: both negative items and items larger than max_long
+ e = py.test.raises(OverflowError, new_enum_type, "foo", ('a', 'b'),
+ (-1, max_long + 1))
+ assert str(e.value) == (
+ "enum 'foo' values don't all fit into either 'long' "
+ "or 'unsigned long'")
+ # overflow: items smaller than -max_long-1
+ e = py.test.raises(OverflowError, new_enum_type, "foo", ('a', 'b'),
+ (-max_long-2, 5))
+ assert str(e.value) == (
+ "enum 'foo' declaration for 'a' does not fit a long or unsigned long")
+ # overflow: items larger than max_ulong
+ e = py.test.raises(OverflowError, new_enum_type, "foo", ('a', 'b'),
+ (5, max_ulong+1))
+ assert str(e.value) == (
+ "enum 'foo' declaration for 'b' does not fit a long or unsigned long")
def test_callback_returning_enum():
BInt = new_primitive_type("int")
BEnum = new_enum_type("foo", ('def', 'c', 'ab'), (0, 1, -20))
def cb(n):
- return '#%d' % n
+ if n & 1:
+ return cast(BEnum, n)
+ else:
+ return n
BFunc = new_function_type((BInt,), BEnum)
f = callback(BFunc, cb)
- assert f(0) == 'def'
- assert f(1) == 'c'
- assert f(-20) == 'ab'
- assert f(20) == '#20'
+ assert f(0) == 0
+ assert f(1) == 1
+ assert f(-20) == -20
+ assert f(20) == 20
+ assert f(21) == 21
+
+def test_callback_returning_enum_unsigned():
+ BInt = new_primitive_type("int")
+ BEnum = new_enum_type("foo", ('def', 'c', 'ab'), (0, 1, 20))
+ def cb(n):
+ print n
+ if n & 1:
+ return cast(BEnum, n)
+ else:
+ return n
+ BFunc = new_function_type((BInt,), BEnum)
+ f = callback(BFunc, cb)
+ assert f(0) == 0
+ assert f(1) == 1
+ assert f(-21) == 2**32 - 21
+ assert f(20) == 20
+ assert f(21) == 21
def test_callback_returning_char():
BInt = new_primitive_type("int")
@@ -2497,7 +2574,7 @@
if sys.platform == "win32":
py.test.skip("testing FILE not implemented")
#
- BFILE = new_struct_type("_IO_FILE")
+ BFILE = new_struct_type("$FILE")
BFILEP = new_pointer_type(BFILE)
BChar = new_primitive_type("char")
BCharP = new_pointer_type(BChar)
@@ -2566,3 +2643,87 @@
for i in range(20):
buf = buflist[i]
assert buf[:] == str2bytes("hi there %d\x00" % i)
+
+def test_slice():
+ BIntP = new_pointer_type(new_primitive_type("int"))
+ BIntArray = new_array_type(BIntP, None)
+ c = newp(BIntArray, 5)
+ assert len(c) == 5
+ assert repr(c) == "<cdata 'int[]' owning 20 bytes>"
+ d = c[1:4]
+ assert len(d) == 3
+ assert repr(d) == "<cdata 'int[]' sliced length 3>"
+ d[0] = 123
+ d[2] = 456
+ assert c[1] == 123
+ assert c[3] == 456
+ assert d[2] == 456
+ py.test.raises(IndexError, "d[3]")
+ py.test.raises(IndexError, "d[-1]")
+
+def test_slice_ptr():
+ BIntP = new_pointer_type(new_primitive_type("int"))
+ BIntArray = new_array_type(BIntP, None)
+ c = newp(BIntArray, 5)
+ d = (c+1)[0:2]
+ assert len(d) == 2
+ assert repr(d) == "<cdata 'int[]' sliced length 2>"
+ d[1] += 50
+ assert c[2] == 50
+
+def test_slice_array_checkbounds():
+ BIntP = new_pointer_type(new_primitive_type("int"))
+ BIntArray = new_array_type(BIntP, None)
+ c = newp(BIntArray, 5)
+ c[0:5]
+ assert len(c[5:5]) == 0
+ py.test.raises(IndexError, "c[-1:1]")
+ cp = c + 0
+ cp[-1:1]
+
+def test_nonstandard_slice():
+ BIntP = new_pointer_type(new_primitive_type("int"))
+ BIntArray = new_array_type(BIntP, None)
+ c = newp(BIntArray, 5)
+ e = py.test.raises(IndexError, "c[:5]")
+ assert str(e.value) == "slice start must be specified"
+ e = py.test.raises(IndexError, "c[4:]")
+ assert str(e.value) == "slice stop must be specified"
+ e = py.test.raises(IndexError, "c[1:2:3]")
+ assert str(e.value) == "slice with step not supported"
+ e = py.test.raises(IndexError, "c[1:2:1]")
+ assert str(e.value) == "slice with step not supported"
+ e = py.test.raises(IndexError, "c[4:2]")
+ assert str(e.value) == "slice start > stop"
+ e = py.test.raises(IndexError, "c[6:6]")
+ assert str(e.value) == "index too large (expected 6 <= 5)"
+
+def test_setslice():
+ BIntP = new_pointer_type(new_primitive_type("int"))
+ BIntArray = new_array_type(BIntP, None)
+ c = newp(BIntArray, 5)
+ c[1:3] = [100, 200]
+ assert list(c) == [0, 100, 200, 0, 0]
+ cp = c + 3
+ cp[-1:1] = [300, 400]
+ assert list(c) == [0, 100, 300, 400, 0]
+ cp[-1:1] = iter([500, 600])
+ assert list(c) == [0, 100, 500, 600, 0]
+ py.test.raises(ValueError, "cp[-1:1] = [1000]")
+ assert list(c) == [0, 100, 1000, 600, 0]
+ py.test.raises(ValueError, "cp[-1:1] = (700, 800, 900)")
+ assert list(c) == [0, 100, 700, 800, 0]
+
+def test_setslice_array():
+ BIntP = new_pointer_type(new_primitive_type("int"))
+ BIntArray = new_array_type(BIntP, None)
+ c = newp(BIntArray, 5)
+ d = newp(BIntArray, [10, 20, 30])
+ c[1:4] = d
+ assert list(c) == [0, 10, 20, 30, 0]
+ #
+ BShortP = new_pointer_type(new_primitive_type("short"))
+ BShortArray = new_array_type(BShortP, None)
+ d = newp(BShortArray, [40, 50])
+ c[1:3] = d
+ assert list(c) == [0, 40, 50, 30, 0]
_______________________________________________
pypy-commit mailing list
[email protected]
http://mail.python.org/mailman/listinfo/pypy-commit