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):
"Cannot convert '%s' to memoryviewslice" %
(src_type,))
elif not MemoryView.src_conforms_to_dst(src.type, dst_type):
error(self.pos, "Memoryview '%s' not conformable to memoryview '%s'." %
(src.type, dst_type))
if src.type.dtype.same_as(dst_type.dtype):
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:
if not src.type.is_pyobject:
......@@ -2412,13 +2418,13 @@ class IndexNode(ExprNode):
self.is_temp = True
if setting and self.base.type.is_memoryviewslice:
self.type.writable_needed = True
self.base.type.writable_needed = True
elif setting:
if not self.base.entry.type.writable:
error(self.pos, "Writing to readonly buffer")
else:
self.writable_needed = True
if self.type.is_buffer:
if self.base.type.is_buffer:
self.base.entry.buffer_aux.writable_needed = True
elif memoryviewslice_access:
......@@ -2570,7 +2576,7 @@ class IndexNode(ExprNode):
if self.is_buffer_access:
if code.globalstate.directives['nonecheck']:
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:
# is_temp is True, so must pull out value and incref it.
code.putln("%s = *%s;" % (self.result(), self.buffer_ptr_code))
......@@ -2657,11 +2663,14 @@ class IndexNode(ExprNode):
# Used from generate_assignment_code and InPlaceAssignmentNode
if code.globalstate.directives['nonecheck']:
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:
# Must manage refcounts. Decref what is already there
# 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()
code.putln("%s = %s;" % (ptr, ptrexpr))
code.put_gotref("*%s" % ptr)
......@@ -2735,7 +2744,9 @@ class IndexNode(ExprNode):
buffer_entry = MemoryView.MemoryViewSliceBufferEntry(entry)
negative_indices = Buffer.buffer_defaults['negative_indices']
return Buffer.put_buffer_lookup_code(entry=buffer_entry,
return buffer_entry, Buffer.put_buffer_lookup_code(
entry=buffer_entry,
index_signeds=[i.type.signed for i in self.indices],
index_cnames=index_temps,
directives=code.globalstate.directives,
......@@ -7681,6 +7692,13 @@ class CoerceToMemViewSliceNode(CoercionNode):
self.type.from_py_function,
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):
# Wrap a node in a C type cast.
......
......@@ -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."
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."
NOT_AMP_ERR = "Invalid operator, only an ampersand '&' is allowed."
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."
def concat_flags(*flags):
......@@ -24,9 +23,9 @@ def concat_flags(*flags):
format_flag = "PyBUF_FORMAT"
memview_c_contiguous = concat_flags(format_flag, "PyBUF_C_CONTIGUOUS")
memview_f_contiguous = concat_flags(format_flag, "PyBUF_F_CONTIGUOUS")
memview_any_contiguous = concat_flags(format_flag, "PyBUF_ANY_CONTIGUOUS")
memview_c_contiguous = "(PyBUF_C_CONTIGUOUS | PyBUF_FORMAT | PyBUF_WRITABLE)"
memview_f_contiguous = "(PyBUF_F_CONTIGUOUS | PyBUF_FORMAT | PyBUF_WRITABLE)"
memview_any_contiguous = "(PyBUF_ANY_CONTIGUOUS | PyBUF_FORMAT | PyBUF_WRITABLE)"
memview_full_access = "PyBUF_FULL"
#memview_strided_access = "PyBUF_STRIDED"
memview_strided_access = "PyBUF_RECORDS"
......@@ -72,8 +71,8 @@ def mangle_dtype_name(dtype):
import Buffer
return Buffer.mangle_dtype_name(dtype)
def axes_to_str(axes):
return "".join([access[0].upper()+packing[0] for (access, packing) in axes])
#def axes_to_str(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,
incref_rhs=True, have_gil=False):
......@@ -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.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)
if is_c_contig:
......@@ -196,50 +195,65 @@ class MemoryViewSliceBufferEntry(Buffer.BufferEntry):
suboffset = "%s.suboffsets[%d]" % (self.cname, 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)
bufp = ('__pyx_memviewslice_index_full(%s, %s, %s, %s)' %
(bufp, index, stride, suboffset))
elif access == 'full' and packing == 'contig':
elif flag == "generic_contiguous":
# We can skip stride multiplication with the cast
code.globalstate.use_utility_code(memviewslice_index_helpers)
bufp = '((char *) ((%s *) %s) + %s)' % (type_decl, bufp, index)
bufp = ('__pyx_memviewslice_index_full_contig(%s, %s)' %
(bufp, suboffset))
elif access == 'ptr' and packing in ('strided', 'follow'):
elif flag == "indirect":
bufp = ("(*((char **) %s + %s * %s) + %s)" %
(bufp, index, stride, suboffset))
elif access == 'ptr' and packing == 'contig':
elif flag == "indirect_contiguous":
bufp = "(*((char **) %s) + %s)" % (bufp, suboffset)
elif access == 'direct' and packing in ('strided', 'follow'):
elif flag == "strided":
bufp = "(%s + %s * %s)" % (bufp, index, stride)
else:
assert (access, packing) == ('direct', 'contig'), (access, packing)
assert flag == 'contiguous', flag
bufp = '((char *) (((%s *) %s) + %s))' % (type_decl, bufp, index)
bufp = '( /* dim=%d */ %s )' % (dim, 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):
base = "__Pyx_BufferNew_%s_From_%s_%s"
base = "__Pyx_BufferNew_%s_From_%s"
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:
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):
dtype = from_mvs.dtype
assert dtype == to_mvs.dtype
return ('__Pyx_BufferCopyContents_%s_%s_%s' %
(axes_to_str(from_mvs.axes),
axes_to_str(to_mvs.axes),
mangle_dtype_name(dtype)))
assert from_mvs.dtype == to_mvs.dtype
return '__Pyx_BufferCopyContents_%s_to_%s' % (from_mvs.specialization_suffix(),
to_mvs.specialization_suffix())
class IsContigFuncUtilCode(object):
......
......@@ -1428,16 +1428,20 @@ class FuncDefNode(StatNode, BlockNode):
self.generate_argument_parsing_code(env, code)
# If an argument is assigned to in the body, we must
# incref it to properly keep track of refcounts.
is_cdef = isinstance(self, CFuncDefNode)
for entry in lenv.arg_entries:
if entry.type.is_pyobject:
if (acquire_gil or entry.assignments) and not entry.in_closure:
code.put_var_incref(entry)
# Note: all memoryviewslices are already newly acquired references
# or increffed defaults!
#if entry.type.is_memoryviewslice:
# code.put_incref_memoryviewslice(entry.cname,
# have_gil=not lenv.nogil)
#code.put_incref("%s.memview" % entry.cname, cython_memoryview_ptr_type)
# Note: defaults are always increffed. For def functions, we
# we aquire arguments from object converstion, so we have
# new references. If we are a cdef function, we need to
# incref our arguments
if is_cdef and entry.type.is_memoryviewslice:
code.put_incref_memoryviewslice(entry.cname,
have_gil=not lenv.nogil)
# ----- Initialise local buffer auxiliary variables
for entry in lenv.var_entries + lenv.arg_entries:
if entry.type.is_buffer and entry.buffer_aux.buflocal_nd_var.used:
......@@ -1567,7 +1571,7 @@ class FuncDefNode(StatNode, BlockNode):
if entry.type.is_pyobject:
if (acquire_gil or entry.assignments) and not entry.in_closure:
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_decref("%s.memview" % entry.cname, cython_memoryview_ptr_type)
if self.needs_closure:
......
......@@ -338,9 +338,7 @@ class MemoryViewSliceType(PyrexType):
to_py_function = None
exception_value = None
exception_check = None
utility_counter = 0
exception_check = True
def __init__(self, base_dtype, axes):
'''
......@@ -381,7 +379,7 @@ class MemoryViewSliceType(PyrexType):
self.dtype = base_dtype
self.axes = 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)
assert not (self.is_c_contig and self.is_f_contig)
......@@ -416,7 +414,6 @@ class MemoryViewSliceType(PyrexType):
if self.scope is None:
import Symtab, MemoryView, Options
from MemoryView import axes_to_str
self.scope = scope = Symtab.CClassScope(
'mvs_class_'+self.specialization_suffix(),
......@@ -504,12 +501,12 @@ class MemoryViewSliceType(PyrexType):
def specialization_suffix(self):
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):
code.putln("%s.data = 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 global_init_code(self, entry, code):
# code.putln("%s.data = NULL;" % entry.cname)
# code.putln("%s.memview = NULL;" % entry.cname)
def check_for_null_code(self, cname):
return cname + '.memview'
......@@ -535,15 +532,14 @@ class MemoryViewSliceType(PyrexType):
else:
c_or_f_flag = "0"
# specializing through UtilityCode.specialize is not so useful as
# specialize on too many things to include in the function name
funcname = "__Pyx_PyObject_to_MemoryviewSlice_%d" % self.utility_counter
suffix = self.specialization_suffix()
funcname = "__Pyx_PyObject_to_MemoryviewSlice_" + suffix
context = dict(
MemoryView.context,
buf_flag = self.flags,
ndim = self.ndim,
axes_specs = ', '.join(self.axes_specs_to_code()),
axes_specs = ', '.join(self.axes_to_code()),
dtype_typedecl = self.dtype.declaration_code(""),
struct_nesting_depth = self.dtype.struct_nesting_depth(),
c_or_f_flag = c_or_f_flag,
......@@ -551,8 +547,6 @@ class MemoryViewSliceType(PyrexType):
)
self.from_py_function = funcname
MemoryViewSliceType.utility_counter += 1
return True
def create_to_py_utility_code(self, env):
......@@ -562,13 +556,13 @@ class MemoryViewSliceType(PyrexType):
return "__pyx_memoryview_fromslice(&%s, %s.memview->obj, %s, %s);" % (
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"
import MemoryView
d = MemoryView._spec_to_const
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"
import MemoryView
d = MemoryView._spec_to_abbrev
......@@ -577,6 +571,24 @@ class MemoryViewSliceType(PyrexType):
def error_condition(self, 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):
#
......@@ -632,6 +644,9 @@ class PyObjectType(PyrexType):
def __repr__(self):
return "<PyObjectType>"
def __eq__(self, other):
return isinstance(other, PyObjectType) and self.name == other.name
def can_coerce_to_pyobject(self, env):
return True
......
......@@ -258,6 +258,9 @@ cdef class memoryview(object):
bytesitem = itemp[:self.view.itemsize]
return struct.unpack(fmt, bytesitem)
def __repr__(self):
return "<MemoryView of %s at 0x%x>" % (self.obj.__class__.__name__, id(self))
def __str__(self):
return "<MemoryView of %r at 0x%x>" % (self.obj, id(self))
......@@ -283,8 +286,8 @@ cdef memoryview_cwrapper(object o, int flags):
return memoryview(o, flags)
@cname('__pyx_memoryview_fromslice')
cdef memoryview memoryview_from_memview_cwrapper(
{{memviewslice_name}} *memviewslice, object orig_obj, int flags, int new_ndim):
cdef memoryview_from_memview_cwrapper({{memviewslice_name}} *memviewslice,
object orig_obj, int flags, int new_ndim):
cdef _memoryviewslice result = _memoryviewslice(orig_obj, flags)
result.from_slice = memviewslice[0]
......
# mode: error
cimport cython
from cython.view cimport contig as foo, full as bar, follow
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] both2
......@@ -17,14 +17,15 @@ cdef long long[01::1, 0x01:, '0' :, False:] fort_contig0
cdef signed char[1::] bad_start
cdef unsigned long[:,:1] bad_stop
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[::blargh] bad_name
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'''
11:25: Cannot specify an array that is both C and Fortran contiguous.
......@@ -36,12 +37,9 @@ _ERRORS = u'''
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.
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.
21:28: Invalid axis specification for a C/Fortran contiguous array.
22:31: Invalid operator, only an ampersand '&' is allowed.
23:28: Invalid axis specification.
24:22: Invalid axis specification.
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.
20:22: Invalid axis specification.
21:25: Invalid axis specification.
22:22: no expressions allowed in axis spec, only names and literals.
25:51: Memoryview 'object[::contiguous, :]' not conformable to memoryview 'object[:, ::contiguous]'.
28:36: Different base types for memoryviews (int, Python object)
'''
......@@ -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
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
def complex_dtype(object[long double complex] buf):
......
# Tests the buffer access syntax functionality by constructing
# 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.
#
# Note: see also bufaccess.pyx
from __future__ import unicode_literals
......@@ -417,25 +410,6 @@ def list_comprehension(int[:] buf, len):
cdef int i
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
@cython.wraparound(False)
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.
#
# @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
def writable(obj):
......@@ -510,7 +471,7 @@ def c_contig(int[::1] buf):
>>> c_contig(A)
2
>>> [str(x) for x in A.recieved_flags]
['FORMAT', 'ND', 'STRIDES', 'C_CONTIGUOUS']
['FORMAT', 'ND', 'STRIDES', 'C_CONTIGUOUS', 'WRITABLE']
"""
return buf[2]
......@@ -523,7 +484,7 @@ def c_contig_2d(int[:, ::1] buf):
>>> c_contig_2d(A)
7
>>> [str(x) for x in A.recieved_flags]
['FORMAT', 'ND', 'STRIDES', 'C_CONTIGUOUS']
['FORMAT', 'ND', 'STRIDES', 'C_CONTIGUOUS', 'WRITABLE']
"""
return buf[1, 3]
......@@ -534,12 +495,12 @@ def f_contig(int[::1, :] buf):
>>> f_contig(A)
2
>>> [str(x) for x in A.recieved_flags]
['FORMAT', 'ND', 'STRIDES', 'F_CONTIGUOUS']
['FORMAT', 'ND', 'STRIDES', 'F_CONTIGUOUS', 'WRITABLE']
"""
return buf[0, 1]
@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.
......@@ -547,7 +508,7 @@ def f_contig_2d(object[int, ndim=2, mode='fortran'] buf):
>>> f_contig_2d(A)
7
>>> [str(x) for x in A.recieved_flags]
['FORMAT', 'ND', 'STRIDES', 'F_CONTIGUOUS']
['FORMAT', 'ND', 'STRIDES', 'F_CONTIGUOUS', 'WRITABLE']
"""
return buf[3, 1]
......@@ -600,18 +561,6 @@ def unsafe_get(int[:] buf, int 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
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]
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
# all works.
......@@ -759,7 +689,7 @@ def printbuf_td_cy_int(td_cy_int[:] buf, shape):
print 'END'
@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,))
0 1 2 END
......@@ -774,7 +704,7 @@ def printbuf_td_h_short(object[td_h_short] buf, shape):
print 'END'
@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,))
0 1 2 END
......@@ -789,7 +719,7 @@ def printbuf_td_h_cy_short(object[td_h_cy_short] buf, shape):
print 'END'
@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,))
0 1 2 END
......@@ -804,7 +734,7 @@ def printbuf_td_h_ushort(object[td_h_ushort] buf, shape):
print 'END'
@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,))
0.25 1.0 3.125 END
......@@ -831,7 +761,7 @@ def get_refcount(x):
return (<PyObject*>x).ob_refcnt
@testcase
def printbuf_object(object[object] buf, shape):
def printbuf_object(object[:] buf, shape):
"""
Only play with unique objects, interned numbers etc. will have
unpredictable refcounts.
......@@ -854,7 +784,7 @@ def printbuf_object(object[object] buf, shape):
print repr(buf[i]), (<PyObject*>buf[i]).ob_refcnt
@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.
......@@ -873,7 +803,7 @@ def assign_to_object(object[object] buf, int idx, obj):
buf[idx] = obj
@testcase
def assign_temporary_to_object(object[object] buf):
def assign_temporary_to_object(object[:] buf):
"""
See comments on printbuf_object above.
......@@ -899,69 +829,11 @@ def assign_temporary_to_object(object[object] buf):
"""
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__
#
@testcase
def bufdefaults1(IntStridedMockBuffer[int, ndim=1] buf):
def bufdefaults1(int[:] buf):
"""
For IntStridedMockBuffer, mode should be
"strided" by defaults which should show
......@@ -972,13 +844,13 @@ def bufdefaults1(IntStridedMockBuffer[int, ndim=1] buf):
acquired A
released A
>>> [str(x) for x in A.recieved_flags]
['FORMAT', 'ND', 'STRIDES']
['FORMAT', 'ND', 'STRIDES', 'WRITABLE']
"""
pass
@testcase
def basic_struct(object[MyStruct] buf):
def basic_struct(MyStruct[:] buf):
"""
See also buffmt.pyx
......@@ -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
@testcase
def nested_struct(object[NestedStruct] buf):
def nested_struct(NestedStruct[:] buf):
"""
See also buffmt.pyx
......@@ -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
@testcase
def packed_struct(object[PackedStruct] buf):
def packed_struct(PackedStruct[:] buf):
"""
See also buffmt.pyx
......@@ -1017,7 +889,7 @@ def packed_struct(object[PackedStruct] buf):
print buf[0].a, buf[0].b
@testcase
def nested_packed_struct(object[NestedPackedStruct] buf):
def nested_packed_struct(NestedPackedStruct[:] buf):
"""
See also buffmt.pyx
......@@ -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
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
def complex_dtype(object[long double complex] buf):
def complex_dtype(long double complex[:] buf):
"""
>>> complex_dtype(LongComplexMockBuffer(None, [(0, -1)]))
-1j
......@@ -1056,7 +912,7 @@ def complex_dtype(object[long double complex] buf):
print buf[0]
@testcase
def complex_inplace(object[long double complex] buf):
def complex_inplace(long double complex[:] buf):
"""
>>> complex_inplace(LongComplexMockBuffer(None, [(0, -1)]))
(1+1j)
......@@ -1065,7 +921,7 @@ def complex_inplace(object[long double complex] buf):
print buf[0]
@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
is accessed.
......@@ -1075,7 +931,7 @@ def complex_struct_dtype(object[LongComplex] buf):
print buf[0].real, buf[0].imag
@testcase
def complex_struct_inplace(object[LongComplex] buf):
def complex_struct_inplace(LongComplex[:] buf):
"""
>>> complex_struct_inplace(LongComplexMockBuffer(None, [(0, -1)]))
1.0 1.0
......@@ -1092,9 +948,85 @@ def complex_struct_inplace(object[LongComplex] buf):
def buffer_nogil():
"""
>>> buffer_nogil()
10
(10, 10)
"""
cdef int[:] buf = IntMockBuffer(None, [1,2,3])
cdef int[:] buf2 = IntMockBuffer(None, [4,5,6])
with nogil:
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):
cdef get_itemsize(self): return sizeof(NestedPackedStruct)
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