Commit 9ca6532b authored by scoder's avatar scoder Committed by GitHub

Merge pull request #1869 from scoder/readonly_buffers

implement read-only memoryviews
parents 83ffbe08 e4838d84
...@@ -8,10 +8,6 @@ Cython Changelog ...@@ -8,10 +8,6 @@ Cython Changelog
Features added Features added
-------------- --------------
* When compiling with gcc, the module init function is now tuned for small
code size instead of whatever compile flags were provided externally.
(Github issue #2102)
* Cdef classes can now multiply inherit from ordinary Python classes. * Cdef classes can now multiply inherit from ordinary Python classes.
(The primary base must still be a c class, possibly ``object``, and (The primary base must still be a c class, possibly ``object``, and
the other bases must *not* be cdef classes.) the other bases must *not* be cdef classes.)
...@@ -19,6 +15,13 @@ Features added ...@@ -19,6 +15,13 @@ Features added
* Type inference is now supported for Pythran compiled NumPy expressions. * Type inference is now supported for Pythran compiled NumPy expressions.
Patch by Nils Braun. (Github issue #1954) Patch by Nils Braun. (Github issue #1954)
* The ``const`` modifier can be applied to memoryview declarations to allow
read-only buffers as input. (Github issues #1605, #1869)
* When compiling with gcc, the module init function is now tuned for small
code size instead of whatever compile flags were provided externally.
(Github issue #2102)
* C file includes are moved behind the module declarations if possible, to allow * C file includes are moved behind the module declarations if possible, to allow
them to depend on module declarations themselves. them to depend on module declarations themselves.
Patch by Jeroen Demeyer. (Github issue #1896) Patch by Jeroen Demeyer. (Github issue #1896)
......
...@@ -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:
...@@ -4298,6 +4301,11 @@ class MemoryViewIndexNode(BufferIndexNode): ...@@ -4298,6 +4301,11 @@ 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
if self.base.is_name or self.base.is_attribute:
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 = []
...@@ -12772,12 +12780,12 @@ class CoerceToMemViewSliceNode(CoercionNode): ...@@ -12772,12 +12780,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):
......
...@@ -390,7 +390,7 @@ class FusedCFuncDefNode(StatListNode): ...@@ -390,7 +390,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,
...@@ -400,7 +400,7 @@ class FusedCFuncDefNode(StatListNode): ...@@ -400,7 +400,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'
......
...@@ -3753,18 +3753,12 @@ class DefNodeWrapper(FuncDefNode): ...@@ -3753,18 +3753,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;" % (
...@@ -4005,17 +3999,14 @@ class DefNodeWrapper(FuncDefNode): ...@@ -4005,17 +3999,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
......
...@@ -316,6 +316,21 @@ class PyrexType(BaseType): ...@@ -316,6 +316,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:
...@@ -493,12 +508,11 @@ class CTypedefType(BaseType): ...@@ -493,12 +508,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"))
...@@ -621,6 +635,7 @@ class MemoryViewSliceType(PyrexType): ...@@ -621,6 +635,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,18 @@ class MemoryViewSliceType(PyrexType): ...@@ -767,7 +782,18 @@ 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:
# Requesting read-only views is always ok => consider only the non-const base type.
dst_dtype = dst_dtype.const_base_type
if src_dtype.is_const:
# When assigning between read-only views, compare only the non-const base types.
src_dtype = src_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 +911,12 @@ class MemoryViewSliceType(PyrexType): ...@@ -885,11 +911,12 @@ 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' % ( # NOTE: auto-detection of readonly buffers is disabled:
result_code, # writable = self.writable_needed or not self.dtype.is_const
from_py_function or self.from_py_function, writable = not self.dtype.is_const
source_code, return self._assign_from_py_code(
code.error_goto_if(error_condition or self.error_condition(result_code), error_pos)) source_code, result_code, error_pos, code, from_py_function, error_condition,
extra_args=['PyBUF_WRITABLE' if writable else '0'])
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)
...@@ -917,25 +944,29 @@ class MemoryViewSliceType(PyrexType): ...@@ -917,25 +944,29 @@ class MemoryViewSliceType(PyrexType):
if self.dtype.is_pyobject: if self.dtype.is_pyobject:
utility_name = "MemviewObjectToObject" utility_name = "MemviewObjectToObject"
else: else:
to_py = self.dtype.create_to_py_utility_code(env) self.dtype.create_to_py_utility_code(env)
from_py = self.dtype.create_from_py_utility_code(env) to_py_function = self.dtype.to_py_function
if not (to_py or from_py):
return "NULL", "NULL"
if not self.dtype.to_py_function: from_py_function = None
get_function = "NULL" if not self.dtype.is_const:
self.dtype.create_from_py_utility_code(env)
from_py_function = self.dtype.from_py_function
if not self.dtype.from_py_function: if not (to_py_function or from_py_function):
return "NULL", "NULL"
if not to_py_function:
get_function = "NULL"
if not from_py_function:
set_function = "NULL" set_function = "NULL"
utility_name = "MemviewDtypeToObject" utility_name = "MemviewDtypeToObject"
error_condition = (self.dtype.error_condition('value') or error_condition = (self.dtype.error_condition('value') or
'PyErr_Occurred()') 'PyErr_Occurred()')
context.update( context.update(
to_py_function = self.dtype.to_py_function, to_py_function=to_py_function,
from_py_function = self.dtype.from_py_function, from_py_function=from_py_function,
dtype = self.dtype.empty_declaration_code(), dtype=self.dtype.empty_declaration_code(),
error_condition = error_condition, error_condition=error_condition,
) )
utility = TempitaUtilityCode.load_cached( utility = TempitaUtilityCode.load_cached(
...@@ -1470,11 +1501,9 @@ class CType(PyrexType): ...@@ -1470,11 +1501,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):
...@@ -2438,6 +2467,7 @@ class CArrayType(CPointerBaseType): ...@@ -2438,6 +2467,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, '%s: %s' % (error_pos, 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)
...@@ -3879,16 +3909,6 @@ class CEnumType(CIntLike, CType): ...@@ -3879,16 +3909,6 @@ class CEnumType(CIntLike, CType):
self.name, self.cname, self.typedef_flag, namespace) self.name, self.cname, self.typedef_flag, namespace)
return self return self
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);
......
...@@ -227,6 +227,33 @@ As for NumPy, new axes can be introduced by indexing an array with ``None`` :: ...@@ -227,6 +227,33 @@ As for NumPy, new axes can be introduced by indexing an array with ``None`` ::
One may mix new axis indexing with all other forms of indexing and slicing. One may mix new axis indexing with all other forms of indexing and slicing.
See also an example_. See also an example_.
Read-only views
---------------
Since Cython 0.28, the memoryview item type can be declared as ``const`` to
support read-only buffers as input::
cdef const double[:] myslice # const item type => read-only view
a = np.linspace(0, 10, num=50)
a.setflags(write=False)
myslice = a
Note that this does not *require* the input buffer to be read-only::
a = np.linspace(0, 10, num=50)
myslice = a # read-only view of a writable buffer
Writable buffers are still accepted by ``const`` views, but read-only
buffers are not accepted for non-const, writable views::
cdef double[:] myslice # a normal read/write memory view
a = np.linspace(0, 10, num=50)
a.setflags(write=False)
myslice = a # ERROR: requesting writable memory view from read-only buffer!
Comparison to the old buffer support Comparison to the old buffer support
==================================== ====================================
......
...@@ -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
......
# mode: error # mode: error
# cython: auto_pickle=False
ctypedef int[1] int_array ctypedef int[1] int_array
ctypedef int[2] int_array2 ctypedef int[2] int_array2
...@@ -30,6 +31,6 @@ d = z # not an error ...@@ -30,6 +31,6 @@ d = z # not an error
_ERRORS = u""" _ERRORS = u"""
20:0: Assignment to slice of wrong length, expected 2, got 1 21:0: Assignment to slice of wrong length, expected 2, got 1
21:0: Assignment to slice of wrong length, expected 1, got 2 22:0: Assignment to slice of wrong length, expected 1, got 2
""" """
...@@ -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]), 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,22 +629,22 @@ def writable(obj): ...@@ -625,22 +629,22 @@ 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
buf[2, 2, 1] = 23 buf[2, 2, 1] = 23
@testcase @testcase
def strided(int[:] buf): def strided(const 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
...@@ -649,13 +653,13 @@ def strided(int[:] buf): ...@@ -649,13 +653,13 @@ def strided(int[:] buf):
return buf[2] return buf[2]
@testcase @testcase
def c_contig(int[::1] buf): def c_contig(const 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,10 +668,10 @@ def c_contig_2d(int[:, ::1] buf): ...@@ -664,10 +668,10 @@ def c_contig_2d(int[:, ::1] buf):
""" """
Multi-dim has separate implementation Multi-dim has separate 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', 'WRITABLE']
""" """
return buf[1, 3] return buf[1, 3]
...@@ -675,10 +679,10 @@ def c_contig_2d(int[:, ::1] buf): ...@@ -675,10 +679,10 @@ def c_contig_2d(int[:, ::1] buf):
@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', 'WRITABLE']
""" """
return buf[0, 1] return buf[0, 1]
...@@ -688,10 +692,10 @@ def f_contig_2d(int[::1, :] buf): ...@@ -688,10 +692,10 @@ 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', 'WRITABLE']
""" """
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,14 +919,14 @@ def printbuf_int_2d(o, shape): ...@@ -915,14 +919,14 @@ 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
released A released A
""" """
# should make shape builtin # should make shape builtin
cdef int[::view.generic, ::view.generic] buf cdef const int[::view.generic, ::view.generic] buf
buf = o buf = o
cdef int i, j cdef int i, j
for i in range(shape[0]): for i in range(shape[0]):
...@@ -933,14 +937,14 @@ def printbuf_int_2d(o, shape): ...@@ -933,14 +937,14 @@ 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
""" """
# should make shape builtin # should make shape builtin
cdef float[:] buf cdef const float[:] buf
buf = o buf = o
cdef int i, j cdef int i, j
for i in range(shape[0]): for i in range(shape[0]):
...@@ -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)), (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)), (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)), (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)), (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'
...@@ -1010,14 +1014,14 @@ def printbuf_td_h_short(td_h_short[:] buf, shape): ...@@ -1010,14 +1014,14 @@ def printbuf_td_h_short(td_h_short[:] buf, shape):
print 'END' print 'END'
@testcase @testcase
def printbuf_td_h_cy_short(td_h_cy_short[:] buf, shape): def printbuf_td_h_cy_short(const 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 'const td_h_cy_short' but got 'int'
""" """
cdef int i cdef int i
for i in range(shape[0]): for i in range(shape[0]):
...@@ -1025,14 +1029,14 @@ def printbuf_td_h_cy_short(td_h_cy_short[:] buf, shape): ...@@ -1025,14 +1029,14 @@ def printbuf_td_h_cy_short(td_h_cy_short[:] buf, shape):
print 'END' print 'END'
@testcase @testcase
def printbuf_td_h_ushort(td_h_ushort[:] buf, shape): def printbuf_td_h_ushort(const 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 'const td_h_ushort' but got 'short'
""" """
cdef int i cdef int i
for i in range(shape[0]): for i in range(shape[0]):
...@@ -1040,14 +1044,14 @@ def printbuf_td_h_ushort(td_h_ushort[:] buf, shape): ...@@ -1040,14 +1044,14 @@ def printbuf_td_h_ushort(td_h_ushort[:] buf, shape):
print 'END' print 'END'
@testcase @testcase
def printbuf_td_h_double(td_h_double[:] buf, shape): def printbuf_td_h_double(const 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 'const td_h_double' but got 'float'
""" """
cdef int i cdef int i
for i in range(shape[0]): for i in range(shape[0]):
...@@ -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,11 +1149,11 @@ def bufdefaults1(int[:] buf): ...@@ -1145,11 +1149,11 @@ 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', 'WRITABLE']
""" """
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)), indirect=True) # , 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)), indirect=True) # , 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(const 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])
# mode: run
# tag: readonly, const, numpy
import numpy as np
def new_array():
return np.arange(10).astype('float')
ARRAY = new_array()
cdef getmax(const double[:] x):
"""Example code, should work with both ro and rw memoryviews"""
cdef double max_val = -float('inf')
for val in x:
if val > max_val:
max_val = val
return max_val
cdef update_array(double [:] x):
"""Modifying a ro memoryview should raise an error"""
x[0] = 23.
cdef getconst(const double [:] x):
"""Should accept ro memoryviews"""
return x[0]
def test_mmview_rw(x):
"""
>>> test_mmview_rw(ARRAY)
9.0
"""
return getmax(x)
def test_mmview_ro(x):
"""
>>> test_mmview_ro(new_array())
9.0
"""
x.setflags(write=False)
assert x.flags.writeable is False
return getmax(x)
def test_update_mmview_rw(x):
"""
>>> test_update_mmview_rw(new_array())
23.0
"""
update_array(x)
return x[0]
def test_update_mmview_ro(x):
"""
>>> test_update_mmview_ro(new_array())
0.0
"""
x.setflags(write=False)
assert x.flags.writeable is False
try:
update_array(x)
except ValueError: pass
else:
assert False, "RO error not raised!"
return getconst(x)
def test_rw_call_getmax(double[:] x):
"""
>>> test_rw_call_getmax(new_array())
23.0
"""
update_array(x)
assert getconst(x) == 23
return getmax(x)
def test_const_mmview_ro(x):
"""
>>> test_const_mmview_ro(new_array())
0.0
"""
x.setflags(write=False)
assert x.flags.writeable is False
return getconst(x)
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