Commit a0e188d4 authored by Mark Florisson's avatar Mark Florisson

Memoryview slice item assignment, better errors, pass all tests

parent df5626e4
...@@ -594,8 +594,14 @@ class ExprNode(Node): ...@@ -594,8 +594,14 @@ class ExprNode(Node):
"Cannot convert '%s' to memoryviewslice" % "Cannot convert '%s' to memoryviewslice" %
(src_type,)) (src_type,))
elif not MemoryView.src_conforms_to_dst(src.type, dst_type): elif not MemoryView.src_conforms_to_dst(src.type, dst_type):
error(self.pos, "Memoryview '%s' not conformable to memoryview '%s'." % if src.type.dtype.same_as(dst_type.dtype):
(src.type, dst_type)) msg = "Memoryview '%s' not conformable to memoryview '%s'."
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)
elif dst_type.is_pyobject: elif dst_type.is_pyobject:
if not src.type.is_pyobject: if not src.type.is_pyobject:
...@@ -2412,13 +2418,13 @@ class IndexNode(ExprNode): ...@@ -2412,13 +2418,13 @@ class IndexNode(ExprNode):
self.is_temp = True self.is_temp = True
if setting and self.base.type.is_memoryviewslice: if setting and self.base.type.is_memoryviewslice:
self.type.writable_needed = True self.base.type.writable_needed = True
elif setting: elif setting:
if not self.base.entry.type.writable: if not self.base.entry.type.writable:
error(self.pos, "Writing to readonly buffer") error(self.pos, "Writing to readonly buffer")
else: else:
self.writable_needed = True self.writable_needed = True
if self.type.is_buffer: if self.base.type.is_buffer:
self.base.entry.buffer_aux.writable_needed = True self.base.entry.buffer_aux.writable_needed = True
elif memoryviewslice_access: elif memoryviewslice_access:
...@@ -2570,7 +2576,7 @@ class IndexNode(ExprNode): ...@@ -2570,7 +2576,7 @@ class IndexNode(ExprNode):
if self.is_buffer_access: if self.is_buffer_access:
if code.globalstate.directives['nonecheck']: if code.globalstate.directives['nonecheck']:
self.put_nonecheck(code) self.put_nonecheck(code)
self.buffer_ptr_code = self.buffer_lookup_code(code) buffer_entry, self.buffer_ptr_code = self.buffer_lookup_code(code)
if self.type.is_pyobject: if self.type.is_pyobject:
# is_temp is True, so must pull out value and incref it. # is_temp is True, so must pull out value and incref it.
code.putln("%s = *%s;" % (self.result(), self.buffer_ptr_code)) code.putln("%s = *%s;" % (self.result(), self.buffer_ptr_code))
...@@ -2657,11 +2663,14 @@ class IndexNode(ExprNode): ...@@ -2657,11 +2663,14 @@ class IndexNode(ExprNode):
# Used from generate_assignment_code and InPlaceAssignmentNode # Used from generate_assignment_code and InPlaceAssignmentNode
if code.globalstate.directives['nonecheck']: if code.globalstate.directives['nonecheck']:
self.put_nonecheck(code) self.put_nonecheck(code)
ptrexpr = self.buffer_lookup_code(code)
buffer_entry, ptrexpr = self.buffer_lookup_code(code)
if self.buffer_type.dtype.is_pyobject: if self.buffer_type.dtype.is_pyobject:
# Must manage refcounts. Decref what is already there # Must manage refcounts. Decref what is already there
# and incref what we put in. # and incref what we put in.
ptr = code.funcstate.allocate_temp(self.buffer_type.buffer_ptr_type, manage_ref=False) ptr = code.funcstate.allocate_temp(buffer_entry.buf_ptr_type,
manage_ref=False)
rhs_code = rhs.result() rhs_code = rhs.result()
code.putln("%s = %s;" % (ptr, ptrexpr)) code.putln("%s = %s;" % (ptr, ptrexpr))
code.put_gotref("*%s" % ptr) code.put_gotref("*%s" % ptr)
...@@ -2735,12 +2744,14 @@ class IndexNode(ExprNode): ...@@ -2735,12 +2744,14 @@ class IndexNode(ExprNode):
buffer_entry = MemoryView.MemoryViewSliceBufferEntry(entry) buffer_entry = MemoryView.MemoryViewSliceBufferEntry(entry)
negative_indices = Buffer.buffer_defaults['negative_indices'] negative_indices = Buffer.buffer_defaults['negative_indices']
return Buffer.put_buffer_lookup_code(entry=buffer_entry,
index_signeds=[i.type.signed for i in self.indices], return buffer_entry, Buffer.put_buffer_lookup_code(
index_cnames=index_temps, entry=buffer_entry,
directives=code.globalstate.directives, index_signeds=[i.type.signed for i in self.indices],
pos=self.pos, code=code, index_cnames=index_temps,
negative_indices=negative_indices) directives=code.globalstate.directives,
pos=self.pos, code=code,
negative_indices=negative_indices)
def put_nonecheck(self, code): def put_nonecheck(self, code):
code.globalstate.use_utility_code(raise_noneindex_error_utility_code) code.globalstate.use_utility_code(raise_noneindex_error_utility_code)
...@@ -7681,6 +7692,13 @@ class CoerceToMemViewSliceNode(CoercionNode): ...@@ -7681,6 +7692,13 @@ class CoerceToMemViewSliceNode(CoercionNode):
self.type.from_py_function, self.type.from_py_function,
self.arg.py_result())) self.arg.py_result()))
error_cond = self.type.error_condition(self.result())
code.putln(code.error_goto_if(error_cond, self.pos))
def generate_disposal_code(self, code):
code.put_xdecref_memoryviewslice(self.result(),
have_gil=not self.env.nogil)
class CastNode(CoercionNode): class CastNode(CoercionNode):
# Wrap a node in a C type cast. # Wrap a node in a C type cast.
......
...@@ -14,9 +14,8 @@ STOP_ERR = "Axis specification only allowed in the 'stop' slot." ...@@ -14,9 +14,8 @@ STOP_ERR = "Axis specification only allowed in the 'stop' slot."
STEP_ERR = "Only the value 1 (one) or valid axis specification allowed in the step slot." STEP_ERR = "Only the value 1 (one) or valid axis specification allowed in the step slot."
ONE_ERR = "The value 1 (one) may appear in the first or last axis specification only." ONE_ERR = "The value 1 (one) may appear in the first or last axis specification only."
BOTH_CF_ERR = "Cannot specify an array that is both C and Fortran contiguous." BOTH_CF_ERR = "Cannot specify an array that is both C and Fortran contiguous."
NOT_AMP_ERR = "Invalid operator, only an ampersand '&' is allowed."
INVALID_ERR = "Invalid axis specification." INVALID_ERR = "Invalid axis specification."
EXPR_ERR = "no expressions allowed in axis spec, only names (e.g. cython.view.contig)." EXPR_ERR = "no expressions allowed in axis spec, only names and literals."
CF_ERR = "Invalid axis specification for a C/Fortran contiguous array." CF_ERR = "Invalid axis specification for a C/Fortran contiguous array."
def concat_flags(*flags): def concat_flags(*flags):
...@@ -24,9 +23,9 @@ def concat_flags(*flags): ...@@ -24,9 +23,9 @@ def concat_flags(*flags):
format_flag = "PyBUF_FORMAT" format_flag = "PyBUF_FORMAT"
memview_c_contiguous = concat_flags(format_flag, "PyBUF_C_CONTIGUOUS") memview_c_contiguous = "(PyBUF_C_CONTIGUOUS | PyBUF_FORMAT | PyBUF_WRITABLE)"
memview_f_contiguous = concat_flags(format_flag, "PyBUF_F_CONTIGUOUS") memview_f_contiguous = "(PyBUF_F_CONTIGUOUS | PyBUF_FORMAT | PyBUF_WRITABLE)"
memview_any_contiguous = concat_flags(format_flag, "PyBUF_ANY_CONTIGUOUS") memview_any_contiguous = "(PyBUF_ANY_CONTIGUOUS | PyBUF_FORMAT | PyBUF_WRITABLE)"
memview_full_access = "PyBUF_FULL" memview_full_access = "PyBUF_FULL"
#memview_strided_access = "PyBUF_STRIDED" #memview_strided_access = "PyBUF_STRIDED"
memview_strided_access = "PyBUF_RECORDS" memview_strided_access = "PyBUF_RECORDS"
...@@ -72,8 +71,8 @@ def mangle_dtype_name(dtype): ...@@ -72,8 +71,8 @@ def mangle_dtype_name(dtype):
import Buffer import Buffer
return Buffer.mangle_dtype_name(dtype) return Buffer.mangle_dtype_name(dtype)
def axes_to_str(axes): #def axes_to_str(axes):
return "".join([access[0].upper()+packing[0] for (access, packing) in axes]) # return "".join([access[0].upper()+packing[0] for (access, packing) in axes])
def put_acquire_memoryviewslice(lhs_cname, lhs_type, lhs_pos, rhs, code, def put_acquire_memoryviewslice(lhs_cname, lhs_type, lhs_pos, rhs, code,
incref_rhs=True, have_gil=False): incref_rhs=True, have_gil=False):
...@@ -106,7 +105,7 @@ def put_assign_to_memviewslice(lhs_cname, rhs_cname, memviewslicetype, code, ...@@ -106,7 +105,7 @@ def put_assign_to_memviewslice(lhs_cname, rhs_cname, memviewslicetype, code,
code.putln("%s.strides[%d] = %s.strides[%d];" % (lhs_cname, i, rhs_cname, i)) code.putln("%s.strides[%d] = %s.strides[%d];" % (lhs_cname, i, rhs_cname, i))
code.putln("%s.suboffsets[%d] = %s.suboffsets[%d];" % (lhs_cname, i, rhs_cname, i)) code.putln("%s.suboffsets[%d] = %s.suboffsets[%d];" % (lhs_cname, i, rhs_cname, i))
def get_buf_flag(specs): def get_buf_flags(specs):
is_c_contig, is_f_contig = is_cf_contig(specs) is_c_contig, is_f_contig = is_cf_contig(specs)
if is_c_contig: if is_c_contig:
...@@ -196,50 +195,65 @@ class MemoryViewSliceBufferEntry(Buffer.BufferEntry): ...@@ -196,50 +195,65 @@ class MemoryViewSliceBufferEntry(Buffer.BufferEntry):
suboffset = "%s.suboffsets[%d]" % (self.cname, dim) suboffset = "%s.suboffsets[%d]" % (self.cname, dim)
index = index_cnames[dim] index = index_cnames[dim]
if access == 'full' and packing in ('strided', 'follow'): flag = get_memoryview_flag(access, packing)
if flag == "generic":
code.globalstate.use_utility_code(memviewslice_index_helpers) code.globalstate.use_utility_code(memviewslice_index_helpers)
bufp = ('__pyx_memviewslice_index_full(%s, %s, %s, %s)' % bufp = ('__pyx_memviewslice_index_full(%s, %s, %s, %s)' %
(bufp, index, stride, suboffset)) (bufp, index, stride, suboffset))
elif access == 'full' and packing == 'contig': elif flag == "generic_contiguous":
# We can skip stride multiplication with the cast # We can skip stride multiplication with the cast
code.globalstate.use_utility_code(memviewslice_index_helpers) code.globalstate.use_utility_code(memviewslice_index_helpers)
bufp = '((char *) ((%s *) %s) + %s)' % (type_decl, bufp, index) bufp = '((char *) ((%s *) %s) + %s)' % (type_decl, bufp, index)
bufp = ('__pyx_memviewslice_index_full_contig(%s, %s)' % bufp = ('__pyx_memviewslice_index_full_contig(%s, %s)' %
(bufp, suboffset)) (bufp, suboffset))
elif access == 'ptr' and packing in ('strided', 'follow'): elif flag == "indirect":
bufp = ("(*((char **) %s + %s * %s) + %s)" % bufp = ("(*((char **) %s + %s * %s) + %s)" %
(bufp, index, stride, suboffset)) (bufp, index, stride, suboffset))
elif access == 'ptr' and packing == 'contig': elif flag == "indirect_contiguous":
bufp = "(*((char **) %s) + %s)" % (bufp, suboffset) bufp = "(*((char **) %s) + %s)" % (bufp, suboffset)
elif access == 'direct' and packing in ('strided', 'follow'): elif flag == "strided":
bufp = "(%s + %s * %s)" % (bufp, index, stride) bufp = "(%s + %s * %s)" % (bufp, index, stride)
else: else:
assert (access, packing) == ('direct', 'contig'), (access, packing) assert flag == 'contiguous', flag
bufp = '((char *) (((%s *) %s) + %s))' % (type_decl, bufp, index) bufp = '((char *) (((%s *) %s) + %s))' % (type_decl, bufp, index)
bufp = '( /* dim=%d */ %s )' % (dim, bufp) bufp = '( /* dim=%d */ %s )' % (dim, bufp)
return "((%s *) %s)" % (type_decl, bufp) return "((%s *) %s)" % (type_decl, bufp)
def get_memoryview_flag(access, packing):
if access == 'full' and packing in ('strided', 'follow'):
return 'generic'
elif access == 'full' and packing == 'contig':
return 'generic_contiguous'
elif access == 'ptr' and packing in ('strided', 'follow'):
return 'indirect'
elif access == 'ptr' and packing == 'contig':
return 'indirect_contiguous'
elif access == 'direct' and packing in ('strided', 'follow'):
return 'strided'
else:
assert (access, packing) == ('direct', 'contig'), (access, packing)
return 'contiguous'
def get_copy_func_name(to_memview): def get_copy_func_name(to_memview):
base = "__Pyx_BufferNew_%s_From_%s_%s" base = "__Pyx_BufferNew_%s_From_%s"
if to_memview.is_c_contig: if to_memview.is_c_contig:
return base % ('C', axes_to_str(to_memview.axes), mangle_dtype_name(to_memview.dtype)) return base % ('C', to_memview.specialization_suffix())
else: else:
return base % ('F', axes_to_str(to_memview.axes), mangle_dtype_name(to_memview.dtype)) return base % ('F', to_memview.specialization_suffix())
def get_copy_contents_name(from_mvs, to_mvs): def get_copy_contents_name(from_mvs, to_mvs):
dtype = from_mvs.dtype assert from_mvs.dtype == to_mvs.dtype
assert dtype == to_mvs.dtype return '__Pyx_BufferCopyContents_%s_to_%s' % (from_mvs.specialization_suffix(),
return ('__Pyx_BufferCopyContents_%s_%s_%s' % to_mvs.specialization_suffix())
(axes_to_str(from_mvs.axes),
axes_to_str(to_mvs.axes),
mangle_dtype_name(dtype)))
class IsContigFuncUtilCode(object): class IsContigFuncUtilCode(object):
......
...@@ -1428,16 +1428,20 @@ class FuncDefNode(StatNode, BlockNode): ...@@ -1428,16 +1428,20 @@ class FuncDefNode(StatNode, BlockNode):
self.generate_argument_parsing_code(env, code) self.generate_argument_parsing_code(env, code)
# If an argument is assigned to in the body, we must # If an argument is assigned to in the body, we must
# incref it to properly keep track of refcounts. # incref it to properly keep track of refcounts.
is_cdef = isinstance(self, CFuncDefNode)
for entry in lenv.arg_entries: for entry in lenv.arg_entries:
if entry.type.is_pyobject: if entry.type.is_pyobject:
if (acquire_gil or entry.assignments) and not entry.in_closure: if (acquire_gil or entry.assignments) and not entry.in_closure:
code.put_var_incref(entry) code.put_var_incref(entry)
# Note: all memoryviewslices are already newly acquired references
# or increffed defaults! # Note: defaults are always increffed. For def functions, we
#if entry.type.is_memoryviewslice: # we aquire arguments from object converstion, so we have
# code.put_incref_memoryviewslice(entry.cname, # new references. If we are a cdef function, we need to
# have_gil=not lenv.nogil) # incref our arguments
#code.put_incref("%s.memview" % entry.cname, cython_memoryview_ptr_type) if is_cdef and entry.type.is_memoryviewslice:
code.put_incref_memoryviewslice(entry.cname,
have_gil=not lenv.nogil)
# ----- Initialise local buffer auxiliary variables # ----- Initialise local buffer auxiliary variables
for entry in lenv.var_entries + lenv.arg_entries: for entry in lenv.var_entries + lenv.arg_entries:
if entry.type.is_buffer and entry.buffer_aux.buflocal_nd_var.used: if entry.type.is_buffer and entry.buffer_aux.buflocal_nd_var.used:
...@@ -1567,7 +1571,7 @@ class FuncDefNode(StatNode, BlockNode): ...@@ -1567,7 +1571,7 @@ class FuncDefNode(StatNode, BlockNode):
if entry.type.is_pyobject: if entry.type.is_pyobject:
if (acquire_gil or entry.assignments) and not entry.in_closure: if (acquire_gil or entry.assignments) and not entry.in_closure:
code.put_var_decref(entry) code.put_var_decref(entry)
if entry.type.is_memoryviewslice and isinstance(self, DefNode): if entry.type.is_memoryviewslice:
code.put_xdecref_memoryviewslice(entry.cname) code.put_xdecref_memoryviewslice(entry.cname)
#code.put_decref("%s.memview" % entry.cname, cython_memoryview_ptr_type) #code.put_decref("%s.memview" % entry.cname, cython_memoryview_ptr_type)
if self.needs_closure: if self.needs_closure:
......
...@@ -338,9 +338,7 @@ class MemoryViewSliceType(PyrexType): ...@@ -338,9 +338,7 @@ class MemoryViewSliceType(PyrexType):
to_py_function = None to_py_function = None
exception_value = None exception_value = None
exception_check = None exception_check = True
utility_counter = 0
def __init__(self, base_dtype, axes): def __init__(self, base_dtype, axes):
''' '''
...@@ -381,7 +379,7 @@ class MemoryViewSliceType(PyrexType): ...@@ -381,7 +379,7 @@ class MemoryViewSliceType(PyrexType):
self.dtype = base_dtype self.dtype = base_dtype
self.axes = axes self.axes = axes
self.ndim = len(axes) self.ndim = len(axes)
self.flags = MemoryView.get_buf_flag(self.axes) self.flags = MemoryView.get_buf_flags(self.axes)
self.is_c_contig, self.is_f_contig = MemoryView.is_cf_contig(self.axes) self.is_c_contig, self.is_f_contig = MemoryView.is_cf_contig(self.axes)
assert not (self.is_c_contig and self.is_f_contig) assert not (self.is_c_contig and self.is_f_contig)
...@@ -416,7 +414,6 @@ class MemoryViewSliceType(PyrexType): ...@@ -416,7 +414,6 @@ class MemoryViewSliceType(PyrexType):
if self.scope is None: if self.scope is None:
import Symtab, MemoryView, Options import Symtab, MemoryView, Options
from MemoryView import axes_to_str
self.scope = scope = Symtab.CClassScope( self.scope = scope = Symtab.CClassScope(
'mvs_class_'+self.specialization_suffix(), 'mvs_class_'+self.specialization_suffix(),
...@@ -504,12 +501,12 @@ class MemoryViewSliceType(PyrexType): ...@@ -504,12 +501,12 @@ class MemoryViewSliceType(PyrexType):
def specialization_suffix(self): def specialization_suffix(self):
import MemoryView import MemoryView
return MemoryView.axes_to_str(self.axes) + '_' + MemoryView.mangle_dtype_name(self.dtype) dtype_name = MemoryView.mangle_dtype_name(self.dtype)
return "%s_%s" % (self.axes_to_name(), dtype_name)
def global_init_code(self, entry, code): #def global_init_code(self, entry, code):
code.putln("%s.data = NULL;" % entry.cname) # code.putln("%s.data = NULL;" % entry.cname)
code.putln("%s.memview = NULL;" % entry.cname) # code.putln("%s.memview = NULL;" % entry.cname)
#code.put_init_to_py_none("%s.memview" % entry.cname, cython_memoryview_ptr_type, nanny=False)
def check_for_null_code(self, cname): def check_for_null_code(self, cname):
return cname + '.memview' return cname + '.memview'
...@@ -535,15 +532,14 @@ class MemoryViewSliceType(PyrexType): ...@@ -535,15 +532,14 @@ class MemoryViewSliceType(PyrexType):
else: else:
c_or_f_flag = "0" c_or_f_flag = "0"
# specializing through UtilityCode.specialize is not so useful as suffix = self.specialization_suffix()
# specialize on too many things to include in the function name funcname = "__Pyx_PyObject_to_MemoryviewSlice_" + suffix
funcname = "__Pyx_PyObject_to_MemoryviewSlice_%d" % self.utility_counter
context = dict( context = dict(
MemoryView.context, MemoryView.context,
buf_flag = self.flags, buf_flag = self.flags,
ndim = self.ndim, ndim = self.ndim,
axes_specs = ', '.join(self.axes_specs_to_code()), axes_specs = ', '.join(self.axes_to_code()),
dtype_typedecl = self.dtype.declaration_code(""), dtype_typedecl = self.dtype.declaration_code(""),
struct_nesting_depth = self.dtype.struct_nesting_depth(), struct_nesting_depth = self.dtype.struct_nesting_depth(),
c_or_f_flag = c_or_f_flag, c_or_f_flag = c_or_f_flag,
...@@ -551,8 +547,6 @@ class MemoryViewSliceType(PyrexType): ...@@ -551,8 +547,6 @@ class MemoryViewSliceType(PyrexType):
) )
self.from_py_function = funcname self.from_py_function = funcname
MemoryViewSliceType.utility_counter += 1
return True return True
def create_to_py_utility_code(self, env): def create_to_py_utility_code(self, env):
...@@ -560,15 +554,15 @@ class MemoryViewSliceType(PyrexType): ...@@ -560,15 +554,15 @@ class MemoryViewSliceType(PyrexType):
def get_to_py_function(self, obj): def get_to_py_function(self, obj):
return "__pyx_memoryview_fromslice(&%s, %s.memview->obj, %s, %s);" % ( return "__pyx_memoryview_fromslice(&%s, %s.memview->obj, %s, %s);" % (
obj.result(), obj.result(), self.flags, self.ndim) obj.result(), obj.result(), self.flags, self.ndim)
def axes_specs_to_code(self): def axes_to_code(self):
"Return a list of code constants for each axis" "Return a list of code constants for each axis"
import MemoryView import MemoryView
d = MemoryView._spec_to_const d = MemoryView._spec_to_const
return ["(%s | %s)" % (d[a], d[p]) for a, p in self.axes] return ["(%s | %s)" % (d[a], d[p]) for a, p in self.axes]
def axes_specs_to_name(self): def axes_to_name(self):
"Return an abbreviated name for our axes" "Return an abbreviated name for our axes"
import MemoryView import MemoryView
d = MemoryView._spec_to_abbrev d = MemoryView._spec_to_abbrev
...@@ -577,6 +571,24 @@ class MemoryViewSliceType(PyrexType): ...@@ -577,6 +571,24 @@ class MemoryViewSliceType(PyrexType):
def error_condition(self, result_code): def error_condition(self, result_code):
return "!%s.memview" % result_code return "!%s.memview" % result_code
def __str__(self):
import MemoryView
axes_code_list = []
for access, packing in self.axes:
flag = MemoryView.get_memoryview_flag(access, packing)
if flag == "strided":
axes_code_list.append(":")
else:
axes_code_list.append("::" + flag)
if self.dtype.is_pyobject:
dtype_name = self.dtype.name
else:
dtype_name = self.dtype
return "%s[%s]" % (dtype_name, ", ".join(axes_code_list))
class BufferType(BaseType): class BufferType(BaseType):
# #
...@@ -632,6 +644,9 @@ class PyObjectType(PyrexType): ...@@ -632,6 +644,9 @@ class PyObjectType(PyrexType):
def __repr__(self): def __repr__(self):
return "<PyObjectType>" return "<PyObjectType>"
def __eq__(self, other):
return isinstance(other, PyObjectType) and self.name == other.name
def can_coerce_to_pyobject(self, env): def can_coerce_to_pyobject(self, env):
return True return True
......
...@@ -258,6 +258,9 @@ cdef class memoryview(object): ...@@ -258,6 +258,9 @@ cdef class memoryview(object):
bytesitem = itemp[:self.view.itemsize] bytesitem = itemp[:self.view.itemsize]
return struct.unpack(fmt, bytesitem) return struct.unpack(fmt, bytesitem)
def __repr__(self):
return "<MemoryView of %s at 0x%x>" % (self.obj.__class__.__name__, id(self))
def __str__(self): def __str__(self):
return "<MemoryView of %r at 0x%x>" % (self.obj, id(self)) return "<MemoryView of %r at 0x%x>" % (self.obj, id(self))
...@@ -283,8 +286,8 @@ cdef memoryview_cwrapper(object o, int flags): ...@@ -283,8 +286,8 @@ cdef memoryview_cwrapper(object o, int flags):
return memoryview(o, flags) return memoryview(o, flags)
@cname('__pyx_memoryview_fromslice') @cname('__pyx_memoryview_fromslice')
cdef memoryview memoryview_from_memview_cwrapper( cdef memoryview_from_memview_cwrapper({{memviewslice_name}} *memviewslice,
{{memviewslice_name}} *memviewslice, object orig_obj, int flags, int new_ndim): object orig_obj, int flags, int new_ndim):
cdef _memoryviewslice result = _memoryviewslice(orig_obj, flags) cdef _memoryviewslice result = _memoryviewslice(orig_obj, flags)
result.from_slice = memviewslice[0] result.from_slice = memviewslice[0]
......
# mode: error # mode: error
cimport cython cimport cython
from cython.view cimport contig as foo, full as bar, follow
from cython cimport view from cython cimport view
biz = cython.view.contig
foz = cython.view.full
adict = {'view': cython.view}
alist = [adict]
cdef signed short[::1, ::1] both cdef signed short[::1, ::1] both
cdef signed short[::1, :, :, ::1] both2 cdef signed short[::1, :, :, ::1] both2
...@@ -17,14 +17,15 @@ cdef long long[01::1, 0x01:, '0' :, False:] fort_contig0 ...@@ -17,14 +17,15 @@ cdef long long[01::1, 0x01:, '0' :, False:] fort_contig0
cdef signed char[1::] bad_start cdef signed char[1::] bad_start
cdef unsigned long[:,:1] bad_stop cdef unsigned long[:,:1] bad_stop
cdef unsigned long[:,::1,:] neither_c_or_f cdef unsigned long[:,::1,:] neither_c_or_f
cdef signed char[::1, ::view.follow & view.direct] bad_f_contig
cdef signed char[::1, ::view.follow] bad_f_contig2
cdef signed char[::view.contig | view.direct] not_ampersand
cdef signed char[::view.ptr & view.direct] no_access_spec
cdef signed char[::1-1+1] expr_spec cdef signed char[::1-1+1] expr_spec
cdef signed char[::blargh] bad_name cdef signed char[::blargh] bad_name
cdef double[::alist[0]['view'].full] expr_attribute cdef double[::alist[0]['view'].full] expr_attribute
cdef double[::view.ptr & view.follow] no_single_follow
cdef object[::1, :] unconformable1 = object()
cdef object[:, ::1] unconformable2 = unconformable1
cdef int[::1, :] dtype_unconformable = object()
unconformable1 = dtype_unconformable
_ERRORS = u''' _ERRORS = u'''
11:25: Cannot specify an array that is both C and Fortran contiguous. 11:25: Cannot specify an array that is both C and Fortran contiguous.
...@@ -36,12 +37,9 @@ _ERRORS = u''' ...@@ -36,12 +37,9 @@ _ERRORS = u'''
17:18: there must be nothing or the value 0 (zero) in the start slot. 17:18: there must be nothing or the value 0 (zero) in the start slot.
18:22: Axis specification only allowed in the 'stop' slot. 18:22: Axis specification only allowed in the 'stop' slot.
19:23: The value 1 (one) may appear in the first or last axis specification only. 19:23: The value 1 (one) may appear in the first or last axis specification only.
20:36: Invalid axis specification for a C/Fortran contiguous array. 20:22: Invalid axis specification.
21:28: Invalid axis specification for a C/Fortran contiguous array. 21:25: Invalid axis specification.
22:31: Invalid operator, only an ampersand '&' is allowed. 22:22: no expressions allowed in axis spec, only names and literals.
23:28: Invalid axis specification. 25:51: Memoryview 'object[::contiguous, :]' not conformable to memoryview 'object[:, ::contiguous]'.
24:22: Invalid axis specification. 28:36: Different base types for memoryviews (int, Python object)
25:25: Invalid axis specification.
26:22: no expressions allowed in axis spec, only names (e.g. cython.view.contig).
27:12: Invalid use of the follow specifier.
''' '''
...@@ -1062,22 +1062,6 @@ def nested_packed_struct(object[NestedPackedStruct] buf): ...@@ -1062,22 +1062,6 @@ def nested_packed_struct(object[NestedPackedStruct] buf):
""" """
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
cdef struct LongComplex:
long double real
long double imag
cdef class LongComplexMockBuffer(MockBuffer):
cdef int write(self, char* buf, object value) except -1:
cdef LongComplex* s
s = <LongComplex*>buf;
s.real, s.imag = value
return 0
cdef get_itemsize(self): return sizeof(LongComplex)
cdef get_default_format(self): return b"Zg"
#cdef extern from "complex.h":
# pass
@testcase @testcase
def complex_dtype(object[long double complex] buf): def complex_dtype(object[long double complex] buf):
......
# Tests the buffer access syntax functionality by constructing # Note: see also bufaccess.pyx
# mock buffer objects.
#
# Note that the buffers are mock objects created for testing
# the buffer access behaviour -- for instance there is no flag
# checking in the buffer objects (why test our test case?), rather
# what we want to test is what is passed into the flags argument.
#
from __future__ import unicode_literals from __future__ import unicode_literals
...@@ -417,25 +410,6 @@ def list_comprehension(int[:] buf, len): ...@@ -417,25 +410,6 @@ def list_comprehension(int[:] buf, len):
cdef int i cdef int i
print u"|".join([unicode(buf[i]) for i in range(len)]) print u"|".join([unicode(buf[i]) for i in range(len)])
#
# The negative_indices buffer option
#
@testcase
def no_negative_indices(object[int, negative_indices=False] buf, int idx):
"""
The most interesting thing here is to inspect the C source and
make sure optimal code is produced.
>>> A = IntMockBuffer(None, range(6))
>>> no_negative_indices(A, 3)
3
>>> no_negative_indices(A, -1)
Traceback (most recent call last):
...
IndexError: Out of bounds on buffer access (axis 0)
"""
return buf[idx]
@testcase @testcase
@cython.wraparound(False) @cython.wraparound(False)
def wraparound_directive(int[:] buf, int pos_idx, int neg_idx): def wraparound_directive(int[:] buf, int pos_idx, int neg_idx):
...@@ -459,19 +433,6 @@ def wraparound_directive(int[:] buf, int pos_idx, int neg_idx): ...@@ -459,19 +433,6 @@ def wraparound_directive(int[:] buf, int pos_idx, int neg_idx):
# #
# Test which flags are passed. # Test which flags are passed.
# #
# @testcase
# def readonly(obj):
# """
# >>> R = UnsignedShortMockBuffer("R", range(27), shape=(3, 3, 3))
# >>> readonly(R)
# acquired R
# 25
# released R
# >>> [str(x) for x in R.recieved_flags] # Works in both py2 and py3
# ['FORMAT', 'INDIRECT', 'ND', 'STRIDES']
# """
# cdef unsigned short int[:, :, :] buf = obj
# print buf[2, 2, 1]
@testcase @testcase
def writable(obj): def writable(obj):
...@@ -510,7 +471,7 @@ def c_contig(int[::1] buf): ...@@ -510,7 +471,7 @@ def c_contig(int[::1] buf):
>>> c_contig(A) >>> c_contig(A)
2 2
>>> [str(x) for x in A.recieved_flags] >>> [str(x) for x in A.recieved_flags]
['FORMAT', 'ND', 'STRIDES', 'C_CONTIGUOUS'] ['FORMAT', 'ND', 'STRIDES', 'C_CONTIGUOUS', 'WRITABLE']
""" """
return buf[2] return buf[2]
...@@ -523,7 +484,7 @@ def c_contig_2d(int[:, ::1] buf): ...@@ -523,7 +484,7 @@ def c_contig_2d(int[:, ::1] buf):
>>> c_contig_2d(A) >>> c_contig_2d(A)
7 7
>>> [str(x) for x in A.recieved_flags] >>> [str(x) for x in A.recieved_flags]
['FORMAT', 'ND', 'STRIDES', 'C_CONTIGUOUS'] ['FORMAT', 'ND', 'STRIDES', 'C_CONTIGUOUS', 'WRITABLE']
""" """
return buf[1, 3] return buf[1, 3]
...@@ -534,12 +495,12 @@ def f_contig(int[::1, :] buf): ...@@ -534,12 +495,12 @@ def f_contig(int[::1, :] buf):
>>> f_contig(A) >>> f_contig(A)
2 2
>>> [str(x) for x in A.recieved_flags] >>> [str(x) for x in A.recieved_flags]
['FORMAT', 'ND', 'STRIDES', 'F_CONTIGUOUS'] ['FORMAT', 'ND', 'STRIDES', 'F_CONTIGUOUS', 'WRITABLE']
""" """
return buf[0, 1] return buf[0, 1]
@testcase @testcase
def f_contig_2d(object[int, ndim=2, mode='fortran'] buf): 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.
...@@ -547,7 +508,7 @@ def f_contig_2d(object[int, ndim=2, mode='fortran'] buf): ...@@ -547,7 +508,7 @@ def f_contig_2d(object[int, ndim=2, mode='fortran'] buf):
>>> f_contig_2d(A) >>> f_contig_2d(A)
7 7
>>> [str(x) for x in A.recieved_flags] >>> [str(x) for x in A.recieved_flags]
['FORMAT', 'ND', 'STRIDES', 'F_CONTIGUOUS'] ['FORMAT', 'ND', 'STRIDES', 'F_CONTIGUOUS', 'WRITABLE']
""" """
return buf[3, 1] return buf[3, 1]
...@@ -600,18 +561,6 @@ def unsafe_get(int[:] buf, int idx): ...@@ -600,18 +561,6 @@ def unsafe_get(int[:] buf, int idx):
""" """
return buf[idx] return buf[idx]
# @testcase
# @cython.boundscheck(False)
# def unsafe_get_nonegative(object[int, negative_indices=False] buf, int idx):
# """
# Also inspect the C source to see that it is optimal...
#
# >>> A = IntMockBuffer(None, range(10), shape=(3,), offset=5)
# >>> unsafe_get_nonegative(A, -2)
# 3
# """
# return buf[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):
""" """
...@@ -629,25 +578,6 @@ def mixed_get(int[:] buf, int unsafe_idx, int safe_idx): ...@@ -629,25 +578,6 @@ def mixed_get(int[:] buf, int unsafe_idx, int safe_idx):
two = buf[safe_idx] two = buf[safe_idx]
return (one, two) return (one, two)
#
# Coercions
#
## @testcase
## def coercions(object[unsigned char] uc):
## """
## TODO
## """
## print type(uc[0])
## uc[0] = -1
## print uc[0]
## uc[0] = <int>3.14
## print uc[0]
## cdef char* ch = b"asfd"
## cdef object[object] objbuf
## objbuf[3] = ch
# #
# Testing that accessing data using various types of buffer access # Testing that accessing data using various types of buffer access
# all works. # all works.
...@@ -759,7 +689,7 @@ def printbuf_td_cy_int(td_cy_int[:] buf, shape): ...@@ -759,7 +689,7 @@ def printbuf_td_cy_int(td_cy_int[:] buf, shape):
print 'END' print 'END'
@testcase @testcase
def printbuf_td_h_short(object[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,))
0 1 2 END 0 1 2 END
...@@ -774,7 +704,7 @@ def printbuf_td_h_short(object[td_h_short] buf, shape): ...@@ -774,7 +704,7 @@ def printbuf_td_h_short(object[td_h_short] buf, shape):
print 'END' print 'END'
@testcase @testcase
def printbuf_td_h_cy_short(object[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)), (3,))
0 1 2 END 0 1 2 END
...@@ -789,7 +719,7 @@ def printbuf_td_h_cy_short(object[td_h_cy_short] buf, shape): ...@@ -789,7 +719,7 @@ def printbuf_td_h_cy_short(object[td_h_cy_short] buf, shape):
print 'END' print 'END'
@testcase @testcase
def printbuf_td_h_ushort(object[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)), (3,))
0 1 2 END 0 1 2 END
...@@ -804,7 +734,7 @@ def printbuf_td_h_ushort(object[td_h_ushort] buf, shape): ...@@ -804,7 +734,7 @@ def printbuf_td_h_ushort(object[td_h_ushort] buf, shape):
print 'END' print 'END'
@testcase @testcase
def printbuf_td_h_double(object[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]), (3,))
0.25 1.0 3.125 END 0.25 1.0 3.125 END
...@@ -831,7 +761,7 @@ def get_refcount(x): ...@@ -831,7 +761,7 @@ def get_refcount(x):
return (<PyObject*>x).ob_refcnt return (<PyObject*>x).ob_refcnt
@testcase @testcase
def printbuf_object(object[object] buf, shape): def printbuf_object(object[:] buf, shape):
""" """
Only play with unique objects, interned numbers etc. will have Only play with unique objects, interned numbers etc. will have
unpredictable refcounts. unpredictable refcounts.
...@@ -854,7 +784,7 @@ def printbuf_object(object[object] buf, shape): ...@@ -854,7 +784,7 @@ def printbuf_object(object[object] buf, shape):
print repr(buf[i]), (<PyObject*>buf[i]).ob_refcnt print repr(buf[i]), (<PyObject*>buf[i]).ob_refcnt
@testcase @testcase
def assign_to_object(object[object] buf, int idx, obj): def assign_to_object(object[:] buf, int idx, obj):
""" """
See comments on printbuf_object above. See comments on printbuf_object above.
...@@ -873,7 +803,7 @@ def assign_to_object(object[object] buf, int idx, obj): ...@@ -873,7 +803,7 @@ def assign_to_object(object[object] buf, int idx, obj):
buf[idx] = obj buf[idx] = obj
@testcase @testcase
def assign_temporary_to_object(object[object] buf): def assign_temporary_to_object(object[:] buf):
""" """
See comments on printbuf_object above. See comments on printbuf_object above.
...@@ -899,69 +829,11 @@ def assign_temporary_to_object(object[object] buf): ...@@ -899,69 +829,11 @@ def assign_temporary_to_object(object[object] buf):
""" """
buf[1] = {3-2: 2+(2*4)-2} buf[1] = {3-2: 2+(2*4)-2}
#
# cast option
#
@testcase
def buffer_cast(object[unsigned int, cast=True] buf, int idx):
"""
Round-trip a signed int through unsigned int buffer access.
>>> A = IntMockBuffer(None, [-100])
>>> buffer_cast(A, 0)
-100
"""
cdef unsigned int data = buf[idx]
return <int>data
@testcase
def buffer_cast_fails(object[char, cast=True] buf):
"""
Cannot cast between datatype of different sizes.
>>> buffer_cast_fails(IntMockBuffer(None, [0]))
Traceback (most recent call last):
...
ValueError: Item size of buffer (4 bytes) does not match size of 'char' (1 byte)
"""
return buf[0]
#
# Typed buffers
#
@testcase
def typedbuffer1(obj):
"""
>>> typedbuffer1(IntMockBuffer("A", range(10)))
acquired A
released A
>>> typedbuffer1(None)
>>> typedbuffer1(4)
Traceback (most recent call last):
...
TypeError: Cannot convert int to memslice.IntMockBuffer
"""
cdef IntMockBuffer[int, ndim=1] buf = obj
@testcase
def typedbuffer2(IntMockBuffer[int, ndim=1] obj):
"""
>>> typedbuffer2(IntMockBuffer("A", range(10)))
acquired A
released A
>>> typedbuffer2(None)
>>> typedbuffer2(4)
Traceback (most recent call last):
...
TypeError: Argument 'obj' has incorrect type (expected memslice.IntMockBuffer, got int)
"""
pass
# #
# Test __cythonbufferdefaults__ # Test __cythonbufferdefaults__
# #
@testcase @testcase
def bufdefaults1(IntStridedMockBuffer[int, ndim=1] buf): def bufdefaults1(int[:] buf):
""" """
For IntStridedMockBuffer, mode should be For IntStridedMockBuffer, mode should be
"strided" by defaults which should show "strided" by defaults which should show
...@@ -972,13 +844,13 @@ def bufdefaults1(IntStridedMockBuffer[int, ndim=1] buf): ...@@ -972,13 +844,13 @@ def bufdefaults1(IntStridedMockBuffer[int, ndim=1] buf):
acquired A acquired A
released A released A
>>> [str(x) for x in A.recieved_flags] >>> [str(x) for x in A.recieved_flags]
['FORMAT', 'ND', 'STRIDES'] ['FORMAT', 'ND', 'STRIDES', 'WRITABLE']
""" """
pass pass
@testcase @testcase
def basic_struct(object[MyStruct] buf): def basic_struct(MyStruct[:] buf):
""" """
See also buffmt.pyx See also buffmt.pyx
...@@ -990,7 +862,7 @@ def basic_struct(object[MyStruct] buf): ...@@ -990,7 +862,7 @@ def basic_struct(object[MyStruct] buf):
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
@testcase @testcase
def nested_struct(object[NestedStruct] buf): def nested_struct(NestedStruct[:] buf):
""" """
See also buffmt.pyx See also buffmt.pyx
...@@ -1002,7 +874,7 @@ def nested_struct(object[NestedStruct] buf): ...@@ -1002,7 +874,7 @@ def nested_struct(object[NestedStruct] buf):
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
@testcase @testcase
def packed_struct(object[PackedStruct] buf): def packed_struct(PackedStruct[:] buf):
""" """
See also buffmt.pyx See also buffmt.pyx
...@@ -1017,7 +889,7 @@ def packed_struct(object[PackedStruct] buf): ...@@ -1017,7 +889,7 @@ def packed_struct(object[PackedStruct] buf):
print buf[0].a, buf[0].b print buf[0].a, buf[0].b
@testcase @testcase
def nested_packed_struct(object[NestedPackedStruct] buf): def nested_packed_struct(NestedPackedStruct[:] buf):
""" """
See also buffmt.pyx See also buffmt.pyx
...@@ -1030,25 +902,9 @@ def nested_packed_struct(object[NestedPackedStruct] buf): ...@@ -1030,25 +902,9 @@ def nested_packed_struct(object[NestedPackedStruct] buf):
""" """
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
cdef struct LongComplex:
long double real
long double imag
cdef class LongComplexMockBuffer(MockBuffer):
cdef int write(self, char* buf, object value) except -1:
cdef LongComplex* s
s = <LongComplex*>buf;
s.real, s.imag = value
return 0
cdef get_itemsize(self): return sizeof(LongComplex)
cdef get_default_format(self): return b"Zg"
#cdef extern from "complex.h":
# pass
@testcase @testcase
def complex_dtype(object[long double complex] buf): def complex_dtype(long double complex[:] buf):
""" """
>>> complex_dtype(LongComplexMockBuffer(None, [(0, -1)])) >>> complex_dtype(LongComplexMockBuffer(None, [(0, -1)]))
-1j -1j
...@@ -1056,7 +912,7 @@ def complex_dtype(object[long double complex] buf): ...@@ -1056,7 +912,7 @@ def complex_dtype(object[long double complex] buf):
print buf[0] print buf[0]
@testcase @testcase
def complex_inplace(object[long double complex] buf): def complex_inplace(long double complex[:] buf):
""" """
>>> complex_inplace(LongComplexMockBuffer(None, [(0, -1)])) >>> complex_inplace(LongComplexMockBuffer(None, [(0, -1)]))
(1+1j) (1+1j)
...@@ -1065,7 +921,7 @@ def complex_inplace(object[long double complex] buf): ...@@ -1065,7 +921,7 @@ def complex_inplace(object[long double complex] buf):
print buf[0] print buf[0]
@testcase @testcase
def complex_struct_dtype(object[LongComplex] buf): 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.
...@@ -1075,7 +931,7 @@ def complex_struct_dtype(object[LongComplex] buf): ...@@ -1075,7 +931,7 @@ def complex_struct_dtype(object[LongComplex] buf):
print buf[0].real, buf[0].imag print buf[0].real, buf[0].imag
@testcase @testcase
def complex_struct_inplace(object[LongComplex] buf): def complex_struct_inplace(LongComplex[:] buf):
""" """
>>> complex_struct_inplace(LongComplexMockBuffer(None, [(0, -1)])) >>> complex_struct_inplace(LongComplexMockBuffer(None, [(0, -1)]))
1.0 1.0 1.0 1.0
...@@ -1092,9 +948,85 @@ def complex_struct_inplace(object[LongComplex] buf): ...@@ -1092,9 +948,85 @@ def complex_struct_inplace(object[LongComplex] buf):
def buffer_nogil(): def buffer_nogil():
""" """
>>> buffer_nogil() >>> buffer_nogil()
10 (10, 10)
""" """
cdef int[:] buf = IntMockBuffer(None, [1,2,3]) cdef int[:] buf = IntMockBuffer(None, [1,2,3])
cdef int[:] buf2 = IntMockBuffer(None, [4,5,6])
with nogil: with nogil:
buf[1] = 10 buf[1] = 10
return buf[1] buf2 = buf
return buf[1], buf2[1]
#
### Test cdef functions
#
cdef cdef_function(int[:] buf1, object[::view.indirect, :] buf2 = ObjectMockBuffer(None,
[["spam"],["ham"],["eggs"]])):
print 'cdef called'
print buf1[6], buf2[1, 0]
buf2[1, 0] = "eggs"
def test_cdef_function(o1, o2=None):
"""
>>> A = IntMockBuffer("A", range(10))
>>> test_cdef_function(A)
acquired A
cdef called
6 ham
released A
acquired A
cdef called
6 eggs
released A
>>> B = ObjectMockBuffer("B", range(25), shape=(5, 5))
>>> test_cdef_function(A, B)
acquired A
acquired B
cdef called
6 eggs
released A
released B
acquired A
acquired B
cdef called
6 eggs
released A
released B
"""
cdef_function(o1)
cdef_function(o1)
if o2:
cdef_function(o1, o2)
cdef int[:] global_A = IntMockBuffer("A", range(10))
cdef object[::view.indirect, :] global_B = ObjectMockBuffer(
None, [["spam"],["ham"],["eggs"]])
cdef cdef_function2(int[:] buf1, object[::view.indirect, :] buf2 = global_B):
print 'cdef2 called'
print buf1[6], buf2[1, 0]
buf2[1, 0] = "eggs"
def test_cdef_function2():
"""
>>> test_cdef_function2()
cdef2 called
6 ham
eggs
cdef2 called
6 eggs
"""
cdef int[:] A = global_A
cdef object[::view.indirect, :] B = global_B
cdef_function2(A, B)
del A
del B
print global_B[1, 0]
cdef_function2(global_A, global_B)
...@@ -311,3 +311,16 @@ cdef class NestedPackedStructMockBuffer(MockBuffer): ...@@ -311,3 +311,16 @@ cdef class NestedPackedStructMockBuffer(MockBuffer):
cdef get_itemsize(self): return sizeof(NestedPackedStruct) cdef get_itemsize(self): return sizeof(NestedPackedStruct)
cdef get_default_format(self): return b"ci^ci@i" cdef get_default_format(self): return b"ci^ci@i"
cdef struct LongComplex:
long double real
long double imag
cdef class LongComplexMockBuffer(MockBuffer):
cdef int write(self, char* buf, object value) except -1:
cdef LongComplex* s
s = <LongComplex*>buf;
s.real, s.imag = value
return 0
cdef get_itemsize(self): return sizeof(LongComplex)
cdef get_default_format(self): return b"Zg"
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