[pypy-commit] pypy refactor-buffer-api: use a readonly flag on buffers rather than separate classes
bdkearns
noreply at buildbot.pypy.org
Tue Apr 22 20:34:07 CEST 2014
Author: Brian Kearns <bdkearns at gmail.com>
Branch: refactor-buffer-api
Changeset: r70864:a7949cb30472
Date: 2014-03-25 19:20 -0400
http://bitbucket.org/pypy/pypy/changeset/a7949cb30472/
Log: use a readonly flag on buffers rather than separate classes
diff --git a/pypy/interpreter/buffer.py b/pypy/interpreter/buffer.py
--- a/pypy/interpreter/buffer.py
+++ b/pypy/interpreter/buffer.py
@@ -6,7 +6,8 @@
class Buffer(object):
"""Abstract base class for buffers."""
- __slots__ = []
+ __slots__ = ['readonly']
+ _immutable_ = True
def getlength(self):
raise NotImplementedError
@@ -24,20 +25,6 @@
# May be overridden. No bounds checks.
return ''.join([self.getitem(i) for i in range(start, stop, step)])
- def get_raw_address(self):
- raise ValueError("no raw buffer")
-
- def is_writable(self):
- return False
-
-
-class RWBuffer(Buffer):
- """Abstract base class for read-write buffers."""
- __slots__ = []
-
- def is_writable(self):
- return True
-
def setitem(self, index, char):
"Write a character into the buffer."
raise NotImplementedError # Must be overriden. No bounds checks.
@@ -47,12 +34,20 @@
for i in range(len(string)):
self.setitem(start + i, string[i])
+ def get_raw_address(self):
+ raise ValueError("no raw buffer")
+
+ def is_writable(self):
+ return not self.readonly
+
class StringBuffer(Buffer):
__slots__ = ['value']
+ _immutable_ = True
def __init__(self, value):
self.value = value
+ self.readonly = True
def getlength(self):
return len(self.value)
@@ -70,13 +65,14 @@
assert 0 <= start <= stop
return self.value[start:stop]
return "".join([self.value[start + i*step] for i in xrange(size)])
-# ____________________________________________________________
-class SubBufferMixin(object):
- _attrs_ = ['buffer', 'offset', 'size']
+class SubBuffer(Buffer):
+ __slots__ = ['buffer', 'offset', 'size']
+ _immutable_ = True
def __init__(self, buffer, offset, size):
+ self.readonly = buffer.readonly
self.buffer = buffer
self.offset = offset
self.size = size
@@ -100,14 +96,6 @@
return self.buffer.getslice(self.offset + start, self.offset + stop,
step, size)
-
-class SubBuffer(Buffer):
- import_from_mixin(SubBufferMixin)
-
-
-class RWSubBuffer(RWBuffer):
- import_from_mixin(SubBufferMixin)
-
def setitem(self, index, char):
self.buffer.setitem(self.offset + index, char)
diff --git a/pypy/module/__pypy__/bytebuffer.py b/pypy/module/__pypy__/bytebuffer.py
--- a/pypy/module/__pypy__/bytebuffer.py
+++ b/pypy/module/__pypy__/bytebuffer.py
@@ -2,13 +2,16 @@
# A convenient read-write buffer. Located here for want of a better place.
#
-from pypy.interpreter.buffer import RWBuffer
+from pypy.interpreter.buffer import Buffer
from pypy.interpreter.gateway import unwrap_spec
-class ByteBuffer(RWBuffer):
+class ByteBuffer(Buffer):
+ _immutable_ = True
+
def __init__(self, len):
self.data = ['\x00'] * len
+ self.readonly = False
def getlength(self):
return len(self.data)
diff --git a/pypy/module/_cffi_backend/cbuffer.py b/pypy/module/_cffi_backend/cbuffer.py
--- a/pypy/module/_cffi_backend/cbuffer.py
+++ b/pypy/module/_cffi_backend/cbuffer.py
@@ -1,4 +1,4 @@
-from pypy.interpreter.buffer import RWBuffer
+from pypy.interpreter.buffer import Buffer
from pypy.interpreter.error import oefmt
from pypy.interpreter.gateway import unwrap_spec, interp2app
from pypy.interpreter.typedef import TypeDef, make_weakref_descr
@@ -10,12 +10,13 @@
from rpython.rtyper.lltypesystem.rstr import copy_string_to_raw
-class LLBuffer(RWBuffer):
+class LLBuffer(Buffer):
_immutable_ = True
def __init__(self, raw_cdata, size):
self.raw_cdata = raw_cdata
self.size = size
+ self.readonly = False
def getlength(self):
return self.size
@@ -32,7 +33,7 @@
def getslice(self, start, stop, step, size):
if step == 1:
return rffi.charpsize2str(rffi.ptradd(self.raw_cdata, start), size)
- return RWBuffer.getslice(self, start, stop, step, size)
+ return Buffer.getslice(self, start, stop, step, size)
def setslice(self, start, string):
raw_cdata = rffi.ptradd(self.raw_cdata, start)
diff --git a/pypy/module/_io/interp_bufferedio.py b/pypy/module/_io/interp_bufferedio.py
--- a/pypy/module/_io/interp_bufferedio.py
+++ b/pypy/module/_io/interp_bufferedio.py
@@ -4,7 +4,7 @@
from pypy.interpreter.typedef import (
TypeDef, GetSetProperty, generic_new_descr, interp_attrproperty_w)
from pypy.interpreter.gateway import interp2app, unwrap_spec, WrappedDefault
-from pypy.interpreter.buffer import RWBuffer
+from pypy.interpreter.buffer import Buffer
from rpython.rlib.rstring import StringBuilder
from rpython.rlib.rarithmetic import r_longlong, intmask
from rpython.rlib import rposix
@@ -101,11 +101,14 @@
readinto = interp2app(W_BufferedIOBase.readinto_w),
)
-class RawBuffer(RWBuffer):
+class RawBuffer(Buffer):
+ _immutable_ = True
+
def __init__(self, buf, start, length):
self.buf = buf
self.start = start
self.length = length
+ self.readonly = False
def getlength(self):
return self.length
diff --git a/pypy/module/_rawffi/buffer.py b/pypy/module/_rawffi/buffer.py
--- a/pypy/module/_rawffi/buffer.py
+++ b/pypy/module/_rawffi/buffer.py
@@ -1,12 +1,14 @@
-from pypy.interpreter.buffer import RWBuffer
+from pypy.interpreter.buffer import Buffer
# XXX not the most efficient implementation
-class RawFFIBuffer(RWBuffer):
+class RawFFIBuffer(Buffer):
+ _immutable_ = True
def __init__(self, datainstance):
self.datainstance = datainstance
+ self.readonly = False
def getlength(self):
return self.datainstance.getrawsize()
diff --git a/pypy/module/array/interp_array.py b/pypy/module/array/interp_array.py
--- a/pypy/module/array/interp_array.py
+++ b/pypy/module/array/interp_array.py
@@ -9,7 +9,7 @@
from rpython.rtyper.lltypesystem.rstr import copy_string_to_raw
from pypy.interpreter.baseobjspace import W_Root
-from pypy.interpreter.buffer import RWBuffer
+from pypy.interpreter.buffer import Buffer
from pypy.interpreter.error import OperationError, oefmt
from pypy.interpreter.gateway import (
interp2app, interpindirect2app, unwrap_spec)
@@ -133,10 +133,10 @@
self.allocated = 0
def readbuf_w(self, space):
- return ArrayBuffer(self)
+ return ArrayBuffer(self, True)
def writebuf_w(self, space):
- return ArrayBuffer(self)
+ return ArrayBuffer(self, False)
def descr_append(self, space, w_x):
""" append(x)
@@ -586,9 +586,12 @@
v.typecode = k
unroll_typecodes = unrolling_iterable(types.keys())
-class ArrayBuffer(RWBuffer):
- def __init__(self, array):
+class ArrayBuffer(Buffer):
+ _immutable_ = True
+
+ def __init__(self, array, readonly):
self.array = array
+ self.readonly = readonly
def getlength(self):
return self.array.len * self.array.itemsize
diff --git a/pypy/module/array/test/test_array.py b/pypy/module/array/test/test_array.py
--- a/pypy/module/array/test/test_array.py
+++ b/pypy/module/array/test/test_array.py
@@ -421,12 +421,8 @@
def test_buffer_write(self):
a = self.array('c', 'hello')
buf = buffer(a)
- print repr(buf)
- try:
- buf[3] = 'L'
- except TypeError:
- skip("buffer(array) returns a read-only buffer on CPython")
- assert a.tostring() == 'helLo'
+ exc = raises(TypeError, "buf[3] = 'L'")
+ assert str(exc.value) == "buffer is read-only"
def test_buffer_keepalive(self):
buf = buffer(self.array('c', 'text'))
diff --git a/pypy/module/cpyext/slotdefs.py b/pypy/module/cpyext/slotdefs.py
--- a/pypy/module/cpyext/slotdefs.py
+++ b/pypy/module/cpyext/slotdefs.py
@@ -230,11 +230,13 @@
class CPyBuffer(Buffer):
# Similar to Py_buffer
+ _immutable_ = True
def __init__(self, ptr, size, w_obj):
self.ptr = ptr
self.size = size
self.w_obj = w_obj # kept alive
+ self.readonly = True
def getlength(self):
return self.size
diff --git a/pypy/module/cpyext/test/test_arraymodule.py b/pypy/module/cpyext/test/test_arraymodule.py
--- a/pypy/module/cpyext/test/test_arraymodule.py
+++ b/pypy/module/cpyext/test/test_arraymodule.py
@@ -53,8 +53,11 @@
def test_buffer(self):
module = self.import_module(name='array')
arr = module.array('i', [1,2,3,4])
+ buf = buffer(arr)
+ exc = raises(TypeError, "buf[1] = '1'")
+ assert str(exc.value) == "buffer is read-only"
# XXX big-endian
- assert str(buffer(arr)) == ('\x01\0\0\0'
- '\x02\0\0\0'
- '\x03\0\0\0'
- '\x04\0\0\0')
+ assert str(buf) == ('\x01\0\0\0'
+ '\x02\0\0\0'
+ '\x03\0\0\0'
+ '\x04\0\0\0')
diff --git a/pypy/module/micronumpy/concrete.py b/pypy/module/micronumpy/concrete.py
--- a/pypy/module/micronumpy/concrete.py
+++ b/pypy/module/micronumpy/concrete.py
@@ -1,4 +1,4 @@
-from pypy.interpreter.buffer import RWBuffer
+from pypy.interpreter.buffer import Buffer
from pypy.interpreter.error import OperationError, oefmt
from rpython.rlib import jit
from rpython.rlib.debug import make_sure_not_resized
@@ -314,8 +314,8 @@
def get_storage(self):
return self.storage
- def get_buffer(self, space):
- return ArrayBuffer(self)
+ def get_buffer(self, space, readonly):
+ return ArrayBuffer(self, readonly)
def astype(self, space, dtype):
strides, backstrides = calc_strides(self.get_shape(), dtype,
@@ -469,9 +469,12 @@
free_raw_storage(self.storage)
-class ArrayBuffer(RWBuffer):
- def __init__(self, impl):
+class ArrayBuffer(Buffer):
+ _immutable_ = True
+
+ def __init__(self, impl, readonly):
self.impl = impl
+ self.readonly = readonly
def getitem(self, item):
return raw_storage_getitem(lltype.Char, self.impl.storage, item)
diff --git a/pypy/module/micronumpy/ndarray.py b/pypy/module/micronumpy/ndarray.py
--- a/pypy/module/micronumpy/ndarray.py
+++ b/pypy/module/micronumpy/ndarray.py
@@ -603,19 +603,19 @@
"ctypes not implemented yet"))
def buffer_w(self, space, flags):
- return self.implementation.get_buffer(space)
+ return self.implementation.get_buffer(space, True)
def readbuf_w(self, space):
- return self.implementation.get_buffer(space)
+ return self.implementation.get_buffer(space, True)
def writebuf_w(self, space):
- return self.implementation.get_buffer(space)
+ return self.implementation.get_buffer(space, False)
def charbuf_w(self, space):
- return self.implementation.get_buffer(space).as_str()
+ return self.implementation.get_buffer(space, True).as_str()
def descr_get_data(self, space):
- return space.newbuffer(self.implementation.get_buffer(space))
+ return space.newbuffer(self.implementation.get_buffer(space, False))
@unwrap_spec(offset=int, axis1=int, axis2=int)
def descr_diagonal(self, space, offset=0, axis1=0, axis2=1):
diff --git a/pypy/module/micronumpy/test/test_ndarray.py b/pypy/module/micronumpy/test/test_ndarray.py
--- a/pypy/module/micronumpy/test/test_ndarray.py
+++ b/pypy/module/micronumpy/test/test_ndarray.py
@@ -356,6 +356,9 @@
a = np.array([1,2,3])
b = buffer(a)
assert type(b) is buffer
+ assert 'read-only buffer' in repr(b)
+ exc = raises(TypeError, "b[0] = '0'")
+ assert str(exc.value) == 'buffer is read-only'
def test_type(self):
from numpypy import array
@@ -2243,6 +2246,7 @@
a.data[4] = '\xff'
assert a[1] == 0xff
assert len(a.data) == 16
+ assert type(a.data) is buffer
def test_explicit_dtype_conversion(self):
from numpypy import array
diff --git a/pypy/module/mmap/interp_mmap.py b/pypy/module/mmap/interp_mmap.py
--- a/pypy/module/mmap/interp_mmap.py
+++ b/pypy/module/mmap/interp_mmap.py
@@ -2,7 +2,7 @@
from pypy.interpreter.baseobjspace import W_Root
from pypy.interpreter.typedef import TypeDef
from pypy.interpreter.gateway import interp2app, unwrap_spec
-from pypy.interpreter.buffer import RWBuffer
+from pypy.interpreter.buffer import Buffer
from rpython.rlib import rmmap, rarithmetic
from rpython.rlib.rmmap import RValueError, RTypeError, RMMapError
@@ -19,7 +19,7 @@
def readbuf_w(self, space):
self.check_valid()
- return MMapBuffer(self.space, self.mmap)
+ return MMapBuffer(self.space, self.mmap, True)
def close(self):
self.mmap.close()
@@ -286,10 +286,13 @@
mmap_error._dont_inline_ = True
-class MMapBuffer(RWBuffer):
- def __init__(self, space, mmap):
+class MMapBuffer(Buffer):
+ _immutable_ = True
+
+ def __init__(self, space, mmap, readonly):
self.space = space
self.mmap = mmap
+ self.readonly = readonly
def getlength(self):
return self.mmap.size
@@ -303,7 +306,7 @@
if step == 1:
return self.mmap.getslice(start, size)
else:
- return RWBuffer.getslice(self, start, stop, step, size)
+ return Buffer.getslice(self, start, stop, step, size)
def setitem(self, index, char):
self.check_valid_writeable()
@@ -313,14 +316,6 @@
self.check_valid_writeable()
self.mmap.setslice(start, string)
- def is_writable(self):
- try:
- self.mmap.check_writeable()
- except RMMapError:
- return False
- else:
- return True
-
def get_raw_address(self):
self.check_valid()
return self.mmap.data
diff --git a/pypy/module/mmap/test/test_mmap.py b/pypy/module/mmap/test/test_mmap.py
--- a/pypy/module/mmap/test/test_mmap.py
+++ b/pypy/module/mmap/test/test_mmap.py
@@ -560,14 +560,24 @@
m = mmap(f.fileno(), 6)
m[5] = '?'
b = buffer(m)
- try:
- b[:3] = "FOO"
- except TypeError: # on CPython: "buffer is read-only" :-/
- skip("on CPython: buffer is read-only")
+ exc = raises(TypeError, 'b[:3] = "FOO"')
+ assert str(exc.value) == "buffer is read-only"
m.close()
f.seek(0)
got = f.read()
- assert got == "FOOba?"
+ assert got == "fooba?"
+ f.close()
+
+ def test_memoryview(self):
+ from mmap import mmap
+ f = open(self.tmpname + "y", "w+")
+ f.write("foobar")
+ f.flush()
+ m = mmap(f.fileno(), 6)
+ m[5] = '?'
+ exc = raises(TypeError, memoryview, m)
+ assert 'buffer interface' in str(exc.value)
+ m.close()
f.close()
def test_offset(self):
diff --git a/pypy/objspace/std/bufferobject.py b/pypy/objspace/std/bufferobject.py
--- a/pypy/objspace/std/bufferobject.py
+++ b/pypy/objspace/std/bufferobject.py
@@ -59,10 +59,7 @@
if size < -1:
raise OperationError(space.w_ValueError,
space.wrap("size must be zero or positive"))
- if isinstance(buf, buffer.RWBuffer):
- buf = buffer.RWSubBuffer(buf, offset, size)
- else:
- buf = buffer.SubBuffer(buf, offset, size)
+ buf = buffer.SubBuffer(buf, offset, size)
return W_Buffer(buf)
def descr_len(self, space):
@@ -77,7 +74,7 @@
@unwrap_spec(newstring='bufferstr')
def descr_setitem(self, space, w_index, newstring):
- if not isinstance(self.buf, buffer.RWBuffer):
+ if not self.buf.is_writable():
raise OperationError(space.w_TypeError,
space.wrap("buffer is read-only"))
_buffer_setitem(space, self.buf, w_index, newstring)
@@ -118,7 +115,7 @@
return space.call_method(w_string, '__mul__', w_times)
def descr_repr(self, space):
- if isinstance(self.buf, buffer.RWBuffer):
+ if self.buf.is_writable():
info = 'read-write buffer'
else:
info = 'read-only buffer'
diff --git a/pypy/objspace/std/bytearrayobject.py b/pypy/objspace/std/bytearrayobject.py
--- a/pypy/objspace/std/bytearrayobject.py
+++ b/pypy/objspace/std/bytearrayobject.py
@@ -5,7 +5,7 @@
from rpython.rlib.rstring import StringBuilder
from pypy.interpreter.baseobjspace import W_Root
-from pypy.interpreter.buffer import RWBuffer
+from pypy.interpreter.buffer import Buffer
from pypy.interpreter.error import OperationError, oefmt
from pypy.interpreter.gateway import WrappedDefault, interp2app, unwrap_spec
from pypy.interpreter.signature import Signature
@@ -1150,9 +1150,12 @@
start += step
-class BytearrayBuffer(RWBuffer):
+class BytearrayBuffer(Buffer):
+ _immutable_ = True
+
def __init__(self, data):
self.data = data
+ self.readonly = False
def getlength(self):
return len(self.data)
diff --git a/pypy/objspace/std/memoryobject.py b/pypy/objspace/std/memoryobject.py
--- a/pypy/objspace/std/memoryobject.py
+++ b/pypy/objspace/std/memoryobject.py
@@ -85,11 +85,7 @@
size = stop - start
if size < 0:
size = 0
- buf = self.buf
- if isinstance(buf, buffer.RWBuffer):
- buf = buffer.RWSubBuffer(buf, start, size)
- else:
- buf = buffer.SubBuffer(buf, start, size)
+ buf = buffer.SubBuffer(self.buf, start, size)
return W_MemoryView(buf)
def descr_tobytes(self, space):
@@ -116,7 +112,7 @@
@unwrap_spec(newstring='bufferstr')
def descr_setitem(self, space, w_index, newstring):
- if not isinstance(self.buf, buffer.RWBuffer):
+ if not self.buf.is_writable():
raise OperationError(space.w_TypeError,
space.wrap("cannot modify read-only memory"))
_buffer_setitem(space, self.buf, w_index, newstring)
@@ -134,7 +130,7 @@
return space.wrap(1)
def w_is_readonly(self, space):
- return space.wrap(not isinstance(self.buf, buffer.RWBuffer))
+ return space.wrap(not self.buf.is_writable())
def w_get_shape(self, space):
return space.newtuple([space.wrap(self.getlength())])
diff --git a/pypy/objspace/std/test/test_bufferobject.py b/pypy/objspace/std/test/test_bufferobject.py
--- a/pypy/objspace/std/test/test_bufferobject.py
+++ b/pypy/objspace/std/test/test_bufferobject.py
@@ -116,23 +116,27 @@
assert b[0] == 'w'
assert b[:] == 'world'
raises(IndexError, 'b[5]')
- b[0] = 'W'
- assert str(b) == 'World'
- assert a.tostring() == 'hello World'
- b[:] = '12345'
- assert a.tostring() == 'hello 12345'
- raises(IndexError, 'b[5] = "."')
- b[4:2] = ''
- assert a.tostring() == 'hello 12345'
+ exc = raises(TypeError, "b[0] = 'W'")
+ assert str(exc.value) == "buffer is read-only"
+ exc = raises(TypeError, "b[:] = '12345'")
+ assert str(exc.value) == "buffer is read-only"
+ exc = raises(TypeError, 'b[5] = "."')
+ assert str(exc.value) == "buffer is read-only"
+ exc = raises(TypeError, "b[4:2] = ''")
+ assert str(exc.value) == "buffer is read-only"
+ assert str(b) == 'world'
+ assert a.tostring() == 'hello world'
b = buffer(b, 2)
assert len(b) == 3
- assert b[0] == '3'
- assert b[:] == '345'
+ assert b[0] == 'r'
+ assert b[:] == 'rld'
raises(IndexError, 'b[3]')
- b[1] = 'X'
- assert a.tostring() == 'hello 123X5'
- raises(IndexError, 'b[3] = "."')
+ exc = raises(TypeError, "b[1] = 'X'")
+ assert str(exc.value) == "buffer is read-only"
+ exc = raises(TypeError, 'b[3] = "."')
+ assert str(exc.value) == "buffer is read-only"
+ assert a.tostring() == 'hello world'
a = array.array("c", 'hello world')
b = buffer(a, 1, 8)
@@ -140,28 +144,33 @@
assert b[0] == 'e'
assert b[:] == 'ello wor'
raises(IndexError, 'b[8]')
- b[0] = 'E'
- assert str(b) == 'Ello wor'
- assert a.tostring() == 'hEllo world'
- b[:] = '12345678'
- assert a.tostring() == 'h12345678ld'
- raises(IndexError, 'b[8] = "."')
+ exc = raises(TypeError, "b[0] = 'E'")
+ assert str(exc.value) == "buffer is read-only"
+ assert str(b) == 'ello wor'
+ assert a.tostring() == 'hello world'
+ exc = raises(TypeError, "b[:] = '12345678'")
+ assert str(exc.value) == "buffer is read-only"
+ assert a.tostring() == 'hello world'
+ exc = raises(TypeError, 'b[8] = "."')
+ assert str(exc.value) == "buffer is read-only"
b = buffer(b, 2, 3)
assert len(b) == 3
- assert b[2] == '5'
- assert b[:] == '345'
+ assert b[2] == ' '
+ assert b[:] == 'lo '
raises(IndexError, 'b[3]')
- b[1] = 'X'
- assert a.tostring() == 'h123X5678ld'
- raises(IndexError, 'b[3] = "."')
+ exc = raises(TypeError, "b[1] = 'X'")
+ assert str(exc.value) == "buffer is read-only"
+ assert a.tostring() == 'hello world'
+ exc = raises(TypeError, 'b[3] = "."')
+ assert str(exc.value) == "buffer is read-only"
b = buffer(a, 55)
assert len(b) == 0
assert b[:] == ''
b = buffer(a, 6, 999)
assert len(b) == 5
- assert b[:] == '678ld'
+ assert b[:] == 'world'
raises(ValueError, buffer, a, -1)
raises(ValueError, buffer, a, 0, -2)
More information about the pypy-commit
mailing list