Commit b7e14b9a authored by Mark Florisson's avatar Mark Florisson

cimport cython in CyUtility, more tests, nogil slicing

parent 8105941c
...@@ -13,6 +13,8 @@ cdef class UtilityCode(UtilityCodeBase): ...@@ -13,6 +13,8 @@ cdef class UtilityCode(UtilityCodeBase):
cdef public dict _cache cdef public dict _cache
cdef public list specialize_list cdef public list specialize_list
cdef public object proto_block cdef public object proto_block
cdef public object name
cdef public object file
cpdef put_code(self, output) cpdef put_code(self, output)
......
...@@ -75,10 +75,13 @@ class CythonScope(ModuleScope): ...@@ -75,10 +75,13 @@ class CythonScope(ModuleScope):
Creates some entries for testing purposes and entries for Creates some entries for testing purposes and entries for
cython.array() and for cython.view.*. cython.array() and for cython.view.*.
""" """
cython_testscope_utility_code.declare_in_scope(self) cython_testscope_utility_code.declare_in_scope(
cython_test_extclass_utility_code.declare_in_scope(self) self, cython_scope=self)
cython_test_extclass_utility_code.declare_in_scope(
self, cython_scope=self)
MemoryView.cython_array_utility_code.declare_in_scope(self) MemoryView.cython_array_utility_code.declare_in_scope(
self, cython_scope=self)
# #
# The view sub-scope # The view sub-scope
...@@ -88,9 +91,11 @@ class CythonScope(ModuleScope): ...@@ -88,9 +91,11 @@ class CythonScope(ModuleScope):
viewscope.is_cython_builtin = True viewscope.is_cython_builtin = True
viewscope.pxd_file_loaded = True viewscope.pxd_file_loaded = True
cythonview_testscope_utility_code.declare_in_scope(viewscope) cythonview_testscope_utility_code.declare_in_scope(
viewscope, cython_scope=self)
view_utility_scope = MemoryView.view_utility_code.declare_in_scope(viewscope) view_utility_scope = MemoryView.view_utility_code.declare_in_scope(
viewscope, cython_scope=self)
# MemoryView.memview_fromslice_utility_code.from_scope = view_utility_scope # MemoryView.memview_fromslice_utility_code.from_scope = view_utility_scope
# MemoryView.memview_fromslice_utility_code.declare_in_scope(viewscope) # MemoryView.memview_fromslice_utility_code.declare_in_scope(viewscope)
......
...@@ -2432,7 +2432,8 @@ class IndexNode(ExprNode): ...@@ -2432,7 +2432,8 @@ class IndexNode(ExprNode):
import MemoryView import MemoryView
skip_child_analysis = True skip_child_analysis = True
indices = MemoryView.unellipsify(indices, self.base.type.ndim) have_slices, indices = MemoryView.unellipsify(indices,
self.base.type.ndim)
self.memslice_index = len(indices) == self.base.type.ndim self.memslice_index = len(indices) == self.base.type.ndim
axes = [] axes = []
...@@ -2462,24 +2463,19 @@ class IndexNode(ExprNode): ...@@ -2462,24 +2463,19 @@ class IndexNode(ExprNode):
value = getattr(index, attr) value = getattr(index, attr)
if not value.is_none: if not value.is_none:
value = value.coerce_to(index_type, env) value = value.coerce_to(index_type, env)
value = value.coerce_to_temp(env) #value = value.coerce_to_temp(env)
setattr(index, attr, value) setattr(index, attr, value)
new_indices.append(value) new_indices.append(value)
elif index.type.is_int: elif index.type.is_int:
self.memslice_index = True self.memslice_index = True
index = index.coerce_to(index_type, env).coerce_to_temp( index = index.coerce_to(index_type, env)\
index_type) #.coerce_to_temp(
# index_type)
indices[i] = index indices[i] = index
new_indices.append(index) new_indices.append(index)
if access in ('ptr', 'generic') and i != 0: if access in ('ptr', 'generic') and i != 0 and have_slices:
# If this dimension is to disappear, then how do we
# indicate that we need to dereference in this dimension
# if the previous dimension is already indirect, or if
# the previous dimension was direct but also indexed?
# Basically only a[i, j, k, :] can work, as you can
# set the base pointer to start in the fourth dimension
self.type = error_type self.type = error_type
return error(index.pos, return error(index.pos,
"Indexing of non-leading indirect or generic " "Indexing of non-leading indirect or generic "
...@@ -2494,6 +2490,8 @@ class IndexNode(ExprNode): ...@@ -2494,6 +2490,8 @@ class IndexNode(ExprNode):
self.original_indices = indices self.original_indices = indices
self.indices = new_indices self.indices = new_indices
self.env = env
elif self.base.type.is_buffer: elif self.base.type.is_buffer:
# Buffer indexing # Buffer indexing
if len(indices) == self.base.type.ndim: if len(indices) == self.base.type.ndim:
...@@ -2893,7 +2891,8 @@ class IndexNode(ExprNode): ...@@ -2893,7 +2891,8 @@ class IndexNode(ExprNode):
self.original_indices, self.original_indices,
self.base.type, self.base.type,
self.type, self.type,
self.result()) self.result(),
have_gil = not self.env.nogil)
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)
...@@ -4092,8 +4091,9 @@ class AttributeNode(ExprNode): ...@@ -4092,8 +4091,9 @@ class AttributeNode(ExprNode):
def nogil_check(self, env): def nogil_check(self, env):
if self.is_py_attr: if self.is_py_attr:
self.gil_error() self.gil_error()
import MemoryView elif self.type.is_memoryviewslice:
MemoryView.err_if_nogil_initialized_check(self.pos, env, 'attribute') import MemoryView
MemoryView.err_if_nogil_initialized_check(self.pos, env, 'attribute')
gil_message = "Accessing Python attribute" gil_message = "Accessing Python attribute"
......
...@@ -228,189 +228,72 @@ class MemoryViewSliceBufferEntry(Buffer.BufferEntry): ...@@ -228,189 +228,72 @@ class MemoryViewSliceBufferEntry(Buffer.BufferEntry):
return bufp return bufp
def generate_buffer_slice_code(self, code, indices, type, dst_type, dst): def generate_buffer_slice_code(self, code, indices, type, dst_type, dst, have_gil):
""" slicefunc = "__pyx_memoryview_slice_memviewslice"
Generate code for a new memoryview slice. new_ndim = 0
cname = self.cname
indices An index in the indices list is either a SliceNode where
start/stop and step are converted to temps of type Py_ssize_t, or a suboffset_dim = code.funcstate.allocate_temp(PyrexTypes.c_int_type,
temp integer index of type Py_ssize_t. False)
type is the type that is sliced index_code = ("%(slicefunc)s(&%(cname)s, &%(dst)s, %(have_gil)d, "
dst_type is the resulting type "%(dim)d, %(new_ndim)d, &%(suboffset_dim)s, "
dst is the result memoryview slice temp "%(idx)s, 0, 0, 0, 0, 0, 0)")
For every dimension we do the following: slice_code = ("%(slicefunc)s(&%(cname)s, &%(dst)s, %(have_gil)d, "
"/* dim */ %(dim)d, "
ensure 0 <= start < n and 0 <= stop <= n "/* new_ndim */ %(new_ndim)d, "
"/* suboffset_dim */ &%(suboffset_dim)s, "
if every dimension is direct: "/* start */ %(start)s, "
keep track of possible offsets, set data * to "/* stop */ %(stop)s, "
mslice[0, 0, 0, ...] after the loop "/* step */ %(step)s, "
else: "/* have_start */ %(have_start)d, "
keep a variable 'dim' that "/* have_stop */ %(have_stop)d, "
if < 0 "/* have_step */ %(have_step)d, "
indicates the data * "/* is_slice */ 1)")
else
indicates suboffsets[dim] def generate_slice_call(expr):
pos = index.pos
now set shape, strides and suboffsets according to start/stop/step
if have_gil:
add the slice or indexing offset to data * or to suboffsets[dim] code.putln(code.error_goto_if(expr, pos))
update the dim variable if necessary
"""
axes = []
temps = []
dtype = PyrexTypes.c_py_ssize_t_type
def put_bounds_code(r, start):
"ensure that start 0 <= r < n"
code.putln("if (%s < 0) {" % r)
code.putln( "%s += %s;" % (r, shape))
code.putln( "if (%s < 0) %s = 0;" % (r, r))
if start:
code.putln("} else if (%s >= %s) %s = %s - 1;" % (r, shape, r, shape))
else: else:
code.putln("} else if (%s > %s) %s = %s;" % (r, shape, r, shape)) code.putln("{")
code.putln( "const char *__pyx_t_result = %s;" % expr)
def update_dim():
"if we are indirect, update our dim index"
if access in ('ptr', 'generic'):
if access == 'generic':
code.put("if (%s >= 0)" % suboffset)
code.putln("%s = %d;" % (offset_dim, dst_dim))
all_direct = True
for access, packing in type.axes:
all_direct = all_direct and access == 'direct'
if not all_direct:
break
if not all_direct:
offset_dim = code.funcstate.allocate_temp(PyrexTypes.c_int_type,
False)
code.putln("%s = -1;" % (offset_dim,))
code.putln("%s.data = %s.data;" % (dst, self.cname))
dst_dim = 0
for dim, index in enumerate(indices):
goto_err = code.error_goto(index.pos)
shape = "%s.shape[%d]" % (self.cname, dim)
stride = "%s.strides[%d]" % (self.cname, dim)
suboffset = "%s.suboffsets[%d]" % (self.cname, dim)
dst_shape = "%s.shape[%d]" % (dst, dst_dim)
dst_stride = "%s.strides[%d]" % (dst, dst_dim)
dst_suboffset = "%s.suboffsets[%d]" % (dst, dst_dim)
access, packing = type.axes[dim]
if isinstance(index, ExprNodes.SliceNode): code.putln( "if (unlikely(__pyx_t_result)) {")
# slice or part of ellipsis code.put_ensure_gil()
code.putln( "PyErr_Format(PyExc_IndexError, "
"__pyx_t_result, %d)" % dim)
code.put_release_ensured_gil()
code.putln(code.goto_error(pos))
code.putln( "}")
start = stop = step = None
dst_dim += 1
# First fix the bounds
if not index.start.is_none:
start = index.start.result()
put_bounds_code(start, start=True)
if not index.stop.is_none:
stop = index.stop.result()
put_bounds_code(stop, start=False)
# Compute the new strides
if not index.step.is_none:
step = index.step.result()
code.putln("%s = %s * %s;" % (dst_stride, stride, step))
else:
code.putln("%s = %s;" % (dst_stride, stride))
# Take care of suboffsets
code.putln("%s = %s;" % (dst_suboffset, suboffset))
# If start or stop is not specified, then we need to set
# them according to step
if not start or not stop:
if step:
code.putln("if (%s > 0) {" % step)
if not start:
start = code.funcstate.allocate_temp(dtype, False)
temps.append(start)
code.putln("%s = 0;" % start)
if not stop:
stop = code.funcstate.allocate_temp(dtype, False)
temps.append(stop)
code.putln("%s = %s;" % (stop, shape))
code.putln("} else {")
if start:
code.putln("%s = %s - 1;" % (start, shape))
if stop:
code.putln("%s = -1;" % stop)
code.putln("}")
else:
if not start:
start = "0"
if not stop:
stop = shape
d = dict(locals(), step=step or "1")
# Take care of shape
code.putln("%(dst_shape)s = (%(stop)s - %(start)s) / %(step)s;" % d)
code.putln("if (%(dst_shape)s && (%(stop)s - %(start)s) %% %(step)s) %(dst_shape)s++;" % d)
code.putln("if (%(dst_shape)s < 0) %(dst_shape)s = 0;" % d)
# Take care of slicing offsets
if start != "0":
if all_direct:
axes.append([dim, start, access, packing])
else:
offset = "%s * %s" % (start, stride)
d = dict(dim=offset_dim, dst=dst, offset=offset)
code.putln("if (%(dim)s < 0) %(dst)s.data += %(offset)s;" % d)
code.putln("else %(dst)s.suboffsets[%(dim)s] += %(offset)s;" % d)
else:
# integer index (or object converted to integer index)
# Note: this dimension disappears
assert access == 'direct'
r = index.result()
# Bounds checking with error
code.globalstate.use_utility_code(Buffer.raise_indexerror_code)
out_of_bounds = "%s < 0 || %s >= %s" % (r, r, shape)
code.putln("if (%s) {" % code.unlikely(out_of_bounds))
code.putln('__Pyx_RaiseBufferIndexError(%d);' % dim)
code.putln(code.error_goto(index.pos))
code.putln("}") code.putln("}")
if all_direct: code.putln("%s = -1;" % suboffset_dim)
axes.append([dim, index.result(), access, packing]) code.putln("%(dst)s.data = %(cname)s.data;" % locals())
else: code.putln("%(dst)s.memview = %(cname)s.memview;" % locals())
add_slice_offset(r)
# Take care of data * for direct access in all dimensions for dim, index in enumerate(indices):
if all_direct: if not isinstance(index, ExprNodes.SliceNode):
bufp = self._generate_buffer_lookup_code(code, axes, idx = index.result()
cast_result=False) generate_slice_call(index_code % locals())
code.putln("%s.data = %s;" % (dst, bufp)) else:
d = {}
for s in "start stop step".split():
idx = getattr(index, s)
have_idx = d['have_' + s] = not idx.is_none
if have_idx:
d[s] = idx.result()
else:
d[s] = "0"
# Finally take care of memview d.update(locals())
code.putln("%s.memview = %s.memview;" % (dst, self.cname)) generate_slice_call(slice_code % d)
new_ndim += 1
for temp in temps: code.funcstate.release_temp(suboffset_dim)
code.funcstate.release_temp(temp)
def empty_slice(pos): def empty_slice(pos):
none = ExprNodes.NoneNode(pos) none = ExprNodes.NoneNode(pos)
...@@ -420,10 +303,13 @@ def empty_slice(pos): ...@@ -420,10 +303,13 @@ def empty_slice(pos):
def unellipsify(indices, ndim): def unellipsify(indices, ndim):
result = [] result = []
seen_ellipsis = False seen_ellipsis = False
have_slices = False
for index in indices: for index in indices:
if isinstance(index, ExprNodes.EllipsisNode): if isinstance(index, ExprNodes.EllipsisNode):
have_slices = True
full_slice = empty_slice(index.pos) full_slice = empty_slice(index.pos)
if seen_ellipsis: if seen_ellipsis:
result.append(full_slice) result.append(full_slice)
else: else:
...@@ -431,13 +317,15 @@ def unellipsify(indices, ndim): ...@@ -431,13 +317,15 @@ def unellipsify(indices, ndim):
result.extend([full_slice] * nslices) result.extend([full_slice] * nslices)
seen_ellipsis = True seen_ellipsis = True
else: else:
have_slices = have_slices or isinstance(index, ExprNodes.SliceNode)
result.append(index) result.append(index)
if len(result) < ndim: if len(result) < ndim:
have_slices = True
nslices = ndim - len(result) nslices = ndim - len(result)
result.extend([empty_slice(indices[-1].pos)] * nslices) result.extend([empty_slice(indices[-1].pos)] * nslices)
return result return have_slices, result
def get_memoryview_flag(access, packing): def get_memoryview_flag(access, packing):
if access == 'full' and packing in ('strided', 'follow'): if access == 'full' and packing in ('strided', 'follow'):
...@@ -885,10 +773,6 @@ view_constant_to_access_packing = { ...@@ -885,10 +773,6 @@ view_constant_to_access_packing = {
'indirect_contiguous': ('ptr', 'contig'), 'indirect_contiguous': ('ptr', 'contig'),
} }
def get_access_packing(view_scope_constant):
if view_scope_constant.name == 'generic':
return 'full',
def validate_axes_specs(positions, specs): def validate_axes_specs(positions, specs):
packing_specs = ('contig', 'strided', 'follow') packing_specs = ('contig', 'strided', 'follow')
......
...@@ -9,6 +9,7 @@ class NonManglingModuleScope(Symtab.ModuleScope): ...@@ -9,6 +9,7 @@ class NonManglingModuleScope(Symtab.ModuleScope):
def __init__(self, prefix, *args, **kw): def __init__(self, prefix, *args, **kw):
self.prefix = prefix self.prefix = prefix
self.cython_scope = None
Symtab.ModuleScope.__init__(self, *args, **kw) Symtab.ModuleScope.__init__(self, *args, **kw)
def add_imported_entry(self, name, entry, pos): def add_imported_entry(self, name, entry, pos):
...@@ -31,9 +32,12 @@ class CythonUtilityCodeContext(StringParseContext): ...@@ -31,9 +32,12 @@ class CythonUtilityCodeContext(StringParseContext):
def find_module(self, module_name, relative_to = None, pos = None, def find_module(self, module_name, relative_to = None, pos = None,
need_pxd = 1): need_pxd = 1):
if module_name != self.module_name: if module_name != self.module_name:
raise AssertionError("Not yet supporting any cimports/includes " if module_name not in self.modules:
"from string code snippets") raise AssertionError("Only the cython cimport is supported.")
else:
return self.modules[module_name]
if self.scope is None: if self.scope is None:
self.scope = NonManglingModuleScope(self.prefix, self.scope = NonManglingModuleScope(self.prefix,
...@@ -78,7 +82,7 @@ class CythonUtilityCode(Code.UtilityCodeBase): ...@@ -78,7 +82,7 @@ class CythonUtilityCode(Code.UtilityCodeBase):
self.requires = requires or [] self.requires = requires or []
self.from_scope = from_scope self.from_scope = from_scope
def get_tree(self, entries_only=False): def get_tree(self, entries_only=False, cython_scope=None):
from AnalysedTreeTransforms import AutoTestDictTransform from AnalysedTreeTransforms import AutoTestDictTransform
# The AutoTestDictTransform creates the statement "__test__ = {}", # The AutoTestDictTransform creates the statement "__test__ = {}",
# which when copied into the main ModuleNode overwrites # which when copied into the main ModuleNode overwrites
...@@ -88,6 +92,7 @@ class CythonUtilityCode(Code.UtilityCodeBase): ...@@ -88,6 +92,7 @@ class CythonUtilityCode(Code.UtilityCodeBase):
import Pipeline, ParseTreeTransforms import Pipeline, ParseTreeTransforms
context = CythonUtilityCodeContext(self.name) context = CythonUtilityCodeContext(self.name)
context.prefix = self.prefix context.prefix = self.prefix
context.cython_scope = cython_scope
#context = StringParseContext(self.name) #context = StringParseContext(self.name)
tree = parse_from_strings(self.name, self.impl, context=context, tree = parse_from_strings(self.name, self.impl, context=context,
allow_struct_enum_decorator=True) allow_struct_enum_decorator=True)
...@@ -125,13 +130,13 @@ class CythonUtilityCode(Code.UtilityCodeBase): ...@@ -125,13 +130,13 @@ class CythonUtilityCode(Code.UtilityCodeBase):
def put_code(self, output): def put_code(self, output):
pass pass
def declare_in_scope(self, dest_scope, used=False): def declare_in_scope(self, dest_scope, used=False, cython_scope=None):
""" """
Declare all entries from the utility code in dest_scope. Code will only Declare all entries from the utility code in dest_scope. Code will only
be included for used entries. If module_name is given, declare the be included for used entries. If module_name is given, declare the
type entries with that name. type entries with that name.
""" """
tree = self.get_tree(entries_only=True) tree = self.get_tree(entries_only=True, cython_scope=cython_scope)
entries = tree.scope.entries entries = tree.scope.entries
entries.pop('__name__') entries.pop('__name__')
......
...@@ -165,6 +165,8 @@ cdef array array_cwrapper(tuple shape, Py_ssize_t itemsize, char *format, char * ...@@ -165,6 +165,8 @@ cdef array array_cwrapper(tuple shape, Py_ssize_t itemsize, char *format, char *
########## View.MemoryView ########## ########## View.MemoryView ##########
import cython
# from cpython cimport ... # from cpython cimport ...
cdef extern from "Python.h": cdef extern from "Python.h":
int PyIndex_Check(object) int PyIndex_Check(object)
...@@ -245,7 +247,6 @@ cdef class memoryview(object): ...@@ -245,7 +247,6 @@ cdef class memoryview(object):
cdef char *itemp = <char *> self.view.buf cdef char *itemp = <char *> self.view.buf
for dim, idx in enumerate(index): for dim, idx in enumerate(index):
_check_index(idx)
itemp = pybuffer_index(&self.view, itemp, idx, dim) itemp = pybuffer_index(&self.view, itemp, idx, dim)
return itemp return itemp
...@@ -255,13 +256,13 @@ cdef class memoryview(object): ...@@ -255,13 +256,13 @@ cdef class memoryview(object):
if index is Ellipsis: if index is Ellipsis:
return self return self
have_slices, index = _unellipsify(index, self.view.ndim) have_slices, indices = _unellipsify(index, self.view.ndim)
cdef char *itemp cdef char *itemp
if have_slices: if have_slices:
return pybuffer_slice(self, index) return memview_slice(self, indices)
else: else:
itemp = self.get_item_pointer(index) itemp = self.get_item_pointer(indices)
return self.convert_item_to_object(itemp) return self.convert_item_to_object(itemp)
@cname('__pyx_memoryview_setitem') @cname('__pyx_memoryview_setitem')
...@@ -355,12 +356,11 @@ cdef class memoryview(object): ...@@ -355,12 +356,11 @@ cdef class memoryview(object):
cdef memoryview_cwrapper(object o, int flags): cdef memoryview_cwrapper(object o, int flags):
return memoryview(o, flags) return memoryview(o, flags)
cdef _check_index(index):
if not PyIndex_Check(index):
raise TypeError("Cannot index with %s" % type(index))
cdef tuple _unellipsify(object index, int ndim): cdef tuple _unellipsify(object index, int ndim):
"""
Replace all ellipses with full slices and fill incomplete indices with
full slices.
"""
if not isinstance(index, tuple): if not isinstance(index, tuple):
tup = (index,) tup = (index,)
else: else:
...@@ -373,11 +373,14 @@ cdef tuple _unellipsify(object index, int ndim): ...@@ -373,11 +373,14 @@ cdef tuple _unellipsify(object index, int ndim):
if item is Ellipsis: if item is Ellipsis:
if not seen_ellipsis: if not seen_ellipsis:
result.extend([slice(None)] * (ndim - len(tup) + 1)) result.extend([slice(None)] * (ndim - len(tup) + 1))
result.extend(tup[idx + 1:]) seen_ellipsis = True
else: else:
result.append(slice(None)) result.append(slice(None))
have_slices = True have_slices = True
else: else:
if not isinstance(item, slice) and not PyIndex_Check(item):
raise TypeError("Cannot index with type '%s'" % type(item))
have_slices = have_slices or isinstance(item, slice) have_slices = have_slices or isinstance(item, slice)
result.append(item) result.append(item)
...@@ -385,102 +388,212 @@ cdef tuple _unellipsify(object index, int ndim): ...@@ -385,102 +388,212 @@ cdef tuple _unellipsify(object index, int ndim):
if nslices: if nslices:
result.extend([slice(None)] * nslices) result.extend([slice(None)] * nslices)
for idx in tup: return have_slices or nslices, tuple(result)
if isinstance(idx, slice):
return True, tup
return False, tup #
### Slicing a memoryview
#
@cname('__pyx_pybuffer_slice') @cname('__pyx_memview_slice')
cdef memoryview pybuffer_slice(memoryview memview, object indices): cdef memoryview memview_slice(memoryview memview, object indices):
cdef Py_ssize_t idx, dim, new_dim = 0, suboffset_dim = -1 cdef int new_ndim = 0, suboffset_dim = -1, dim
cdef Py_ssize_t shape, stride
cdef bint negative_step cdef bint negative_step
cdef int new_ndim = 0 cdef {{memviewslice_name}} dst, src
cdef {{memviewslice_name}} dst cdef {{memviewslice_name}} *p_src
for dim, index in enumerate(indices): cdef _memoryviewslice memviewsliceobj
shape = memview.view.shape[dim]
stride = memview.view.strides[dim]
assert memview.view.ndim > 0
if isinstance(memview, _memoryviewslice):
memviewsliceobj = memview
p_src = &memviewsliceobj.from_slice
else:
create_slice(memview, &src)
p_src = &src
# Note: don't use variable src at this point
# SubNote: we should be able to declare variables in blocks...
# memoryview_fromslice() will inc our dst slice
dst.memview = p_src.memview
dst.data = p_src.data
for dim, index in enumerate(indices):
if PyIndex_Check(index): if PyIndex_Check(index):
idx = index slice_memviewslice(p_src, &dst, True, dim, new_ndim, &suboffset_dim,
if idx < 0: index, 0, 0, 0, 0, 0, False)
idx += shape
if not 0 <= idx < shape:
raise IndexError("Index out of bounds (axis %d)" % dim)
else: else:
# index is a slice slice_memviewslice(p_src, &dst, True, dim, new_ndim, &suboffset_dim,
index.start or 0,
index.stop or 0,
index.step or 0,
index.start is not None,
index.stop is not None,
index.step is not None,
True)
new_ndim += 1 new_ndim += 1
start, stop, step = index.start, index.stop, index.step if isinstance(memview, _memoryviewslice):
negative_step = step and step < 0 return memoryview_fromslice(&dst, new_ndim,
memviewsliceobj.to_object_func,
memviewsliceobj.to_dtype_func)
else:
return memoryview_fromslice(&dst, new_ndim, NULL, NULL)
# set some defaults #
if not start: ### Slicing in a single dimension of a memoryviewslice
if negative_step: #
start = shape - 1
else:
start = 0
if not stop: cdef extern from "stdlib.h":
if negative_step: void abort() nogil
stop = -1
else: cdef extern from "stdio.h":
stop = shape ctypedef struct FILE
FILE *stderr
int fputs(char *s, FILE *stream)
cdef extern from "pystate.h":
void PyThreadState_Get() nogil
# These are not actually nogil, but we check for the GIL before calling them
void PyErr_SetString(PyObject *type, char *msg) nogil
PyObject *PyErr_Format(PyObject *exc, char *msg, ...) nogil
cdef:
char *ERR_OOB = "Index out of bounds (axis %d)"
char *ERR_STEP = "Step must not be zero (axis %d)"
char *ERR_INDIRECT_GIL = ("Cannot make indirect dimension %d disappear "
"through indexing, consider slicing with %d:%d")
char *ERR_INDIRECT_NOGIL = ("Cannot make indirect dimension %d disappear "
"through indexing")
PyObject *exc = <PyObject *> IndexError
@cname('__pyx_memoryview_slice_memviewslice')
cdef char *slice_memviewslice({{memviewslice_name}} *src,
{{memviewslice_name}} *dst,
bint have_gil,
int dim,
int new_ndim,
int *suboffset_dim,
Py_ssize_t start,
Py_ssize_t stop,
Py_ssize_t step,
int have_start,
int have_stop,
int have_step,
bint is_slice) nogil except *:
"""
Create a new slice dst given slice src.
have_gil - if true, the GIL must be held and exceptions may be raised
dim - the current src dimension (indexing will make dimensions
disappear)
new_dim - the new dst dimension
suboffset_dim - pointer to a single int initialized to -1 to keep track of
where slicing offsets should be added
"""
# check our bounds cdef:
Py_ssize_t shape, stride, suboffset
Py_ssize_t new_shape
bint negative_step
if have_gil:
# Assert the GIL
PyThreadState_Get()
shape = src.shape[dim]
stride = src.strides[dim]
suboffset = src.suboffsets[dim]
if not is_slice:
# index is a normal integer-like index
if start < 0:
start += shape
if not 0 <= start < shape:
if have_gil:
PyErr_Format(exc, ERR_OOB, dim)
return ERR_OOB
else:
# index is a slice
negative_step = have_step != 0 and step < 0
if have_step and step == 0:
if have_gil:
# ValueError might be more appropriate, but this will make it consistent
# with nogil slicing
PyErr_SetString(exc, ERR_STEP)
return ERR_STEP
# check our bounds and set defaults
if have_start:
if start < 0: if start < 0:
start += shape start += shape
if start < 0: if start < 0:
start = 0 start = 0
elif start >= shape: elif start >= shape:
if negative_step:
start = shape - 1
else:
start = shape
else:
if negative_step:
start = shape - 1 start = shape - 1
else:
start = 0
if have_stop:
if stop < 0: if stop < 0:
stop += shape stop += shape
if stop < 0: if stop < 0:
stop = 0 stop = 0
elif stop > shape: elif stop > shape:
stop = shape stop = shape
else:
if negative_step:
stop = -1
else:
stop = shape
step = step or 1 if not have_step:
step = 1
# shape/strides/suboffsets # len = ceil( (stop - start) / step )
dst.strides[new_dim] = stride * step with cython.cdivision(True):
dst.shape[new_dim] = (stop - start) / step new_shape = (stop - start) // step
if (stop - start) % step:
dst.shape[new_dim] += 1
dst.suboffsets[new_dim] = memview.view.suboffsets[dim]
# set this for the slicing offset if (stop - start) % step:
if negative_step: new_shape += 1
idx = stop
else:
idx = start
# Add the slicing or idexing offsets to the right suboffset or base data * if new_shape < 0:
if suboffset_dim < 0: new_shape = 0
dst.data += idx * stride
else:
dst.suboffsets[suboffset_dim] += idx * stride
if memview.view.suboffsets[dim]: # shape/strides/suboffsets
if PyIndex_Check(index): dst.strides[new_ndim] = stride * step
raise IndexError( dst.shape[new_ndim] = new_shape
"Cannot make indirect dimension %d disappear through " dst.suboffsets[new_ndim] = suboffset
"indexing, consider slicing with %d:%d" % (dim, idx, idx + 1))
suboffset_dim = new_dim
cdef _memoryviewslice memviewsliceobj # Add the slicing or idexing offsets to the right suboffset or base data *
if isinstance(memview, _memoryviewslice): if suboffset_dim[0] < 0:
memviewsliceobj = memview dst.data += start * stride
return memoryview_fromslice(&dst, new_dim,
memviewsliceobj.to_object_func,
memviewsliceobj.to_dtype_func)
else: else:
return memoryview_fromslice(&dst, new_dim, NULL, NULL) dst.suboffsets[suboffset_dim[0]] += start * stride
if suboffset >= 0:
if not is_slice:
if have_gil:
PyErr_Format(exc, ERR_INDIRECT_GIL, dim, start, start + 1)
return ERR_INDIRECT_NOGIL
else:
suboffset_dim[0] = new_ndim
return NULL
#
### Index a memoryview
#
@cname('__pyx_pybuffer_index') @cname('__pyx_pybuffer_index')
cdef char *pybuffer_index(Py_buffer *view, char *bufp, Py_ssize_t index, cdef char *pybuffer_index(Py_buffer *view, char *bufp, Py_ssize_t index,
int dim) except NULL: int dim) except NULL:
...@@ -511,13 +624,16 @@ cdef char *pybuffer_index(Py_buffer *view, char *bufp, Py_ssize_t index, ...@@ -511,13 +624,16 @@ cdef char *pybuffer_index(Py_buffer *view, char *bufp, Py_ssize_t index,
return resultp return resultp
#
### Creating new memoryview objects from slices and memoryviews
#
@cname('__pyx_memoryviewslice') @cname('__pyx_memoryviewslice')
cdef class _memoryviewslice(memoryview): cdef class _memoryviewslice(memoryview):
"Internal class for passing memory view slices to Python" "Internal class for passing memoryview slices to Python"
# We need this to keep our shape/strides/suboffset pointers valid # We need this to keep our shape/strides/suboffset pointers valid
cdef {{memviewslice_name}} from_slice cdef {{memviewslice_name}} from_slice
# We need this only to print it's classes name # We need this only to print it's class' name
cdef object from_object cdef object from_object
cdef object (*to_object_func)(char *) cdef object (*to_object_func)(char *)
...@@ -550,9 +666,12 @@ cdef memoryview_fromslice({{memviewslice_name}} *memviewslice, ...@@ -550,9 +666,12 @@ cdef memoryview_fromslice({{memviewslice_name}} *memviewslice,
object (*to_object_func)(char *), object (*to_object_func)(char *),
int (*to_dtype_func)(char *, object) except 0): int (*to_dtype_func)(char *, object) except 0):
cdef _memoryviewslice result
cdef int i
assert 0 < ndim <= memviewslice.memview.view.ndim, (ndim, memviewslice.memview.view.ndim) assert 0 < ndim <= memviewslice.memview.view.ndim, (ndim, memviewslice.memview.view.ndim)
cdef _memoryviewslice result = _memoryviewslice(None, 0) result = _memoryviewslice(None, 0)
result.from_slice = memviewslice[0] result.from_slice = memviewslice[0]
__PYX_INC_MEMVIEW(memviewslice, 1) __PYX_INC_MEMVIEW(memviewslice, 1)
...@@ -560,30 +679,41 @@ cdef memoryview_fromslice({{memviewslice_name}} *memviewslice, ...@@ -560,30 +679,41 @@ cdef memoryview_fromslice({{memviewslice_name}} *memviewslice,
result.from_object = <object> memviewslice.memview.obj result.from_object = <object> memviewslice.memview.obj
result.view = memviewslice.memview.view result.view = memviewslice.memview.view
result.view.shape = <Py_ssize_t *> &result.from_slice.shape result.view.buf = <void *> memviewslice.data
result.view.strides = <Py_ssize_t *> result.from_slice.strides
result.view.suboffsets = <Py_ssize_t *> &result.from_slice.suboffsets
result.view.ndim = ndim result.view.ndim = ndim
result.view.shape = <Py_ssize_t *> result.from_slice.shape
result.view.strides = <Py_ssize_t *> result.from_slice.strides
result.view.suboffsets = <Py_ssize_t *> result.from_slice.suboffsets
result.view.len = result.view.itemsize
for i in range(ndim):
result.view.len *= result.view.shape[i]
result.to_object_func = to_object_func result.to_object_func = to_object_func
result.to_dtype_func = to_dtype_func result.to_dtype_func = to_dtype_func
return result return result
@cname('__pyx_memoryview_create_slice')
cdef void create_slice(memoryview memview, {{memviewslice_name}} *dst):
cdef int dim
dst.memview = <__pyx_memoryview *> memview
dst.data = <char *> memview.view.buf
for dim in range(memview.view.ndim):
dst.shape[dim] = memview.view.shape[dim]
dst.strides[dim] = memview.view.strides[dim]
dst.suboffsets[dim] = memview.view.suboffsets[dim]
@cname('__pyx_memoryview_copy')
cdef memoryview_copy(memoryview memview): cdef memoryview_copy(memoryview memview):
cdef {{memviewslice_name}} memviewslice cdef {{memviewslice_name}} memviewslice
cdef int dim
cdef object (*to_object_func)(char *) cdef object (*to_object_func)(char *)
cdef int (*to_dtype_func)(char *, object) except 0 cdef int (*to_dtype_func)(char *, object) except 0
memviewslice.memview = <__pyx_memoryview *> memview create_slice(memview, &memviewslice)
memviewslice.data = <char *> memview.view.buf
# Copy all of these as from_slice will
for dim in range(memview.view.ndim):
memviewslice.shape[dim] = memview.view.shape[dim]
memviewslice.strides[dim] = memview.view.strides[dim]
memviewslice.suboffsets[dim] = memview.view.suboffsets[dim]
if isinstance(memview, _memoryviewslice): if isinstance(memview, _memoryviewslice):
to_object_func = (<_memoryviewslice> memview).to_object_func to_object_func = (<_memoryviewslice> memview).to_object_func
......
...@@ -284,7 +284,7 @@ static CYTHON_INLINE void __Pyx_INC_MEMVIEW({{memviewslice_name}} *memslice, ...@@ -284,7 +284,7 @@ static CYTHON_INLINE void __Pyx_INC_MEMVIEW({{memviewslice_name}} *memslice,
if (!memview) if (!memview)
return; /* allow uninitialized memoryview assignment */ return; /* allow uninitialized memoryview assignment */
if (memview->acquisition_count <= 0) if (memview->acquisition_count < 0)
__pyx_fatalerror("Acquisition count is %d (line %d)", __pyx_fatalerror("Acquisition count is %d (line %d)",
memview->acquisition_count, lineno); memview->acquisition_count, lineno);
...@@ -463,4 +463,4 @@ int {{set_function}}(const char *itemp, PyObject *obj) { ...@@ -463,4 +463,4 @@ int {{set_function}}(const char *itemp, PyObject *obj) {
Py_DECREF(*(PyObject **) itemp); Py_DECREF(*(PyObject **) itemp);
*(PyObject **) itemp = obj; *(PyObject **) itemp = obj;
return 1; return 1;
} }
\ No newline at end of file
...@@ -588,40 +588,122 @@ def assign_temporary_to_object(object[:] mslice): ...@@ -588,40 +588,122 @@ def assign_temporary_to_object(object[:] mslice):
buf = mslice buf = mslice
buf[1] = {3-2: 2+(2*4)-2} buf[1] = {3-2: 2+(2*4)-2}
def test_slicing(arg): def print_int_offsets(*args):
for item in args:
print item / sizeof(int),
print
def test_generic_slicing(arg):
""" """
Test simple slicing Test simple slicing
>>> test_slicing(IntMockBuffer("A", range(8 * 14 * 11), shape=(8, 14, 11))) >>> test_generic_slicing(IntMockBuffer("A", range(8 * 14 * 11), shape=(8, 14, 11)))
acquired A acquired A
3 9 2 (3, 9, 2)
1232 -44 4 308 -11 1
-1 -1 -1 -1 -1 -1
released A released A
Test direct slicing, negative slice oob in dim 2 Test direct slicing, negative slice oob in dim 2
>>> test_slicing(IntMockBuffer("A", range(1 * 2 * 3), shape=(1, 2, 3))) >>> test_generic_slicing(IntMockBuffer("A", range(1 * 2 * 3), shape=(1, 2, 3)))
acquired A acquired A
0 0 2 (0, 0, 2)
48 -12 4 12 -3 1
-1 -1 -1 -1 -1 -1
released A released A
Test indirect slicing Test indirect slicing
>>> L = [[range(k * 12 + j * 4, k * 12 + j * 4 + 4) for j in xrange(3)] for k in xrange(5)] >>> L = [[range(k * 12 + j * 4, k * 12 + j * 4 + 4) for j in xrange(3)] for k in xrange(5)]
>>> test_slicing(IntMockBuffer("A", L, shape=(5, 3, 4))) >>> test_generic_slicing(IntMockBuffer("A", L, shape=(5, 3, 4)))
acquired A acquired A
2 0 2 (2, 0, 2)
8 -4 4 0 1 -1
0 0 -1
released A released A
>>> stride1 = 21 * 14
>>> stride2 = 21
>>> L = [[range(k * stride1 + j * stride2, k * stride1 + j * stride2 + 21) for j in xrange(14)] for k in xrange(9)]
>>> test_generic_slicing(IntMockBuffer("A", L, shape=(9, 14, 21)))
acquired A
(3, 9, 2)
20 1 -1
released A
""" """
cdef int[::view.generic, ::view.generic, :] _a = arg cdef int[::view.generic, ::view.generic, :] _a = arg
a = _a a = _a
b = a[2:8:2, -4:1:-1, 1:3] b = a[2:8:2, -4:1:-1, 1:3]
print b.shape[0], b.shape[1], b.shape[2] print b.shape
print b.strides[0], b.strides[1], b.strides[2] if b.suboffsets[0] < 0:
print b.suboffsets[0], b.suboffsets[1], b.suboffsets[2] print_int_offsets(*b.strides)
print_int_offsets(*b.suboffsets)
cdef int i, j, k
for i in range(b.shape[0]):
for j in range(b.shape[1]):
for k in range(b.shape[2]):
itemA = a[2 + 2 * i, -4 - j, 1 + k]
itemB = b[i, j, k]
assert itemA == itemB, (i, j, k, itemA, itemB)
def test_indirect_slicing(arg):
"""
Test indirect slicing
>>> L = [[range(k * 12 + j * 4, k * 12 + j * 4 + 4) for j in xrange(3)] for k in xrange(5)]
>>> test_indirect_slicing(IntMockBuffer("A", L, shape=(5, 3, 4)))
acquired A
(5, 3, 2)
0 0 -1
58
released A
>>> stride1 = 21 * 14
>>> stride2 = 21
>>> L = [[range(k * stride1 + j * stride2, k * stride1 + j * stride2 + 21) for j in xrange(14)] for k in xrange(9)]
>>> test_indirect_slicing(IntMockBuffer("A", L, shape=(9, 14, 21)))
acquired A
(5, 14, 3)
0 16 -1
2412
released A
"""
cdef int[::view.indirect, ::view.indirect, :] _a = arg
a = _a
b = a[-5:, ..., -5:100:2]
print b.shape
print_int_offsets(*b.suboffsets)
print b[4, 2, 1]
def test_direct_slicing(arg):
"""
Fused types would be convenient to test this stuff!
Test simple slicing
>>> test_direct_slicing(IntMockBuffer("A", range(8 * 14 * 11), shape=(8, 14, 11)))
acquired A
(3, 9, 2)
308 -11 1
-1 -1 -1
released A
Test direct slicing, negative slice oob in dim 2
>>> test_direct_slicing(IntMockBuffer("A", range(1 * 2 * 3), shape=(1, 2, 3)))
acquired A
(0, 0, 2)
12 -3 1
-1 -1 -1
released A
"""
cdef int[:, :, :] _a = arg
a = _a
b = a[2:8:2, -4:1:-1, 1:3]
print b.shape
print_int_offsets(*b.strides)
print_int_offsets(*b.suboffsets)
cdef int i, j, k cdef int i, j, k
for i in range(b.shape[0]): for i in range(b.shape[0]):
...@@ -631,13 +713,14 @@ def test_slicing(arg): ...@@ -631,13 +713,14 @@ def test_slicing(arg):
itemB = b[i, j, k] itemB = b[i, j, k]
assert itemA == itemB, (i, j, k, itemA, itemB) assert itemA == itemB, (i, j, k, itemA, itemB)
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))
>>> test_slicing_and_indexing(a) >>> test_slicing_and_indexing(a)
acquired A acquired A
5 2 (5, 2)
60 8 15 2
126 113 126 113
[111] [111]
released A released A
...@@ -648,8 +731,8 @@ def test_slicing_and_indexing(arg): ...@@ -648,8 +731,8 @@ def test_slicing_and_indexing(arg):
c = b[4:1:-1, ::-1] c = b[4:1:-1, ::-1]
d = c[2, 1:2] d = c[2, 1:2]
print b.shape[0], b.shape[1] print b.shape
print b.strides[0], b.strides[1] print_int_offsets(*b.strides)
cdef int i, j cdef int i, j
for i in range(b.shape[0]): for i in range(b.shape[0]):
...@@ -659,4 +742,14 @@ def test_slicing_and_indexing(arg): ...@@ -659,4 +742,14 @@ def test_slicing_and_indexing(arg):
assert itemA == itemB, (i, j, itemA, itemB) assert itemA == itemB, (i, j, itemA, itemB)
print c[1, 1], c[2, 0] print c[1, 1], c[2, 0]
print [d[i] for i in range(d.shape[0])] print [d[i] for i in range(d.shape[0])]
\ No newline at end of file
def test_oob():
"""
>>> test_oob()
Traceback (most recent call last):
...
IndexError: Index out of bounds (axis 1)
"""
cdef int[:, :] a = IntMockBuffer("A", range(4 * 9), shape=(4, 9))
print a[:, 20]
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
cimport cython
from cython cimport view from cython cimport view
__test__ = {} __test__ = {}
...@@ -1158,40 +1159,127 @@ def test_cdef_function2(): ...@@ -1158,40 +1159,127 @@ def test_cdef_function2():
cdef_function2(global_A, global_B) cdef_function2(global_A, global_B)
def print_int_offsets(*args):
for item in args:
print item / sizeof(int),
print
@testcase @testcase
def test_slicing(arg): def test_generic_slicing(arg):
""" """
Test simple slicing Test simple slicing
>>> test_slicing(IntMockBuffer("A", range(8 * 14 * 11), shape=(8, 14, 11))) >>> test_generic_slicing(IntMockBuffer("A", range(8 * 14 * 11), shape=(8, 14, 11)))
acquired A acquired A
3 9 2 3 9 2
1232 -44 4 308 -11 1
-1 -1 -1 -1 -1 -1
released A released A
Test direct slicing, negative slice oob in dim 2 Test direct slicing, negative slice oob in dim 2
>>> test_slicing(IntMockBuffer("A", range(1 * 2 * 3), shape=(1, 2, 3))) >>> test_generic_slicing(IntMockBuffer("A", range(1 * 2 * 3), shape=(1, 2, 3)))
acquired A acquired A
0 0 2 0 0 2
48 -12 4 12 -3 1
-1 -1 -1 -1 -1 -1
released A released A
Test indirect slicing Test indirect slicing
>>> L = [[range(k * 12 + j * 4, k * 12 + j * 4 + 4) for j in xrange(3)] for k in xrange(5)] >>> L = [[range(k * 12 + j * 4, k * 12 + j * 4 + 4) for j in xrange(3)] for k in xrange(5)]
>>> test_slicing(IntMockBuffer("A", L, shape=(5, 3, 4))) >>> test_generic_slicing(IntMockBuffer("A", L, shape=(5, 3, 4)))
acquired A acquired A
2 0 2 2 0 2
8 -4 4 0 1 -1
0 0 -1 released A
>>> stride1 = 21 * 14
>>> stride2 = 21
>>> L = [[range(k * stride1 + j * stride2, k * stride1 + j * stride2 + 21) for j in xrange(14)] for k in xrange(9)]
>>> test_generic_slicing(IntMockBuffer("A", L, shape=(9, 14, 21)))
acquired A
3 9 2
20 1 -1
released A released A
""" """
cdef int[::view.generic, ::view.generic, :] a = arg cdef int[::view.generic, ::view.generic, :] a = arg
cdef int[::view.generic, ::view.generic, :] b = a[2:8:2, -4:1:-1, 1:3] cdef int[::view.generic, ::view.generic, :] b = a[2:8:2, -4:1:-1, 1:3]
print b.shape[0], b.shape[1], b.shape[2] print b.shape[0], b.shape[1], b.shape[2]
print b.strides[0], b.strides[1], b.strides[2] if b.suboffsets[0] < 0:
print b.suboffsets[0], b.suboffsets[1], b.suboffsets[2] print_int_offsets(b.strides[0], b.strides[1], b.strides[2])
print_int_offsets(b.suboffsets[0], b.suboffsets[1], b.suboffsets[2])
cdef int i, j, k
for i in range(b.shape[0]):
for j in range(b.shape[1]):
for k in range(b.shape[2]):
itemA = a[2 + 2 * i, -4 - j, 1 + k]
itemB = b[i, j, k]
assert itemA == itemB, (i, j, k, itemA, itemB)
@testcase
def test_indirect_slicing(arg):
"""
Test indirect slicing
>>> L = [[range(k * 12 + j * 4, k * 12 + j * 4 + 4) for j in xrange(3)] for k in xrange(5)]
>>> test_indirect_slicing(IntMockBuffer("A", L, shape=(5, 3, 4)))
acquired A
5 3 2
0 0 -1
58
56
released A
>>> stride1 = 21 * 14
>>> stride2 = 21
>>> L = [[range(k * stride1 + j * stride2, k * stride1 + j * stride2 + 21) for j in xrange(14)] for k in xrange(9)]
>>> test_indirect_slicing(IntMockBuffer("A", L, shape=(9, 14, 21)))
acquired A
5 14 3
0 16 -1
2412
2410
released A
"""
cdef int[::view.indirect, ::view.indirect, :] a = arg
cdef int[::view.indirect, ::view.indirect, :] b = a[-5:, ..., -5:100:2]
cdef int[::view.indirect, ::view.indirect] c = b[..., 0]
print b.shape[0], b.shape[1], b.shape[2]
print_int_offsets(b.suboffsets[0], b.suboffsets[1], b.suboffsets[2])
print b[4, 2, 1]
print c[4, 2]
@testcase
def test_direct_slicing(arg):
"""
Fused types would be convenient to test this stuff!
Test simple slicing
>>> test_direct_slicing(IntMockBuffer("A", range(8 * 14 * 11), shape=(8, 14, 11)))
acquired A
3 9 2
308 -11 1
-1 -1 -1
released A
Test direct slicing, negative slice oob in dim 2
>>> test_direct_slicing(IntMockBuffer("A", range(1 * 2 * 3), shape=(1, 2, 3)))
acquired A
0 0 2
12 -3 1
-1 -1 -1
released A
"""
cdef int[:, :, ::1] a = arg
cdef int[:, :, :] b = a[2:8:2, -4:1:-1, 1:3]
print b.shape[0], b.shape[1], b.shape[2]
print_int_offsets(b.strides[0], b.strides[1], b.strides[2])
print_int_offsets(b.suboffsets[0], b.suboffsets[1], b.suboffsets[2])
cdef int i, j, k cdef int i, j, k
for i in range(b.shape[0]): for i in range(b.shape[0]):
...@@ -1208,7 +1296,7 @@ def test_slicing_and_indexing(arg): ...@@ -1208,7 +1296,7 @@ def test_slicing_and_indexing(arg):
>>> test_slicing_and_indexing(a) >>> test_slicing_and_indexing(a)
acquired A acquired A
5 2 5 2
60 8 15 2
126 113 126 113
[111] [111]
released A released A
...@@ -1219,7 +1307,7 @@ def test_slicing_and_indexing(arg): ...@@ -1219,7 +1307,7 @@ def test_slicing_and_indexing(arg):
cdef int[:] d = c[2, 1:2] cdef int[:] d = c[2, 1:2]
print b.shape[0], b.shape[1] print b.shape[0], b.shape[1]
print b.strides[0], b.strides[1] print_int_offsets(b.strides[0], b.strides[1])
cdef int i, j cdef int i, j
for i in range(b.shape[0]): for i in range(b.shape[0]):
...@@ -1229,4 +1317,16 @@ def test_slicing_and_indexing(arg): ...@@ -1229,4 +1317,16 @@ def test_slicing_and_indexing(arg):
assert itemA == itemB, (i, j, itemA, itemB) assert itemA == itemB, (i, j, itemA, itemB)
print c[1, 1], c[2, 0] print c[1, 1], c[2, 0]
print [d[i] for i in range(d.shape[0])] print [d[i] for i in range(d.shape[0])]
\ No newline at end of file
@testcase
def test_oob():
"""
>>> test_oob()
Traceback (most recent call last):
...
IndexError: Index out of bounds (axis 1)
"""
cdef int[:, :] a = IntMockBuffer("A", range(4 * 9), shape=(4, 9))
print a[:, 20]
...@@ -6,13 +6,15 @@ Test slicing for memoryviews and memoryviewslices ...@@ -6,13 +6,15 @@ Test slicing for memoryviews and memoryviewslices
""" """
cimport numpy as np cimport numpy as np
import numpy import numpy as np
ctypedef np.int32_t dtype_t
def get_array(): def get_array():
# We need to type our array to get a __pyx_get_buffer() that typechecks # We need to type our array to get a __pyx_get_buffer() that typechecks
# for np.ndarray and calls __getbuffer__ in numpy.pxd # for np.ndarray and calls __getbuffer__ in numpy.pxd
cdef np.ndarray[int, ndim=3] a cdef np.ndarray[dtype_t, ndim=3] a
a = numpy.arange(8 * 14 * 11).reshape(8, 14, 11) a = np.arange(8 * 14 * 11, dtype=np.int32).reshape(8, 14, 11)
return a return a
a = get_array() a = get_array()
...@@ -31,11 +33,11 @@ def test_partial_slicing(array): ...@@ -31,11 +33,11 @@ def test_partial_slicing(array):
""" """
>>> test_partial_slicing(a) >>> test_partial_slicing(a)
""" """
cdef int[:, :, :] a = array cdef dtype_t[:, :, :] a = array
obj = array[4] obj = array[4]
cdef int[:, :] b = a[4, :] cdef dtype_t[:, :] b = a[4, :]
cdef int[:, :] c = a[4] cdef dtype_t[:, :] c = a[4]
ae(b.shape[0], c.shape[0], obj.shape[0]) ae(b.shape[0], c.shape[0], obj.shape[0])
ae(b.shape[1], c.shape[1], obj.shape[1]) ae(b.shape[1], c.shape[1], obj.shape[1])
...@@ -46,15 +48,15 @@ def test_ellipsis(array): ...@@ -46,15 +48,15 @@ def test_ellipsis(array):
""" """
>>> test_ellipsis(a) >>> test_ellipsis(a)
""" """
cdef int[:, :, :] a = array cdef dtype_t[:, :, :] a = array
cdef int[:, :] b = a[..., 4] cdef dtype_t[:, :] b = a[..., 4]
b_obj = array[..., 4] b_obj = array[..., 4]
cdef int[:, :] c = a[4, ...] cdef dtype_t[:, :] c = a[4, ...]
c_obj = array[4, ...] c_obj = array[4, ...]
cdef int[:, :] d = a[2:8, ..., 2] cdef dtype_t[:, :] d = a[2:8, ..., 2]
d_obj = array[2:8, ..., 2] d_obj = array[2:8, ..., 2]
ae(tuple([b.shape[i] for i in range(2)]), b_obj.shape) ae(tuple([b.shape[i] for i in range(2)]), b_obj.shape)
...@@ -75,7 +77,7 @@ def test_ellipsis(array): ...@@ -75,7 +77,7 @@ def test_ellipsis(array):
for j in range(d.shape[1]): for j in range(d.shape[1]):
ae(d[i, j], d_obj[i, j]) ae(d[i, j], d_obj[i, j])
cdef int[:] e = a[..., 5, 6] cdef dtype_t[:] e = a[..., 5, 6]
e_obj = array[..., 5, 6] e_obj = array[..., 5, 6]
ae(e.shape[0], e_obj.shape[0]) ae(e.shape[0], e_obj.shape[0])
ae(e.strides[0], e_obj.strides[0]) ae(e.strides[0], e_obj.strides[0])
...@@ -88,7 +90,7 @@ def test_partial_slicing_memoryview(array): ...@@ -88,7 +90,7 @@ def test_partial_slicing_memoryview(array):
""" """
>>> test_partial_slicing_memoryview(a) >>> test_partial_slicing_memoryview(a)
""" """
cdef int[:, :, :] _a = array cdef dtype_t[:, :, :] _a = array
a = _a a = _a
obj = array[4] obj = array[4]
...@@ -104,7 +106,7 @@ def test_ellipsis_memoryview(array): ...@@ -104,7 +106,7 @@ def test_ellipsis_memoryview(array):
""" """
>>> test_ellipsis_memoryview(a) >>> test_ellipsis_memoryview(a)
""" """
cdef int[:, :, :] _a = array cdef dtype_t[:, :, :] _a = array
a = _a a = _a
b = a[..., 4] b = a[..., 4]
...@@ -137,4 +139,4 @@ def test_ellipsis_memoryview(array): ...@@ -137,4 +139,4 @@ def test_ellipsis_memoryview(array):
e = a[..., 5, 6] e = a[..., 5, 6]
e_obj = array[..., 5, 6] e_obj = array[..., 5, 6]
ae(e.shape[0], e_obj.shape[0]) ae(e.shape[0], e_obj.shape[0])
ae(e.strides[0], e_obj.strides[0]) ae(e.strides[0], e_obj.strides[0])
\ No newline at end of file
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