Commit 1800cbda authored by Stefan Behnel's avatar Stefan Behnel

Initial attempt at implementing read-only memoryviews. (Github issue #1605)

parent 1487db2f
...@@ -870,16 +870,19 @@ class ExprNode(Node): ...@@ -870,16 +870,19 @@ class ExprNode(Node):
elif not src_type.is_error: elif not src_type.is_error:
error(self.pos, error(self.pos,
"Cannot convert '%s' to memoryviewslice" % (src_type,)) "Cannot convert '%s' to memoryviewslice" % (src_type,))
elif not src.type.conforms_to(dst_type, broadcast=self.is_memview_broadcast, else:
copying=self.is_memview_copy_assignment): if src.type.writable_needed:
if src.type.dtype.same_as(dst_type.dtype): dst_type.writable_needed = True
msg = "Memoryview '%s' not conformable to memoryview '%s'." if not src.type.conforms_to(dst_type, broadcast=self.is_memview_broadcast,
tup = src.type, dst_type copying=self.is_memview_copy_assignment):
else: if src.type.dtype.same_as(dst_type.dtype):
msg = "Different base types for memoryviews (%s, %s)" msg = "Memoryview '%s' not conformable to memoryview '%s'."
tup = src.type.dtype, dst_type.dtype tup = src.type, dst_type
else:
msg = "Different base types for memoryviews (%s, %s)"
tup = src.type.dtype, dst_type.dtype
error(self.pos, msg % tup) error(self.pos, msg % tup)
elif dst_type.is_pyobject: elif dst_type.is_pyobject:
if not src.type.is_pyobject: if not src.type.is_pyobject:
...@@ -4253,6 +4256,10 @@ class MemoryViewIndexNode(BufferIndexNode): ...@@ -4253,6 +4256,10 @@ class MemoryViewIndexNode(BufferIndexNode):
indices = self.indices indices = self.indices
have_slices, indices, newaxes = MemoryView.unellipsify(indices, self.base.type.ndim) have_slices, indices, newaxes = MemoryView.unellipsify(indices, self.base.type.ndim)
if not getting:
self.writable_needed = True
self.base.entry.type.writable_needed = True
self.memslice_index = (not newaxes and len(indices) == self.base.type.ndim) self.memslice_index = (not newaxes and len(indices) == self.base.type.ndim)
axes = [] axes = []
...@@ -12640,12 +12647,12 @@ class CoerceToMemViewSliceNode(CoercionNode): ...@@ -12640,12 +12647,12 @@ class CoerceToMemViewSliceNode(CoercionNode):
def generate_result_code(self, code): def generate_result_code(self, code):
self.type.create_from_py_utility_code(self.env) self.type.create_from_py_utility_code(self.env)
code.putln("%s = %s(%s);" % (self.result(), code.putln(self.type.from_py_call_code(
self.type.from_py_function, self.arg.py_result(),
self.arg.py_result())) self.result(),
self.pos,
error_cond = self.type.error_condition(self.result()) code
code.putln(code.error_goto_if(error_cond, self.pos)) ))
class CastNode(CoercionNode): class CastNode(CoercionNode):
......
...@@ -374,7 +374,7 @@ class FusedCFuncDefNode(StatListNode): ...@@ -374,7 +374,7 @@ class FusedCFuncDefNode(StatListNode):
coerce_from_py_func=memslice_type.from_py_function, coerce_from_py_func=memslice_type.from_py_function,
dtype=dtype) dtype=dtype)
decl_code.putln( decl_code.putln(
"{{memviewslice_cname}} {{coerce_from_py_func}}(object)") "{{memviewslice_cname}} {{coerce_from_py_func}}(object, int)")
pyx_code.context.update( pyx_code.context.update(
specialized_type_name=specialized_type.specialization_string, specialized_type_name=specialized_type.specialization_string,
...@@ -384,7 +384,7 @@ class FusedCFuncDefNode(StatListNode): ...@@ -384,7 +384,7 @@ class FusedCFuncDefNode(StatListNode):
u""" u"""
# try {{dtype}} # try {{dtype}}
if itemsize == -1 or itemsize == {{sizeof_dtype}}: if itemsize == -1 or itemsize == {{sizeof_dtype}}:
memslice = {{coerce_from_py_func}}(arg) memslice = {{coerce_from_py_func}}(arg, 0)
if memslice.memview: if memslice.memview:
__PYX_XDEC_MEMVIEW(&memslice, 1) __PYX_XDEC_MEMVIEW(&memslice, 1)
# print 'found a match for the buffer through format parsing' # print 'found a match for the buffer through format parsing'
......
...@@ -28,12 +28,12 @@ def concat_flags(*flags): ...@@ -28,12 +28,12 @@ def concat_flags(*flags):
format_flag = "PyBUF_FORMAT" format_flag = "PyBUF_FORMAT"
memview_c_contiguous = "(PyBUF_C_CONTIGUOUS | PyBUF_FORMAT | PyBUF_WRITABLE)" memview_c_contiguous = "(PyBUF_C_CONTIGUOUS | PyBUF_FORMAT)"
memview_f_contiguous = "(PyBUF_F_CONTIGUOUS | PyBUF_FORMAT | PyBUF_WRITABLE)" memview_f_contiguous = "(PyBUF_F_CONTIGUOUS | PyBUF_FORMAT)"
memview_any_contiguous = "(PyBUF_ANY_CONTIGUOUS | PyBUF_FORMAT | PyBUF_WRITABLE)" memview_any_contiguous = "(PyBUF_ANY_CONTIGUOUS | PyBUF_FORMAT)"
memview_full_access = "PyBUF_FULL" memview_full_access = "PyBUF_FULL_RO"
#memview_strided_access = "PyBUF_STRIDED" #memview_strided_access = "PyBUF_STRIDED_RO"
memview_strided_access = "PyBUF_RECORDS" memview_strided_access = "PyBUF_RECORDS_RO"
MEMVIEW_DIRECT = '__Pyx_MEMVIEW_DIRECT' MEMVIEW_DIRECT = '__Pyx_MEMVIEW_DIRECT'
MEMVIEW_PTR = '__Pyx_MEMVIEW_PTR' MEMVIEW_PTR = '__Pyx_MEMVIEW_PTR'
......
...@@ -3698,18 +3698,12 @@ class DefNodeWrapper(FuncDefNode): ...@@ -3698,18 +3698,12 @@ class DefNodeWrapper(FuncDefNode):
entry = arg.entry entry = arg.entry
code.putln("%s = %s;" % (entry.cname, item)) code.putln("%s = %s;" % (entry.cname, item))
else: else:
func = arg.type.from_py_function if arg.type.from_py_function:
if func:
if arg.default: if arg.default:
# C-typed default arguments must be handled here # C-typed default arguments must be handled here
code.putln('if (%s) {' % item) code.putln('if (%s) {' % item)
rhs = "%s(%s)" % (func, item) code.putln(arg.type.from_py_call_code(
if arg.type.is_enum: item, arg.entry.cname, arg.pos, code))
rhs = arg.type.cast_code(rhs)
code.putln("%s = %s; %s" % (
arg.entry.cname,
rhs,
code.error_goto_if(arg.type.error_condition(arg.entry.cname), arg.pos)))
if arg.default: if arg.default:
code.putln('} else {') code.putln('} else {')
code.putln("%s = %s;" % ( code.putln("%s = %s;" % (
...@@ -3950,17 +3944,14 @@ class DefNodeWrapper(FuncDefNode): ...@@ -3950,17 +3944,14 @@ class DefNodeWrapper(FuncDefNode):
def generate_arg_conversion_from_pyobject(self, arg, code): def generate_arg_conversion_from_pyobject(self, arg, code):
new_type = arg.type new_type = arg.type
func = new_type.from_py_function
# copied from CoerceFromPyTypeNode # copied from CoerceFromPyTypeNode
if func: if new_type.from_py_function:
lhs = arg.entry.cname code.putln(new_type.from_py_call_code(
rhs = "%s(%s)" % (func, arg.hdr_cname) arg.hdr_cname,
if new_type.is_enum: arg.entry.cname,
rhs = PyrexTypes.typecast(new_type, PyrexTypes.c_long_type, rhs) arg.pos,
code.putln("%s = %s; %s" % ( code,
lhs, ))
rhs,
code.error_goto_if(new_type.error_condition(arg.entry.cname), arg.pos)))
else: else:
error(arg.pos, "Cannot convert Python object argument to type '%s'" % new_type) error(arg.pos, "Cannot convert Python object argument to type '%s'" % new_type)
......
...@@ -2481,9 +2481,12 @@ def p_c_simple_base_type(s, self_flag, nonempty, templates = None): ...@@ -2481,9 +2481,12 @@ def p_c_simple_base_type(s, self_flag, nonempty, templates = None):
error(pos, "Expected an identifier, found '%s'" % s.sy) error(pos, "Expected an identifier, found '%s'" % s.sy)
if s.systring == 'const': if s.systring == 'const':
s.next() s.next()
base_type = p_c_base_type(s, base_type = p_c_base_type(s, self_flag=self_flag, nonempty=nonempty, templates=templates)
self_flag = self_flag, nonempty = nonempty, templates = templates) if isinstance(base_type, Nodes.MemoryViewSliceTypeNode):
return Nodes.CConstTypeNode(pos, base_type = base_type) # reverse order to avoid having to write "(const int)[:]"
base_type.base_type_node = Nodes.CConstTypeNode(pos, base_type=base_type.base_type_node)
return base_type
return Nodes.CConstTypeNode(pos, base_type=base_type)
if looking_at_base_type(s): if looking_at_base_type(s):
#print "p_c_simple_base_type: looking_at_base_type at", s.position() #print "p_c_simple_base_type: looking_at_base_type at", s.position()
is_basic = 1 is_basic = 1
......
...@@ -314,6 +314,21 @@ class PyrexType(BaseType): ...@@ -314,6 +314,21 @@ class PyrexType(BaseType):
def needs_nonecheck(self): def needs_nonecheck(self):
return 0 return 0
def _assign_from_py_code(self, source_code, result_code, error_pos, code,
from_py_function=None, error_condition=None, extra_args=None):
args = ', ' + ', '.join('%s' % arg for arg in extra_args) if extra_args else ''
convert_call = "%s(%s%s)" % (
from_py_function or self.from_py_function,
source_code,
args,
)
if self.is_enum:
convert_call = typecast(self, c_long_type, convert_call)
return '%s = %s; %s' % (
result_code,
convert_call,
code.error_goto_if(error_condition or self.error_condition(result_code), error_pos))
def public_decl(base_code, dll_linkage): def public_decl(base_code, dll_linkage):
if dll_linkage: if dll_linkage:
...@@ -491,12 +506,11 @@ class CTypedefType(BaseType): ...@@ -491,12 +506,11 @@ class CTypedefType(BaseType):
def from_py_call_code(self, source_code, result_code, error_pos, code, def from_py_call_code(self, source_code, result_code, error_pos, code,
from_py_function=None, error_condition=None): from_py_function=None, error_condition=None):
if from_py_function is None:
from_py_function = self.from_py_function
if error_condition is None:
error_condition = self.error_condition(result_code)
return self.typedef_base_type.from_py_call_code( return self.typedef_base_type.from_py_call_code(
source_code, result_code, error_pos, code, from_py_function, error_condition) source_code, result_code, error_pos, code,
from_py_function or self.from_py_function,
error_condition or self.error_condition(result_code)
)
def overflow_check_binop(self, binop, env, const_rhs=False): def overflow_check_binop(self, binop, env, const_rhs=False):
env.use_utility_code(UtilityCode.load("Common", "Overflow.c")) env.use_utility_code(UtilityCode.load("Common", "Overflow.c"))
...@@ -619,6 +633,7 @@ class MemoryViewSliceType(PyrexType): ...@@ -619,6 +633,7 @@ class MemoryViewSliceType(PyrexType):
def same_as_resolved_type(self, other_type): def same_as_resolved_type(self, other_type):
return ((other_type.is_memoryviewslice and return ((other_type.is_memoryviewslice and
self.writable_needed == other_type.writable_needed and
self.dtype.same_as(other_type.dtype) and self.dtype.same_as(other_type.dtype) and
self.axes == other_type.axes) or self.axes == other_type.axes) or
other_type is error_type) other_type is error_type)
...@@ -767,7 +782,14 @@ class MemoryViewSliceType(PyrexType): ...@@ -767,7 +782,14 @@ class MemoryViewSliceType(PyrexType):
src = self src = self
if src.dtype != dst.dtype: if self.writable_needed and not dst.writable_needed:
return False
src_dtype, dst_dtype = src.dtype, dst.dtype
if dst_dtype.is_const and not src_dtype.is_const:
dst_dtype = dst_dtype.const_base_type
if src_dtype != dst_dtype:
return False return False
if src.ndim != dst.ndim: if src.ndim != dst.ndim:
...@@ -885,11 +907,9 @@ class MemoryViewSliceType(PyrexType): ...@@ -885,11 +907,9 @@ class MemoryViewSliceType(PyrexType):
def from_py_call_code(self, source_code, result_code, error_pos, code, def from_py_call_code(self, source_code, result_code, error_pos, code,
from_py_function=None, error_condition=None): from_py_function=None, error_condition=None):
return '%s = %s(%s); %s' % ( return self._assign_from_py_code(
result_code, source_code, result_code, error_pos, code, from_py_function, error_condition,
from_py_function or self.from_py_function, extra_args=['PyBUF_WRITABLE' if self.writable_needed else '0'])
source_code,
code.error_goto_if(error_condition or self.error_condition(result_code), error_pos))
def create_to_py_utility_code(self, env): def create_to_py_utility_code(self, env):
self._dtype_to_py_func, self._dtype_from_py_func = self.dtype_object_conversion_funcs(env) self._dtype_to_py_func, self._dtype_from_py_func = self.dtype_object_conversion_funcs(env)
...@@ -1467,11 +1487,9 @@ class CType(PyrexType): ...@@ -1467,11 +1487,9 @@ class CType(PyrexType):
def from_py_call_code(self, source_code, result_code, error_pos, code, def from_py_call_code(self, source_code, result_code, error_pos, code,
from_py_function=None, error_condition=None): from_py_function=None, error_condition=None):
return '%s = %s(%s); %s' % ( return self._assign_from_py_code(
result_code, source_code, result_code, error_pos, code, from_py_function, error_condition)
from_py_function or self.from_py_function,
source_code,
code.error_goto_if(error_condition or self.error_condition(result_code), error_pos))
class PythranExpr(CType): class PythranExpr(CType):
# Pythran object of a given type # Pythran object of a given type
...@@ -2403,6 +2421,7 @@ class CArrayType(CPointerBaseType): ...@@ -2403,6 +2421,7 @@ class CArrayType(CPointerBaseType):
def from_py_call_code(self, source_code, result_code, error_pos, code, def from_py_call_code(self, source_code, result_code, error_pos, code,
from_py_function=None, error_condition=None): from_py_function=None, error_condition=None):
assert not error_condition
call_code = "%s(%s, %s, %s)" % ( call_code = "%s(%s, %s, %s)" % (
from_py_function or self.from_py_function, from_py_function or self.from_py_function,
source_code, result_code, self.size) source_code, result_code, self.size)
...@@ -3826,16 +3845,6 @@ class CEnumType(CType): ...@@ -3826,16 +3845,6 @@ class CEnumType(CType):
"FROM_PY_FUNCTION": self.from_py_function})) "FROM_PY_FUNCTION": self.from_py_function}))
return True return True
def from_py_call_code(self, source_code, result_code, error_pos, code,
from_py_function=None, error_condition=None):
rhs = "%s(%s)" % (
from_py_function or self.from_py_function,
source_code)
return '%s = %s;%s' % (
result_code,
typecast(self, c_long_type, rhs),
' %s' % code.error_goto_if(error_condition or self.error_condition(result_code), error_pos))
def create_type_wrapper(self, env): def create_type_wrapper(self, env):
from .UtilityCode import CythonUtilityCode from .UtilityCode import CythonUtilityCode
env.use_utility_code(CythonUtilityCode.load( env.use_utility_code(CythonUtilityCode.load(
......
...@@ -65,6 +65,7 @@ cdef extern from *: ...@@ -65,6 +65,7 @@ cdef extern from *:
PyBUF_STRIDES PyBUF_STRIDES
PyBUF_INDIRECT PyBUF_INDIRECT
PyBUF_RECORDS PyBUF_RECORDS
PyBUF_RECORDS_RO
ctypedef struct __Pyx_TypeInfo: ctypedef struct __Pyx_TypeInfo:
pass pass
...@@ -408,6 +409,9 @@ cdef class memoryview(object): ...@@ -408,6 +409,9 @@ cdef class memoryview(object):
return self.convert_item_to_object(itemp) return self.convert_item_to_object(itemp)
def __setitem__(memoryview self, object index, object value): def __setitem__(memoryview self, object index, object value):
if self.view.readonly:
raise TypeError("Cannot assign to read-only memoryview")
have_slices, index = _unellipsify(index, self.view.ndim) have_slices, index = _unellipsify(index, self.view.ndim)
if have_slices: if have_slices:
...@@ -507,6 +511,9 @@ cdef class memoryview(object): ...@@ -507,6 +511,9 @@ cdef class memoryview(object):
@cname('getbuffer') @cname('getbuffer')
def __getbuffer__(self, Py_buffer *info, int flags): def __getbuffer__(self, Py_buffer *info, int flags):
if flags & PyBUF_WRITABLE and self.view.readonly:
raise ValueError("Cannot create writable memory view from read-only memoryview")
if flags & PyBUF_STRIDES: if flags & PyBUF_STRIDES:
info.shape = self.view.shape info.shape = self.view.shape
else: else:
...@@ -531,7 +538,7 @@ cdef class memoryview(object): ...@@ -531,7 +538,7 @@ cdef class memoryview(object):
info.ndim = self.view.ndim info.ndim = self.view.ndim
info.itemsize = self.view.itemsize info.itemsize = self.view.itemsize
info.len = self.view.len info.len = self.view.len
info.readonly = 0 info.readonly = self.view.readonly
info.obj = self info.obj = self
__pyx_getbuffer = capsule(<void *> &__pyx_memoryview_getbuffer, "getbuffer(obj, view, flags)") __pyx_getbuffer = capsule(<void *> &__pyx_memoryview_getbuffer, "getbuffer(obj, view, flags)")
...@@ -1012,7 +1019,10 @@ cdef memoryview_fromslice({{memviewslice_name}} memviewslice, ...@@ -1012,7 +1019,10 @@ cdef memoryview_fromslice({{memviewslice_name}} memviewslice,
(<__pyx_buffer *> &result.view).obj = Py_None (<__pyx_buffer *> &result.view).obj = Py_None
Py_INCREF(Py_None) Py_INCREF(Py_None)
result.flags = PyBUF_RECORDS if (<memoryview>memviewslice.memview).flags & PyBUF_WRITABLE:
result.flags = PyBUF_RECORDS
else:
result.flags = PyBUF_RECORDS_RO
result.view.shape = <Py_ssize_t *> result.from_slice.shape result.view.shape = <Py_ssize_t *> result.from_slice.shape
result.view.strides = <Py_ssize_t *> result.from_slice.strides result.view.strides = <Py_ssize_t *> result.from_slice.strides
......
...@@ -82,7 +82,7 @@ typedef volatile __pyx_atomic_int_type __pyx_atomic_int; ...@@ -82,7 +82,7 @@ typedef volatile __pyx_atomic_int_type __pyx_atomic_int;
/////////////// ObjectToMemviewSlice.proto /////////////// /////////////// ObjectToMemviewSlice.proto ///////////////
static CYTHON_INLINE {{memviewslice_name}} {{funcname}}(PyObject *); static CYTHON_INLINE {{memviewslice_name}} {{funcname}}(PyObject *, int writable_flag);
////////// MemviewSliceInit.proto ////////// ////////// MemviewSliceInit.proto //////////
...@@ -127,7 +127,7 @@ static CYTHON_INLINE char *__pyx_memviewslice_index_full( ...@@ -127,7 +127,7 @@ static CYTHON_INLINE char *__pyx_memviewslice_index_full(
/////////////// ObjectToMemviewSlice /////////////// /////////////// ObjectToMemviewSlice ///////////////
//@requires: MemviewSliceValidateAndInit //@requires: MemviewSliceValidateAndInit
static CYTHON_INLINE {{memviewslice_name}} {{funcname}}(PyObject *obj) { static CYTHON_INLINE {{memviewslice_name}} {{funcname}}(PyObject *obj, int writable_flag) {
{{memviewslice_name}} result = {{memslice_init}}; {{memviewslice_name}} result = {{memslice_init}};
__Pyx_BufFmt_StackElem stack[{{struct_nesting_depth}}]; __Pyx_BufFmt_StackElem stack[{{struct_nesting_depth}}];
int axes_specs[] = { {{axes_specs}} }; int axes_specs[] = { {{axes_specs}} };
...@@ -140,7 +140,7 @@ static CYTHON_INLINE {{memviewslice_name}} {{funcname}}(PyObject *obj) { ...@@ -140,7 +140,7 @@ static CYTHON_INLINE {{memviewslice_name}} {{funcname}}(PyObject *obj) {
} }
retcode = __Pyx_ValidateAndInit_memviewslice(axes_specs, {{c_or_f_flag}}, retcode = __Pyx_ValidateAndInit_memviewslice(axes_specs, {{c_or_f_flag}},
{{buf_flag}}, {{ndim}}, {{buf_flag}} | writable_flag, {{ndim}},
&{{dtype_typeinfo}}, stack, &{{dtype_typeinfo}}, stack,
&result, obj); &result, obj);
......
...@@ -599,7 +599,7 @@ def readonly(obj): ...@@ -599,7 +599,7 @@ def readonly(obj):
acquired R acquired R
25 25
released R released R
>>> [str(x) for x in R.recieved_flags] # Works in both py2 and py3 >>> [str(x) for x in R.received_flags] # Works in both py2 and py3
['FORMAT', 'INDIRECT', 'ND', 'STRIDES'] ['FORMAT', 'INDIRECT', 'ND', 'STRIDES']
""" """
cdef object[unsigned short int, ndim=3] buf = obj cdef object[unsigned short int, ndim=3] buf = obj
...@@ -612,7 +612,7 @@ def writable(obj): ...@@ -612,7 +612,7 @@ def writable(obj):
>>> writable(R) >>> writable(R)
acquired R acquired R
released R released R
>>> [str(x) for x in R.recieved_flags] # Py2/3 >>> [str(x) for x in R.received_flags] # Py2/3
['FORMAT', 'INDIRECT', 'ND', 'STRIDES', 'WRITABLE'] ['FORMAT', 'INDIRECT', 'ND', 'STRIDES', 'WRITABLE']
""" """
cdef object[unsigned short int, ndim=3] buf = obj cdef object[unsigned short int, ndim=3] buf = obj
...@@ -626,7 +626,7 @@ def strided(object[int, ndim=1, mode='strided'] buf): ...@@ -626,7 +626,7 @@ def strided(object[int, ndim=1, mode='strided'] buf):
acquired A acquired A
released A released A
2 2
>>> [str(x) for x in A.recieved_flags] # Py2/3 >>> [str(x) for x in A.received_flags] # Py2/3
['FORMAT', 'ND', 'STRIDES'] ['FORMAT', 'ND', 'STRIDES']
Check that the suboffsets were patched back prior to release. Check that the suboffsets were patched back prior to release.
...@@ -641,7 +641,7 @@ def c_contig(object[int, ndim=1, mode='c'] buf): ...@@ -641,7 +641,7 @@ def c_contig(object[int, ndim=1, mode='c'] buf):
>>> A = IntMockBuffer(None, range(4)) >>> A = IntMockBuffer(None, range(4))
>>> c_contig(A) >>> c_contig(A)
2 2
>>> [str(x) for x in A.recieved_flags] >>> [str(x) for x in A.received_flags]
['FORMAT', 'ND', 'STRIDES', 'C_CONTIGUOUS'] ['FORMAT', 'ND', 'STRIDES', 'C_CONTIGUOUS']
""" """
return buf[2] return buf[2]
...@@ -654,7 +654,7 @@ def c_contig_2d(object[int, ndim=2, mode='c'] buf): ...@@ -654,7 +654,7 @@ def c_contig_2d(object[int, ndim=2, mode='c'] buf):
>>> A = IntMockBuffer(None, range(12), shape=(3,4)) >>> A = IntMockBuffer(None, range(12), shape=(3,4))
>>> c_contig_2d(A) >>> c_contig_2d(A)
7 7
>>> [str(x) for x in A.recieved_flags] >>> [str(x) for x in A.received_flags]
['FORMAT', 'ND', 'STRIDES', 'C_CONTIGUOUS'] ['FORMAT', 'ND', 'STRIDES', 'C_CONTIGUOUS']
""" """
return buf[1, 3] return buf[1, 3]
...@@ -665,7 +665,7 @@ def f_contig(object[int, ndim=1, mode='fortran'] buf): ...@@ -665,7 +665,7 @@ def f_contig(object[int, ndim=1, mode='fortran'] buf):
>>> A = IntMockBuffer(None, range(4)) >>> A = IntMockBuffer(None, range(4))
>>> f_contig(A) >>> f_contig(A)
2 2
>>> [str(x) for x in A.recieved_flags] >>> [str(x) for x in A.received_flags]
['FORMAT', 'ND', 'STRIDES', 'F_CONTIGUOUS'] ['FORMAT', 'ND', 'STRIDES', 'F_CONTIGUOUS']
""" """
return buf[2] return buf[2]
...@@ -678,7 +678,7 @@ def f_contig_2d(object[int, ndim=2, mode='fortran'] buf): ...@@ -678,7 +678,7 @@ def f_contig_2d(object[int, ndim=2, mode='fortran'] buf):
>>> A = IntMockBuffer(None, range(12), shape=(4,3), strides=(1, 4)) >>> A = IntMockBuffer(None, range(12), shape=(4,3), strides=(1, 4))
>>> f_contig_2d(A) >>> f_contig_2d(A)
7 7
>>> [str(x) for x in A.recieved_flags] >>> [str(x) for x in A.received_flags]
['FORMAT', 'ND', 'STRIDES', 'F_CONTIGUOUS'] ['FORMAT', 'ND', 'STRIDES', 'F_CONTIGUOUS']
""" """
return buf[3, 1] return buf[3, 1]
...@@ -1103,7 +1103,7 @@ def bufdefaults1(IntStridedMockBuffer[int, ndim=1] buf): ...@@ -1103,7 +1103,7 @@ def bufdefaults1(IntStridedMockBuffer[int, ndim=1] buf):
>>> bufdefaults1(A) >>> bufdefaults1(A)
acquired A acquired A
released A released A
>>> [str(x) for x in A.recieved_flags] >>> [str(x) for x in A.received_flags]
['FORMAT', 'ND', 'STRIDES'] ['FORMAT', 'ND', 'STRIDES']
""" """
pass pass
......
...@@ -18,16 +18,17 @@ cdef class MockBuffer: ...@@ -18,16 +18,17 @@ cdef class MockBuffer:
cdef object format, offset cdef object format, offset
cdef void* buffer cdef void* buffer
cdef Py_ssize_t len, itemsize cdef Py_ssize_t len, itemsize
cdef int ndim
cdef Py_ssize_t* strides cdef Py_ssize_t* strides
cdef Py_ssize_t* shape cdef Py_ssize_t* shape
cdef Py_ssize_t* suboffsets cdef Py_ssize_t* suboffsets
cdef object label, log cdef object label, log
cdef int ndim
cdef bint writable
cdef readonly object recieved_flags, release_ok cdef readonly object received_flags, release_ok
cdef public object fail cdef public object fail
def __init__(self, label, data, shape=None, strides=None, format=None, offset=0): def __init__(self, label, data, shape=None, strides=None, format=None, writable=True, offset=0):
# It is important not to store references to data after the constructor # It is important not to store references to data after the constructor
# as refcounting is checked on object buffers. # as refcounting is checked on object buffers.
self.label = label self.label = label
...@@ -35,6 +36,7 @@ cdef class MockBuffer: ...@@ -35,6 +36,7 @@ cdef class MockBuffer:
self.log = "" self.log = ""
self.offset = offset self.offset = offset
self.itemsize = self.get_itemsize() self.itemsize = self.get_itemsize()
self.writable = writable
if format is None: format = self.get_default_format() if format is None: format = self.get_default_format()
if shape is None: shape = (len(data),) if shape is None: shape = (len(data),)
if strides is None: if strides is None:
...@@ -127,16 +129,19 @@ cdef class MockBuffer: ...@@ -127,16 +129,19 @@ cdef class MockBuffer:
if self.fail: if self.fail:
raise ValueError("Failing on purpose") raise ValueError("Failing on purpose")
self.recieved_flags = [] self.received_flags = []
cdef int value cdef int value
for name, value in available_flags: for name, value in available_flags:
if (value & flags) == value: if (value & flags) == value:
self.recieved_flags.append(name) self.received_flags.append(name)
if flags & cpython.buffer.PyBUF_WRITABLE and not self.writable:
raise BufferError("Writable buffer requested from read-only mock: %s" % ' | '.join(self.received_flags))
buffer.buf = <void*>(<char*>self.buffer + (<int>self.offset * self.itemsize)) buffer.buf = <void*>(<char*>self.buffer + (<int>self.offset * self.itemsize))
buffer.obj = self buffer.obj = self
buffer.len = self.len buffer.len = self.len
buffer.readonly = 0 buffer.readonly = not self.writable
buffer.format = <char*>self.format buffer.format = <char*>self.format
buffer.ndim = self.ndim buffer.ndim = self.ndim
buffer.shape = self.shape buffer.shape = self.shape
......
...@@ -420,7 +420,7 @@ def writable(unsigned short int[:, :, :] mslice): ...@@ -420,7 +420,7 @@ def writable(unsigned short int[:, :, :] mslice):
>>> writable(R) >>> writable(R)
acquired R acquired R
released R released R
>>> [str(x) for x in R.recieved_flags] # Py2/3 >>> [str(x) for x in R.received_flags] # Py2/3
['FORMAT', 'ND', 'STRIDES', 'WRITABLE'] ['FORMAT', 'ND', 'STRIDES', 'WRITABLE']
""" """
buf = mslice buf = mslice
......
...@@ -279,8 +279,8 @@ def cascaded_buffer_assignment(obj): ...@@ -279,8 +279,8 @@ def cascaded_buffer_assignment(obj):
@testcase @testcase
def tuple_buffer_assignment1(a, b): def tuple_buffer_assignment1(a, b):
""" """
>>> A = IntMockBuffer("A", range(6)) >>> A = IntMockBuffer("A", range(6), writable=False)
>>> B = IntMockBuffer("B", range(6)) >>> B = IntMockBuffer("B", range(6), writable=False)
>>> tuple_buffer_assignment1(A, B) >>> tuple_buffer_assignment1(A, B)
acquired A acquired A
acquired B acquired B
...@@ -293,8 +293,8 @@ def tuple_buffer_assignment1(a, b): ...@@ -293,8 +293,8 @@ def tuple_buffer_assignment1(a, b):
@testcase @testcase
def tuple_buffer_assignment2(tup): def tuple_buffer_assignment2(tup):
""" """
>>> A = IntMockBuffer("A", range(6)) >>> A = IntMockBuffer("A", range(6), writable=False)
>>> B = IntMockBuffer("B", range(6)) >>> B = IntMockBuffer("B", range(6), writable=False)
>>> tuple_buffer_assignment2((A, B)) >>> tuple_buffer_assignment2((A, B))
acquired A acquired A
acquired B acquired B
...@@ -312,7 +312,7 @@ def explicitly_release_buffer(): ...@@ -312,7 +312,7 @@ def explicitly_release_buffer():
released A released A
After release After release
""" """
cdef int[:] x = IntMockBuffer("A", range(10)) cdef int[:] x = IntMockBuffer("A", range(10), writable=False)
del x del x
print "After release" print "After release"
...@@ -358,7 +358,7 @@ def get_int_2d(int[:, :] buf, int i, int j): ...@@ -358,7 +358,7 @@ def get_int_2d(int[:, :] buf, int i, int j):
def get_int_2d_uintindex(int[:, :] buf, unsigned int i, unsigned int j): def get_int_2d_uintindex(int[:, :] buf, unsigned int i, unsigned int j):
""" """
Unsigned indexing: Unsigned indexing:
>>> C = IntMockBuffer("C", range(6), (2,3)) >>> C = IntMockBuffer("C", range(6), (2,3), writable=False)
>>> get_int_2d_uintindex(C, 0, 0) >>> get_int_2d_uintindex(C, 0, 0)
acquired C acquired C
released C released C
...@@ -422,6 +422,10 @@ def set_int_2d(int[:, :] buf, int i, int j, int value): ...@@ -422,6 +422,10 @@ def set_int_2d(int[:, :] buf, int i, int j, int value):
... ...
IndexError: Out of bounds on buffer access (axis 1) IndexError: Out of bounds on buffer access (axis 1)
>>> C = IntMockBuffer("C", range(6), (2,3), writable=False)
>>> set_int_2d(C, -2, -3, 9)
Traceback (most recent call last):
BufferError: Writable buffer requested from read-only mock: FORMAT | ND | STRIDES | WRITABLE
""" """
buf[i, j] = value buf[i, j] = value
...@@ -588,11 +592,11 @@ def char_index_vars(int[:, :] buf, char i, char j, int value): ...@@ -588,11 +592,11 @@ def char_index_vars(int[:, :] buf, char i, char j, int value):
@testcase @testcase
def list_comprehension(int[:] buf, len): def list_comprehension(int[:] buf, len):
""" """
>>> list_comprehension(IntMockBuffer(None, [1,2,3]), 3) >>> list_comprehension(IntMockBuffer(None, [1,2,3], writable=False), 3)
1|2|3 1|2|3
""" """
cdef int i cdef int i
print u"|".join([unicode(buf[i]) for i in range(len)]) print "|".join([str(buf[i]) for i in range(len)])
@testcase @testcase
@cython.wraparound(False) @cython.wraparound(False)
...@@ -600,7 +604,7 @@ def wraparound_directive(int[:] buf, int pos_idx, int neg_idx): ...@@ -600,7 +604,7 @@ def wraparound_directive(int[:] buf, int pos_idx, int neg_idx):
""" """
Again, the most interesting thing here is to inspect the C source. Again, the most interesting thing here is to inspect the C source.
>>> A = IntMockBuffer(None, range(4)) >>> A = IntMockBuffer(None, range(4), writable=False)
>>> wraparound_directive(A, 2, -1) >>> wraparound_directive(A, 2, -1)
5 5
>>> wraparound_directive(A, -1, 2) >>> wraparound_directive(A, -1, 2)
...@@ -625,7 +629,7 @@ def writable(obj): ...@@ -625,7 +629,7 @@ def writable(obj):
>>> writable(R) >>> writable(R)
acquired R acquired R
released R released R
>>> [str(x) for x in R.recieved_flags] # Py2/3 >>> [str(x) for x in R.received_flags] # Py2/3
['FORMAT', 'ND', 'STRIDES', 'WRITABLE'] ['FORMAT', 'ND', 'STRIDES', 'WRITABLE']
""" """
cdef unsigned short int[:, :, :] buf = obj cdef unsigned short int[:, :, :] buf = obj
...@@ -634,13 +638,13 @@ def writable(obj): ...@@ -634,13 +638,13 @@ def writable(obj):
@testcase @testcase
def strided(int[:] buf): def strided(int[:] buf):
""" """
>>> A = IntMockBuffer("A", range(4)) >>> A = IntMockBuffer("A", range(4), writable=False)
>>> strided(A) >>> strided(A)
acquired A acquired A
released A released A
2 2
>>> [str(x) for x in A.recieved_flags] # Py2/3 >>> [str(x) for x in A.received_flags] # Py2/3
['FORMAT', 'ND', 'STRIDES', 'WRITABLE'] ['FORMAT', 'ND', 'STRIDES']
Check that the suboffsets were patched back prior to release. Check that the suboffsets were patched back prior to release.
>>> A.release_ok >>> A.release_ok
...@@ -651,11 +655,11 @@ def strided(int[:] buf): ...@@ -651,11 +655,11 @@ def strided(int[:] buf):
@testcase @testcase
def c_contig(int[::1] buf): def c_contig(int[::1] buf):
""" """
>>> A = IntMockBuffer(None, range(4)) >>> A = IntMockBuffer(None, range(4), writable=False)
>>> c_contig(A) >>> c_contig(A)
2 2
>>> [str(x) for x in A.recieved_flags] >>> [str(x) for x in A.received_flags]
['FORMAT', 'ND', 'STRIDES', 'C_CONTIGUOUS', 'WRITABLE'] ['FORMAT', 'ND', 'STRIDES', 'C_CONTIGUOUS']
""" """
return buf[2] return buf[2]
...@@ -664,22 +668,22 @@ def c_contig_2d(int[:, ::1] buf): ...@@ -664,22 +668,22 @@ def c_contig_2d(int[:, ::1] buf):
""" """
Multi-dim has seperate implementation Multi-dim has seperate implementation
>>> A = IntMockBuffer(None, range(12), shape=(3,4)) >>> A = IntMockBuffer(None, range(12), shape=(3,4), writable=False)
>>> c_contig_2d(A) >>> c_contig_2d(A)
7 7
>>> [str(x) for x in A.recieved_flags] >>> [str(x) for x in A.received_flags]
['FORMAT', 'ND', 'STRIDES', 'C_CONTIGUOUS', 'WRITABLE'] ['FORMAT', 'ND', 'STRIDES', 'C_CONTIGUOUS']
""" """
return buf[1, 3] return buf[1, 3]
@testcase @testcase
def f_contig(int[::1, :] buf): def f_contig(int[::1, :] buf):
""" """
>>> A = IntMockBuffer(None, range(4), shape=(2, 2), strides=(1, 2)) >>> A = IntMockBuffer(None, range(4), shape=(2, 2), strides=(1, 2), writable=False)
>>> f_contig(A) >>> f_contig(A)
2 2
>>> [str(x) for x in A.recieved_flags] >>> [str(x) for x in A.received_flags]
['FORMAT', 'ND', 'STRIDES', 'F_CONTIGUOUS', 'WRITABLE'] ['FORMAT', 'ND', 'STRIDES', 'F_CONTIGUOUS']
""" """
return buf[0, 1] return buf[0, 1]
...@@ -688,11 +692,11 @@ def f_contig_2d(int[::1, :] buf): ...@@ -688,11 +692,11 @@ def f_contig_2d(int[::1, :] buf):
""" """
Must set up strides manually to ensure Fortran ordering. Must set up strides manually to ensure Fortran ordering.
>>> A = IntMockBuffer(None, range(12), shape=(4,3), strides=(1, 4)) >>> A = IntMockBuffer(None, range(12), shape=(4,3), strides=(1, 4), writable=False)
>>> f_contig_2d(A) >>> f_contig_2d(A)
7 7
>>> [str(x) for x in A.recieved_flags] >>> [str(x) for x in A.received_flags]
['FORMAT', 'ND', 'STRIDES', 'F_CONTIGUOUS', 'WRITABLE'] ['FORMAT', 'ND', 'STRIDES', 'F_CONTIGUOUS']
""" """
return buf[3, 1] return buf[3, 1]
...@@ -711,9 +715,9 @@ def generic(int[::view.generic, ::view.generic] buf1, ...@@ -711,9 +715,9 @@ def generic(int[::view.generic, ::view.generic] buf1,
11 11
released A released A
released B released B
>>> [str(x) for x in A.recieved_flags] >>> [str(x) for x in A.received_flags]
['FORMAT', 'INDIRECT', 'ND', 'STRIDES', 'WRITABLE'] ['FORMAT', 'INDIRECT', 'ND', 'STRIDES', 'WRITABLE']
>>> [str(x) for x in B.recieved_flags] >>> [str(x) for x in B.received_flags]
['FORMAT', 'INDIRECT', 'ND', 'STRIDES', 'WRITABLE'] ['FORMAT', 'INDIRECT', 'ND', 'STRIDES', 'WRITABLE']
""" """
print buf1[1, 1] print buf1[1, 1]
...@@ -741,9 +745,9 @@ def generic(int[::view.generic, ::view.generic] buf1, ...@@ -741,9 +745,9 @@ def generic(int[::view.generic, ::view.generic] buf1,
# 11 # 11
# released A # released A
# released B # released B
# >>> [str(x) for x in A.recieved_flags] # >>> [str(x) for x in A.received_flags]
# ['FORMAT', 'INDIRECT', 'ND', 'STRIDES', 'WRITABLE'] # ['FORMAT', 'INDIRECT', 'ND', 'STRIDES', 'WRITABLE']
# >>> [str(x) for x in B.recieved_flags] # >>> [str(x) for x in B.received_flags]
# ['FORMAT', 'INDIRECT', 'ND', 'STRIDES', 'WRITABLE'] # ['FORMAT', 'INDIRECT', 'ND', 'STRIDES', 'WRITABLE']
# """ # """
# print buf1[1, 1] # print buf1[1, 1]
...@@ -771,9 +775,9 @@ def indirect_strided_and_contig( ...@@ -771,9 +775,9 @@ def indirect_strided_and_contig(
11 11
released A released A
released B released B
>>> [str(x) for x in A.recieved_flags] >>> [str(x) for x in A.received_flags]
['FORMAT', 'INDIRECT', 'ND', 'STRIDES', 'WRITABLE'] ['FORMAT', 'INDIRECT', 'ND', 'STRIDES', 'WRITABLE']
>>> [str(x) for x in B.recieved_flags] >>> [str(x) for x in B.received_flags]
['FORMAT', 'INDIRECT', 'ND', 'STRIDES', 'WRITABLE'] ['FORMAT', 'INDIRECT', 'ND', 'STRIDES', 'WRITABLE']
""" """
print buf1[1, 1] print buf1[1, 1]
...@@ -802,9 +806,9 @@ def indirect_contig( ...@@ -802,9 +806,9 @@ def indirect_contig(
11 11
released A released A
released B released B
>>> [str(x) for x in A.recieved_flags] >>> [str(x) for x in A.received_flags]
['FORMAT', 'INDIRECT', 'ND', 'STRIDES', 'WRITABLE'] ['FORMAT', 'INDIRECT', 'ND', 'STRIDES', 'WRITABLE']
>>> [str(x) for x in B.recieved_flags] >>> [str(x) for x in B.received_flags]
['FORMAT', 'INDIRECT', 'ND', 'STRIDES', 'WRITABLE'] ['FORMAT', 'INDIRECT', 'ND', 'STRIDES', 'WRITABLE']
""" """
print buf1[1, 1] print buf1[1, 1]
...@@ -827,7 +831,7 @@ def indirect_contig( ...@@ -827,7 +831,7 @@ def indirect_contig(
@testcase @testcase
def safe_get(int[:] buf, int idx): def safe_get(int[:] buf, int idx):
""" """
>>> A = IntMockBuffer(None, range(10), shape=(3,), offset=5) >>> A = IntMockBuffer(None, range(10), shape=(3,), offset=5, writable=False)
Validate our testing buffer... Validate our testing buffer...
>>> safe_get(A, 0) >>> safe_get(A, 0)
...@@ -857,7 +861,7 @@ def safe_get(int[:] buf, int idx): ...@@ -857,7 +861,7 @@ def safe_get(int[:] buf, int idx):
def unsafe_get(int[:] buf, int idx): def unsafe_get(int[:] buf, int idx):
""" """
Access outside of the area the buffer publishes. Access outside of the area the buffer publishes.
>>> A = IntMockBuffer(None, range(10), shape=(3,), offset=5) >>> A = IntMockBuffer(None, range(10), shape=(3,), offset=5, writable=False)
>>> unsafe_get(A, -4) >>> unsafe_get(A, -4)
4 4
>>> unsafe_get(A, -5) >>> unsafe_get(A, -5)
...@@ -870,7 +874,7 @@ def unsafe_get(int[:] buf, int idx): ...@@ -870,7 +874,7 @@ def unsafe_get(int[:] buf, int idx):
@testcase @testcase
def mixed_get(int[:] buf, int unsafe_idx, int safe_idx): def mixed_get(int[:] buf, int unsafe_idx, int safe_idx):
""" """
>>> A = IntMockBuffer(None, range(10), shape=(3,), offset=5) >>> A = IntMockBuffer(None, range(10), shape=(3,), offset=5, writable=False)
>>> mixed_get(A, -4, 0) >>> mixed_get(A, -4, 0)
(4, 5) (4, 5)
>>> mixed_get(A, 0, -4) >>> mixed_get(A, 0, -4)
...@@ -902,12 +906,12 @@ def printbuf_int_2d(o, shape): ...@@ -902,12 +906,12 @@ def printbuf_int_2d(o, shape):
""" """
Strided: Strided:
>>> printbuf_int_2d(IntMockBuffer("A", range(6), (2,3)), (2,3)) >>> printbuf_int_2d(IntMockBuffer("A", range(6), (2,3), writable=False), (2,3))
acquired A acquired A
0 1 2 END 0 1 2 END
3 4 5 END 3 4 5 END
released A released A
>>> printbuf_int_2d(IntMockBuffer("A", range(100), (3,3), strides=(20,5)), (3,3)) >>> printbuf_int_2d(IntMockBuffer("A", range(100), (3,3), strides=(20,5), writable=False), (3,3))
acquired A acquired A
0 5 10 END 0 5 10 END
20 25 30 END 20 25 30 END
...@@ -915,7 +919,7 @@ def printbuf_int_2d(o, shape): ...@@ -915,7 +919,7 @@ def printbuf_int_2d(o, shape):
released A released A
Indirect: Indirect:
>>> printbuf_int_2d(IntMockBuffer("A", [[1,2],[3,4]]), (2,2)) >>> printbuf_int_2d(IntMockBuffer("A", [[1,2],[3,4]], writable=False), (2,2))
acquired A acquired A
1 2 END 1 2 END
3 4 END 3 4 END
...@@ -933,7 +937,7 @@ def printbuf_int_2d(o, shape): ...@@ -933,7 +937,7 @@ def printbuf_int_2d(o, shape):
@testcase @testcase
def printbuf_float(o, shape): def printbuf_float(o, shape):
""" """
>>> printbuf_float(FloatMockBuffer("F", [1.0, 1.25, 0.75, 1.0]), (4,)) >>> printbuf_float(FloatMockBuffer("F", [1.0, 1.25, 0.75, 1.0], writable=False), (4,))
acquired F acquired F
1.0 1.25 0.75 1.0 END 1.0 1.25 0.75 1.0 END
released F released F
...@@ -982,9 +986,9 @@ ctypedef td_h_short td_h_cy_short ...@@ -982,9 +986,9 @@ ctypedef td_h_short td_h_cy_short
@testcase @testcase
def printbuf_td_cy_int(td_cy_int[:] buf, shape): def printbuf_td_cy_int(td_cy_int[:] buf, shape):
""" """
>>> printbuf_td_cy_int(IntMockBuffer(None, range(3)), (3,)) >>> printbuf_td_cy_int(IntMockBuffer(None, range(3), writable=False), (3,))
0 1 2 END 0 1 2 END
>>> printbuf_td_cy_int(ShortMockBuffer(None, range(3)), (3,)) >>> printbuf_td_cy_int(ShortMockBuffer(None, range(3), writable=False), (3,))
Traceback (most recent call last): Traceback (most recent call last):
... ...
ValueError: Buffer dtype mismatch, expected 'td_cy_int' but got 'short' ValueError: Buffer dtype mismatch, expected 'td_cy_int' but got 'short'
...@@ -997,9 +1001,9 @@ def printbuf_td_cy_int(td_cy_int[:] buf, shape): ...@@ -997,9 +1001,9 @@ def printbuf_td_cy_int(td_cy_int[:] buf, shape):
@testcase @testcase
def printbuf_td_h_short(td_h_short[:] buf, shape): def printbuf_td_h_short(td_h_short[:] buf, shape):
""" """
>>> printbuf_td_h_short(ShortMockBuffer(None, range(3)), (3,)) >>> printbuf_td_h_short(ShortMockBuffer(None, range(3), writable=False), (3,))
0 1 2 END 0 1 2 END
>>> printbuf_td_h_short(IntMockBuffer(None, range(3)), (3,)) >>> printbuf_td_h_short(IntMockBuffer(None, range(3), writable=False), (3,))
Traceback (most recent call last): Traceback (most recent call last):
... ...
ValueError: Buffer dtype mismatch, expected 'td_h_short' but got 'int' ValueError: Buffer dtype mismatch, expected 'td_h_short' but got 'int'
...@@ -1012,9 +1016,9 @@ def printbuf_td_h_short(td_h_short[:] buf, shape): ...@@ -1012,9 +1016,9 @@ def printbuf_td_h_short(td_h_short[:] buf, shape):
@testcase @testcase
def printbuf_td_h_cy_short(td_h_cy_short[:] buf, shape): def printbuf_td_h_cy_short(td_h_cy_short[:] buf, shape):
""" """
>>> printbuf_td_h_cy_short(ShortMockBuffer(None, range(3)), (3,)) >>> printbuf_td_h_cy_short(ShortMockBuffer(None, range(3), writable=False), (3,))
0 1 2 END 0 1 2 END
>>> printbuf_td_h_cy_short(IntMockBuffer(None, range(3)), (3,)) >>> printbuf_td_h_cy_short(IntMockBuffer(None, range(3), writable=False), (3,))
Traceback (most recent call last): Traceback (most recent call last):
... ...
ValueError: Buffer dtype mismatch, expected 'td_h_cy_short' but got 'int' ValueError: Buffer dtype mismatch, expected 'td_h_cy_short' but got 'int'
...@@ -1027,9 +1031,9 @@ def printbuf_td_h_cy_short(td_h_cy_short[:] buf, shape): ...@@ -1027,9 +1031,9 @@ def printbuf_td_h_cy_short(td_h_cy_short[:] buf, shape):
@testcase @testcase
def printbuf_td_h_ushort(td_h_ushort[:] buf, shape): def printbuf_td_h_ushort(td_h_ushort[:] buf, shape):
""" """
>>> printbuf_td_h_ushort(UnsignedShortMockBuffer(None, range(3)), (3,)) >>> printbuf_td_h_ushort(UnsignedShortMockBuffer(None, range(3), writable=False), (3,))
0 1 2 END 0 1 2 END
>>> printbuf_td_h_ushort(ShortMockBuffer(None, range(3)), (3,)) >>> printbuf_td_h_ushort(ShortMockBuffer(None, range(3), writable=False), (3,))
Traceback (most recent call last): Traceback (most recent call last):
... ...
ValueError: Buffer dtype mismatch, expected 'td_h_ushort' but got 'short' ValueError: Buffer dtype mismatch, expected 'td_h_ushort' but got 'short'
...@@ -1042,9 +1046,9 @@ def printbuf_td_h_ushort(td_h_ushort[:] buf, shape): ...@@ -1042,9 +1046,9 @@ def printbuf_td_h_ushort(td_h_ushort[:] buf, shape):
@testcase @testcase
def printbuf_td_h_double(td_h_double[:] buf, shape): def printbuf_td_h_double(td_h_double[:] buf, shape):
""" """
>>> printbuf_td_h_double(DoubleMockBuffer(None, [0.25, 1, 3.125]), (3,)) >>> printbuf_td_h_double(DoubleMockBuffer(None, [0.25, 1, 3.125], writable=False), (3,))
0.25 1.0 3.125 END 0.25 1.0 3.125 END
>>> printbuf_td_h_double(FloatMockBuffer(None, [0.25, 1, 3.125]), (3,)) >>> printbuf_td_h_double(FloatMockBuffer(None, [0.25, 1, 3.125], writable=False), (3,))
Traceback (most recent call last): Traceback (most recent call last):
... ...
ValueError: Buffer dtype mismatch, expected 'td_h_double' but got 'float' ValueError: Buffer dtype mismatch, expected 'td_h_double' but got 'float'
...@@ -1079,7 +1083,7 @@ def printbuf_object(object[:] buf, shape): ...@@ -1079,7 +1083,7 @@ def printbuf_object(object[:] buf, shape):
>>> a, b, c = "globally_unique_string_23234123", {4:23}, [34,3] >>> a, b, c = "globally_unique_string_23234123", {4:23}, [34,3]
>>> get_refcount(a), get_refcount(b), get_refcount(c) >>> get_refcount(a), get_refcount(b), get_refcount(c)
(2, 2, 2) (2, 2, 2)
>>> A = ObjectMockBuffer(None, [a, b, c]) >>> A = ObjectMockBuffer(None, [a, b, c], writable=False)
>>> printbuf_object(A, (3,)) >>> printbuf_object(A, (3,))
'globally_unique_string_23234123' 2 'globally_unique_string_23234123' 2
{4: 23} 2 {4: 23} 2
...@@ -1145,12 +1149,12 @@ def bufdefaults1(int[:] buf): ...@@ -1145,12 +1149,12 @@ def bufdefaults1(int[:] buf):
"strided" by defaults which should show "strided" by defaults which should show
up in the flags. up in the flags.
>>> A = IntStridedMockBuffer("A", range(10)) >>> A = IntStridedMockBuffer("A", range(10), writable=False)
>>> bufdefaults1(A) >>> bufdefaults1(A)
acquired A acquired A
released A released A
>>> [str(x) for x in A.recieved_flags] >>> [str(x) for x in A.received_flags]
['FORMAT', 'ND', 'STRIDES', 'WRITABLE'] ['FORMAT', 'ND', 'STRIDES']
""" """
pass pass
...@@ -1160,9 +1164,9 @@ def basic_struct(MyStruct[:] buf): ...@@ -1160,9 +1164,9 @@ def basic_struct(MyStruct[:] buf):
""" """
See also buffmt.pyx See also buffmt.pyx
>>> basic_struct(MyStructMockBuffer(None, [(1, 2, 3, 4, 5)])) >>> basic_struct(MyStructMockBuffer(None, [(1, 2, 3, 4, 5)], writable=False))
1 2 3 4 5 1 2 3 4 5
>>> basic_struct(MyStructMockBuffer(None, [(1, 2, 3, 4, 5)], format="ccqii")) >>> basic_struct(MyStructMockBuffer(None, [(1, 2, 3, 4, 5)], format="ccqii", writable=False))
1 2 3 4 5 1 2 3 4 5
""" """
print buf[0].a, buf[0].b, buf[0].c, buf[0].d, buf[0].e print buf[0].a, buf[0].b, buf[0].c, buf[0].d, buf[0].e
...@@ -1172,9 +1176,9 @@ def nested_struct(NestedStruct[:] buf): ...@@ -1172,9 +1176,9 @@ def nested_struct(NestedStruct[:] buf):
""" """
See also buffmt.pyx See also buffmt.pyx
>>> nested_struct(NestedStructMockBuffer(None, [(1, 2, 3, 4, 5)])) >>> nested_struct(NestedStructMockBuffer(None, [(1, 2, 3, 4, 5)], writable=False))
1 2 3 4 5 1 2 3 4 5
>>> nested_struct(NestedStructMockBuffer(None, [(1, 2, 3, 4, 5)], format="T{ii}T{2i}i")) >>> nested_struct(NestedStructMockBuffer(None, [(1, 2, 3, 4, 5)], format="T{ii}T{2i}i", writable=False))
1 2 3 4 5 1 2 3 4 5
""" """
print buf[0].x.a, buf[0].x.b, buf[0].y.a, buf[0].y.b, buf[0].z print buf[0].x.a, buf[0].x.b, buf[0].y.a, buf[0].y.b, buf[0].z
...@@ -1184,11 +1188,11 @@ def packed_struct(PackedStruct[:] buf): ...@@ -1184,11 +1188,11 @@ def packed_struct(PackedStruct[:] buf):
""" """
See also buffmt.pyx See also buffmt.pyx
>>> packed_struct(PackedStructMockBuffer(None, [(1, 2)])) >>> packed_struct(PackedStructMockBuffer(None, [(1, 2)], writable=False))
1 2 1 2
>>> packed_struct(PackedStructMockBuffer(None, [(1, 2)], format="T{c^i}")) >>> packed_struct(PackedStructMockBuffer(None, [(1, 2)], format="T{c^i}", writable=False))
1 2 1 2
>>> packed_struct(PackedStructMockBuffer(None, [(1, 2)], format="T{c=i}")) >>> packed_struct(PackedStructMockBuffer(None, [(1, 2)], format="T{c=i}", writable=False))
1 2 1 2
""" """
...@@ -1199,11 +1203,11 @@ def nested_packed_struct(NestedPackedStruct[:] buf): ...@@ -1199,11 +1203,11 @@ def nested_packed_struct(NestedPackedStruct[:] buf):
""" """
See also buffmt.pyx See also buffmt.pyx
>>> nested_packed_struct(NestedPackedStructMockBuffer(None, [(1, 2, 3, 4, 5)])) >>> nested_packed_struct(NestedPackedStructMockBuffer(None, [(1, 2, 3, 4, 5)], writable=False))
1 2 3 4 5 1 2 3 4 5
>>> nested_packed_struct(NestedPackedStructMockBuffer(None, [(1, 2, 3, 4, 5)], format="ci^ci@i")) >>> nested_packed_struct(NestedPackedStructMockBuffer(None, [(1, 2, 3, 4, 5)], format="ci^ci@i", writable=False))
1 2 3 4 5 1 2 3 4 5
>>> nested_packed_struct(NestedPackedStructMockBuffer(None, [(1, 2, 3, 4, 5)], format="^c@i^ci@i")) >>> nested_packed_struct(NestedPackedStructMockBuffer(None, [(1, 2, 3, 4, 5)], format="^c@i^ci@i", writable=False))
1 2 3 4 5 1 2 3 4 5
""" """
print buf[0].a, buf[0].b, buf[0].sub.a, buf[0].sub.b, buf[0].c print buf[0].a, buf[0].b, buf[0].sub.a, buf[0].sub.b, buf[0].c
...@@ -1212,7 +1216,7 @@ def nested_packed_struct(NestedPackedStruct[:] buf): ...@@ -1212,7 +1216,7 @@ def nested_packed_struct(NestedPackedStruct[:] buf):
@testcase @testcase
def complex_dtype(long double complex[:] buf): def complex_dtype(long double complex[:] buf):
""" """
>>> complex_dtype(LongComplexMockBuffer(None, [(0, -1)])) >>> complex_dtype(LongComplexMockBuffer(None, [(0, -1)], writable=False))
-1j -1j
""" """
print buf[0] print buf[0]
...@@ -1231,7 +1235,7 @@ def complex_struct_dtype(LongComplex[:] buf): ...@@ -1231,7 +1235,7 @@ def complex_struct_dtype(LongComplex[:] buf):
""" """
Note that the format string is "Zg" rather than "2g", yet a struct Note that the format string is "Zg" rather than "2g", yet a struct
is accessed. is accessed.
>>> complex_struct_dtype(LongComplexMockBuffer(None, [(0, -1)])) >>> complex_struct_dtype(LongComplexMockBuffer(None, [(0, -1)], writable=False))
0.0 -1.0 0.0 -1.0
""" """
print buf[0].real, buf[0].imag print buf[0].real, buf[0].imag
...@@ -1359,7 +1363,7 @@ def test_cdef_function2(): ...@@ -1359,7 +1363,7 @@ def test_cdef_function2():
def test_generic_slicing(arg, indirect=False): def test_generic_slicing(arg, indirect=False):
""" """
Test simple slicing Test simple slicing
>>> test_generic_slicing(IntMockBuffer("A", range(8 * 14 * 11), shape=(8, 14, 11))) >>> test_generic_slicing(IntMockBuffer("A", range(8 * 14 * 11), shape=(8, 14, 11), writable=False))
acquired A acquired A
3 9 2 3 9 2
308 -11 1 308 -11 1
...@@ -1367,7 +1371,7 @@ def test_generic_slicing(arg, indirect=False): ...@@ -1367,7 +1371,7 @@ def test_generic_slicing(arg, indirect=False):
released A released A
Test direct slicing, negative slice oob in dim 2 Test direct slicing, negative slice oob in dim 2
>>> test_generic_slicing(IntMockBuffer("A", range(1 * 2 * 3), shape=(1, 2, 3))) >>> test_generic_slicing(IntMockBuffer("A", range(1 * 2 * 3), shape=(1, 2, 3), writable=False))
acquired A acquired A
0 0 2 0 0 2
12 -3 1 12 -3 1
...@@ -1375,13 +1379,13 @@ def test_generic_slicing(arg, indirect=False): ...@@ -1375,13 +1379,13 @@ def test_generic_slicing(arg, indirect=False):
released A released A
Test indirect slicing Test indirect slicing
>>> test_generic_slicing(IntMockBuffer("A", shape_5_3_4_list, shape=(5, 3, 4)), indirect=True) >>> test_generic_slicing(IntMockBuffer("A", shape_5_3_4_list, shape=(5, 3, 4), writable=False), indirect=True)
acquired A acquired A
2 0 2 2 0 2
0 1 -1 0 1 -1
released A released A
>>> test_generic_slicing(IntMockBuffer("A", shape_9_14_21_list, shape=(9, 14, 21)), indirect=True) >>> test_generic_slicing(IntMockBuffer("A", shape_9_14_21_list, shape=(9, 14, 21), writable=False), indirect=True)
acquired A acquired A
3 9 2 3 9 2
10 1 -1 10 1 -1
...@@ -1413,7 +1417,7 @@ def test_generic_slicing(arg, indirect=False): ...@@ -1413,7 +1417,7 @@ def test_generic_slicing(arg, indirect=False):
def test_indirect_slicing(arg): def test_indirect_slicing(arg):
""" """
Test indirect slicing Test indirect slicing
>>> test_indirect_slicing(IntMockBuffer("A", shape_5_3_4_list, shape=(5, 3, 4))) >>> test_indirect_slicing(IntMockBuffer("A", shape_5_3_4_list, shape=(5, 3, 4), writable=False))
acquired A acquired A
5 3 2 5 3 2
0 0 -1 0 0 -1
...@@ -1428,7 +1432,7 @@ def test_indirect_slicing(arg): ...@@ -1428,7 +1432,7 @@ def test_indirect_slicing(arg):
58 58
released A released A
>>> test_indirect_slicing(IntMockBuffer("A", shape_9_14_21_list, shape=(9, 14, 21))) >>> test_indirect_slicing(IntMockBuffer("A", shape_9_14_21_list, shape=(9, 14, 21), writable=False))
acquired A acquired A
5 14 3 5 14 3
0 16 -1 0 16 -1
...@@ -1553,7 +1557,7 @@ def test_direct_slicing(arg): ...@@ -1553,7 +1557,7 @@ def test_direct_slicing(arg):
Fused types would be convenient to test this stuff! Fused types would be convenient to test this stuff!
Test simple slicing Test simple slicing
>>> test_direct_slicing(IntMockBuffer("A", range(8 * 14 * 11), shape=(8, 14, 11))) >>> test_direct_slicing(IntMockBuffer("A", range(8 * 14 * 11), shape=(8, 14, 11), writable=False))
acquired A acquired A
3 9 2 3 9 2
308 -11 1 308 -11 1
...@@ -1561,7 +1565,7 @@ def test_direct_slicing(arg): ...@@ -1561,7 +1565,7 @@ def test_direct_slicing(arg):
released A released A
Test direct slicing, negative slice oob in dim 2 Test direct slicing, negative slice oob in dim 2
>>> test_direct_slicing(IntMockBuffer("A", range(1 * 2 * 3), shape=(1, 2, 3))) >>> test_direct_slicing(IntMockBuffer("A", range(1 * 2 * 3), shape=(1, 2, 3), writable=False))
acquired A acquired A
0 0 2 0 0 2
12 -3 1 12 -3 1
...@@ -1586,7 +1590,7 @@ def test_direct_slicing(arg): ...@@ -1586,7 +1590,7 @@ def test_direct_slicing(arg):
@testcase @testcase
def test_slicing_and_indexing(arg): def test_slicing_and_indexing(arg):
""" """
>>> a = IntStridedMockBuffer("A", range(10 * 3 * 5), shape=(10, 3, 5)) >>> a = IntStridedMockBuffer("A", range(10 * 3 * 5), shape=(10, 3, 5), writable=False)
>>> test_slicing_and_indexing(a) >>> test_slicing_and_indexing(a)
acquired A acquired A
5 2 5 2
...@@ -1622,7 +1626,7 @@ def test_oob(): ...@@ -1622,7 +1626,7 @@ def test_oob():
... ...
IndexError: Index out of bounds (axis 1) IndexError: Index out of bounds (axis 1)
""" """
cdef int[:, :] a = IntMockBuffer("A", range(4 * 9), shape=(4, 9)) cdef int[:, :] a = IntMockBuffer("A", range(4 * 9), shape=(4, 9), writable=False)
print a[:, 20] print a[:, 20]
...@@ -1665,7 +1669,7 @@ def test_nogil_oob2(): ...@@ -1665,7 +1669,7 @@ def test_nogil_oob2():
... ...
IndexError: Index out of bounds (axis 0) IndexError: Index out of bounds (axis 0)
""" """
cdef int[:, :] a = IntMockBuffer("A", range(4 * 9), shape=(4, 9)) cdef int[:, :] a = IntMockBuffer("A", range(4 * 9), shape=(4, 9), writable=False)
with nogil: with nogil:
a[100, 9:] a[100, 9:]
...@@ -1711,7 +1715,7 @@ def test_convert_slicenode_to_indexnode(): ...@@ -1711,7 +1715,7 @@ def test_convert_slicenode_to_indexnode():
2 2
released A released A
""" """
cdef int[:] a = IntMockBuffer("A", range(10), shape=(10,)) cdef int[:] a = IntMockBuffer("A", range(10), shape=(10,), writable=False)
with nogil: with nogil:
a = a[2:4] a = a[2:4]
print a[0] print a[0]
...@@ -1721,10 +1725,10 @@ def test_convert_slicenode_to_indexnode(): ...@@ -1721,10 +1725,10 @@ def test_convert_slicenode_to_indexnode():
@cython.wraparound(False) @cython.wraparound(False)
def test_memslice_prange(arg): def test_memslice_prange(arg):
""" """
>>> test_memslice_prange(IntMockBuffer("A", range(400), shape=(20, 4, 5))) >>> test_memslice_prange(IntMockBuffer("A", range(400), shape=(20, 4, 5))) # FIXME: , writable=False))
acquired A acquired A
released A released A
>>> test_memslice_prange(IntMockBuffer("A", range(200), shape=(100, 2, 1))) >>> test_memslice_prange(IntMockBuffer("A", range(200), shape=(100, 2, 1))) # FIXME: , writable=False))
acquired A acquired A
released A released A
""" """
...@@ -2388,7 +2392,7 @@ def test_inplace_assignment(): ...@@ -2388,7 +2392,7 @@ def test_inplace_assignment():
@testcase @testcase
def test_newaxis(int[:] one_D): def test_newaxis(int[:] one_D):
""" """
>>> A = IntMockBuffer("A", range(6)) >>> A = IntMockBuffer("A", range(6), writable=False)
>>> test_newaxis(A) >>> test_newaxis(A)
acquired A acquired A
3 3
...@@ -2410,7 +2414,7 @@ def test_newaxis(int[:] one_D): ...@@ -2410,7 +2414,7 @@ def test_newaxis(int[:] one_D):
@testcase @testcase
def test_newaxis2(int[:, :] two_D): def test_newaxis2(int[:, :] two_D):
""" """
>>> A = IntMockBuffer("A", range(6), shape=(3, 2)) >>> A = IntMockBuffer("A", range(6), shape=(3, 2), writable=False)
>>> test_newaxis2(A) >>> test_newaxis2(A)
acquired A acquired A
shape: 3 1 1 shape: 3 1 1
...@@ -2444,3 +2448,16 @@ def test_newaxis2(int[:, :] two_D): ...@@ -2444,3 +2448,16 @@ def test_newaxis2(int[:, :] two_D):
_print_attributes(d) _print_attributes(d)
@testcase
def test_const_buffer(int[:] a):
"""
>>> A = IntMockBuffer("A", range(6), shape=(6,), writable=False)
>>> test_const_buffer(A)
acquired A
0
5
released A
"""
cdef const int[:] c = a
print(a[0])
print(c[-1])
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment