summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Florisson <markflorisson88@gmail.com>2011-07-25 21:13:10 +0200
committerMark Florisson <markflorisson88@gmail.com>2011-09-30 14:55:17 +0100
commit83e7a363fcc757ebab94b544b0ac64c270e73e39 (patch)
tree4d032bf6fc912b739f07bbbd69f8ba28915107d2
parentaad49345e18a162ab0caa332b5379729b76f90fe (diff)
downloadcython-83e7a363fcc757ebab94b544b0ac64c270e73e39.tar.gz
MemoryViewSlice indexing and object coercion + MemoryView indexing
-rw-r--r--Cython/Compiler/Buffer.py89
-rw-r--r--Cython/Compiler/Code.py116
-rw-r--r--Cython/Compiler/CythonScope.py22
-rwxr-xr-xCython/Compiler/ExprNodes.py99
-rw-r--r--Cython/Compiler/Main.py8
-rw-r--r--Cython/Compiler/MemoryView.py96
-rw-r--r--Cython/Compiler/ModuleNode.py12
-rw-r--r--Cython/Compiler/Nodes.py40
-rwxr-xr-xCython/Compiler/PyrexTypes.py77
-rw-r--r--Cython/Compiler/Symtab.py4
-rw-r--r--Cython/Compiler/UtilityCode.py15
-rw-r--r--Cython/Utility/MemoryView.pyx165
-rw-r--r--Cython/Utility/MemoryView_C.c126
-rw-r--r--Cython/Utils.py8
-rw-r--r--tests/run/bufaccess.pyx319
-rw-r--r--tests/run/cythonarray.pyx38
-rw-r--r--tests/run/memslice.pyx1102
-rw-r--r--tests/run/memslice_indexing.pyx40
-rw-r--r--tests/run/mockbuffers.pxi313
19 files changed, 2119 insertions, 570 deletions
diff --git a/Cython/Compiler/Buffer.py b/Cython/Compiler/Buffer.py
index 39a053fd9..6825a13ca 100644
--- a/Cython/Compiler/Buffer.py
+++ b/Cython/Compiler/Buffer.py
@@ -215,6 +215,50 @@ class BufferEntry(object):
def _for_all_ndim(self, s):
return [s % (self.cname, i) for i in range(self.type.ndim)]
+ def generate_buffer_lookup_code(self, code, index_cnames):
+ # Create buffer lookup and return it
+ # This is done via utility macros/inline functions, which vary
+ # according to the access mode used.
+ params = []
+ nd = self.type.ndim
+ mode = self.type.mode
+ if mode == 'full':
+ for i, s, o in zip(index_cnames,
+ self.get_buf_stridevars(),
+ self.get_buf_suboffsetvars()):
+ params.append(i)
+ params.append(s)
+ params.append(o)
+ funcname = "__Pyx_BufPtrFull%dd" % nd
+ funcgen = buf_lookup_full_code
+ else:
+ if mode == 'strided':
+ funcname = "__Pyx_BufPtrStrided%dd" % nd
+ funcgen = buf_lookup_strided_code
+ elif mode == 'c':
+ funcname = "__Pyx_BufPtrCContig%dd" % nd
+ funcgen = buf_lookup_c_code
+ elif mode == 'fortran':
+ funcname = "__Pyx_BufPtrFortranContig%dd" % nd
+ funcgen = buf_lookup_fortran_code
+ else:
+ assert False
+ for i, s in zip(index_cnames, self.get_buf_stridevars()):
+ params.append(i)
+ params.append(s)
+
+ # Make sure the utility code is available
+ if funcname not in code.globalstate.utility_codes:
+ code.globalstate.utility_codes.add(funcname)
+ protocode = code.globalstate['utility_code_proto']
+ defcode = code.globalstate['utility_code_def']
+ funcgen(protocode, defcode, name=funcname, nd=nd)
+
+ buf_ptr_type_code = self.buf_ptr_type.declaration_code("")
+ ptrcode = "%s(%s, %s, %s)" % (funcname, buf_ptr_type_code, self.buf_ptr,
+ ", ".join(params))
+ return ptrcode
+
def get_flags(buffer_aux, buffer_type):
flags = 'PyBUF_FORMAT'
@@ -412,7 +456,7 @@ def put_buffer_lookup_code(entry, index_signeds, index_cnames, directives,
cast = "(size_t)"
code.putln("if (%s) %s = %d;" % (
code.unlikely("%s >= %s%s" % (cname, cast, shape)),
- tmp_cname, dim))
+ tmp_cname, dim))
code.globalstate.use_utility_code(raise_indexerror_code)
code.putln("if (%s) {" % code.unlikely("%s != -1" % tmp_cname))
code.putln('__Pyx_RaiseBufferIndexError(%s);' % tmp_cname)
@@ -426,48 +470,7 @@ def put_buffer_lookup_code(entry, index_signeds, index_cnames, directives,
if signed != 0:
code.putln("if (%s < 0) %s += %s;" % (cname, cname, shape))
- # Create buffer lookup and return it
- # This is done via utility macros/inline functions, which vary
- # according to the access mode used.
- params = []
- nd = entry.type.ndim
- mode = entry.type.mode
- if mode == 'full':
- for i, s, o in zip(index_cnames,
- entry.get_buf_stridevars(),
- entry.get_buf_suboffsetvars()):
- params.append(i)
- params.append(s)
- params.append(o)
- funcname = "__Pyx_BufPtrFull%dd" % nd
- funcgen = buf_lookup_full_code
- else:
- if mode == 'strided':
- funcname = "__Pyx_BufPtrStrided%dd" % nd
- funcgen = buf_lookup_strided_code
- elif mode == 'c':
- funcname = "__Pyx_BufPtrCContig%dd" % nd
- funcgen = buf_lookup_c_code
- elif mode == 'fortran':
- funcname = "__Pyx_BufPtrFortranContig%dd" % nd
- funcgen = buf_lookup_fortran_code
- else:
- assert False
- for i, s in zip(index_cnames, entry.get_buf_stridevars()):
- params.append(i)
- params.append(s)
-
- # Make sure the utility code is available
- if funcname not in code.globalstate.utility_codes:
- code.globalstate.utility_codes.add(funcname)
- protocode = code.globalstate['utility_code_proto']
- defcode = code.globalstate['utility_code_def']
- funcgen(protocode, defcode, name=funcname, nd=nd)
-
- buf_ptr_type_code = entry.buf_ptr_type.declaration_code("")
- ptrcode = "%s(%s, %s, %s)" % (funcname, buf_ptr_type_code, entry.buf_ptr,
- ", ".join(params))
- return ptrcode
+ return entry.generate_buffer_lookup_code(code, index_cnames)
def use_bufstruct_declare_code(env):
diff --git a/Cython/Compiler/Code.py b/Cython/Compiler/Code.py
index f17f08cf6..595afc8fa 100644
--- a/Cython/Compiler/Code.py
+++ b/Cython/Compiler/Code.py
@@ -23,7 +23,6 @@ import DebugFlags
import Errors
from Cython import Tempita as tempita
-from Cython.Utils import none_or_sub
try:
from __builtin__ import basestring
except ImportError:
@@ -162,10 +161,10 @@ class UtilityCodeBase(object):
kwargs['impl'] = impl
if 'name' not in kwargs:
- if from_file:
- kwargs['name'] = os.path.splitext(from_file)[0]
- else:
- kwargs['name'] = util_code_name
+ kwargs['name'] = util_code_name
+
+ if 'file' not in kwargs and from_file:
+ kwargs['file'] = from_file
return cls(**kwargs)
@@ -188,13 +187,8 @@ class UtilityCodeBase(object):
proto, impl = utilities[util_code_name]
if context is not None:
- if '__name' not in context:
- context['__name'] = util_code_name
-
- if proto:
- proto = tempita.sub(proto, **context)
- if impl:
- impl = tempita.sub(impl, **context)
+ proto = sub_tempita(proto, context, from_file, util_code_name)
+ impl = sub_tempita(impl, context, from_file, util_code_name)
if cls.is_cython_utility:
# Remember line numbers
@@ -204,19 +198,56 @@ class UtilityCodeBase(object):
load_as_string = classmethod(load_as_string)
+ def none_or_sub(self, s, context, tempita):
+ """
+ Format a string in this utility code with context. If None, do nothing.
+ """
+ if s is None:
+ return None
+
+ if tempita:
+ return sub_tempita(s, context, self.file, self.name)
+
+ return s % context
+
def __str__(self):
return "<%s(%s)" % (type(self).__name__, self.name)
+def sub_tempita(s, context, file, name):
+ "Run tempita on string s with context context."
+ if not s:
+ return None
+
+ if file:
+ context['__name'] = "%s:%s" % (file, name)
+ elif name:
+ context['__name'] = name
+
+ return tempita.sub(s, **context)
+
+
class UtilityCode(UtilityCodeBase):
- # Stores utility code to add during code generation.
- #
- # See GlobalState.put_utility_code.
- #
- # hashes/equals by instance
+ """
+ Stores utility code to add during code generation.
+
+ See GlobalState.put_utility_code.
+
+ hashes/equals by instance
+
+ proto C prototypes
+ impl implemenation code
+ init code to call on module initialization
+ requires utility code dependencies
+ proto_block the place in the resulting file where the prototype should
+ end up
+ name name of the utility code (or None)
+ file filename of the utility code file this utility was loaded
+ from (or None)
+ """
def __init__(self, proto=None, impl=None, init=None, cleanup=None, requires=None,
- proto_block='utility_code_proto', name=None):
+ proto_block='utility_code_proto', name=None, file=None):
# proto_block: Which code block to dump prototype in. See GlobalState.
self.proto = proto
self.impl = impl
@@ -227,11 +258,13 @@ class UtilityCode(UtilityCodeBase):
self.specialize_list = []
self.proto_block = proto_block
self.name = name
+ self.file = file
def get_tree(self):
pass
- def specialize(self, pyrex_type=None, **data):
+
+ def specialize(self, pyrex_type=None, tempita=False, **data):
# Dicts aren't hashable...
if pyrex_type is not None:
data['type'] = pyrex_type.declaration_code('')
@@ -244,12 +277,15 @@ class UtilityCode(UtilityCodeBase):
requires = None
else:
requires = [r.specialize(data) for r in self.requires]
+
s = self._cache[key] = UtilityCode(
- none_or_sub(self.proto, data),
- none_or_sub(self.impl, data),
- none_or_sub(self.init, data),
- none_or_sub(self.cleanup, data),
- requires, self.proto_block)
+ self.none_or_sub(self.proto, data, tempita),
+ self.none_or_sub(self.impl, data, tempita),
+ self.none_or_sub(self.init, data, tempita),
+ self.none_or_sub(self.cleanup, data, tempita),
+ requires,
+ self.proto_block)
+
self.specialize_list.append(s)
return s
@@ -275,6 +311,29 @@ class UtilityCode(UtilityCodeBase):
self.cleanup(writer, output.module_pos)
+class ContentHashingUtilityCode(UtilityCode):
+ "UtilityCode that hashes and compares based on self.proto and self.impl"
+
+ def __hash__(self):
+ return hash((self.proto, self.impl))
+
+ def __eq__(self, other):
+ return (self.proto, self.impl) == (other.proto, other.impl)
+
+
+class LazyUtilityCode(UtilityCodeBase):
+ """
+ Utility code that calls a callback with the root code writer when
+ available. Useful when you only have 'env' but not 'code'.
+ """
+
+ def __init__(self, callback):
+ self.callback = callback
+
+ def put_code(self, globalstate):
+ utility = self.callback(globalstate.rootwriter)
+ globalstate.use_utility_code(utility)
+
class FunctionState(object):
# return_label string function return point label
@@ -1538,6 +1597,15 @@ class CCodeWriter(object):
for entry in entries:
self.put_var_xdecref_clear(entry)
+ def put_incref_memoryviewslice(self, slice_cname, have_gil=False):
+ self.putln("__PYX_INC_MEMVIEW(&%s, %d);" % (slice_cname, int(have_gil)))
+
+ def put_xdecref_memoryviewslice(self, slice_cname, have_gil=False):
+ self.putln("__PYX_XDEC_MEMVIEW(&%s, %d);" % (slice_cname, int(have_gil)))
+
+ def put_xgiveref_memoryviewslice(self, slice_cname):
+ self.put_xgiveref("%s.memview" % slice_cname)
+
def put_init_to_py_none(self, cname, type, nanny=True):
from PyrexTypes import py_object_type, typecast
py_none = typecast(type, py_object_type, "Py_None")
diff --git a/Cython/Compiler/CythonScope.py b/Cython/Compiler/CythonScope.py
index d1c404ec5..2e3cc44a7 100644
--- a/Cython/Compiler/CythonScope.py
+++ b/Cython/Compiler/CythonScope.py
@@ -10,10 +10,12 @@ import MemoryView
class CythonScope(ModuleScope):
is_cython_builtin = 1
- def __init__(self):
+ def __init__(self, context):
ModuleScope.__init__(self, u'cython', None, None)
self.pxd_file_loaded = True
self.populate_cython_scope()
+ # The Main.Context object
+ self.context = context
def lookup_type(self, name):
# This function should go away when types are all first-level objects.
@@ -80,6 +82,10 @@ class CythonScope(ModuleScope):
# The view sub-scope
#
self.viewscope = viewscope = ModuleScope(u'cython.view', self, None)
+
+ # Hacky monkey patch
+ self.viewscope.global_scope = self.global_scope
+
self.declare_module('view', viewscope, None)
viewscope.is_cython_builtin = True
viewscope.pxd_file_loaded = True
@@ -93,7 +99,7 @@ def create_cython_scope(context, create_testscope):
# One could in fact probably make it a singleton,
# but not sure yet whether any code mutates it (which would kill reusing
# it across different contexts)
- scope = CythonScope()
+ scope = CythonScope(context)
if create_testscope:
scope.test_cythonscope()
@@ -129,6 +135,12 @@ cython_test_extclass_utility_code = \
cythonview_testscope_utility_code = load_testscope_utility("View.TestScope")
view_utility_code = MemoryView.load_memview_cy_utility(
- "View.MemoryView", requires=(Buffer.GetAndReleaseBufferUtilityCode(),))
-
-cython_array_utility_code = MemoryView.load_memview_cy_utility("CythonArray")
+ "View.MemoryView", context=MemoryView.context,
+ requires=[Buffer.GetAndReleaseBufferUtilityCode(),
+ MemoryView.memviewslice_declare_code],
+)
+
+cython_array_utility_code = MemoryView.load_memview_cy_utility(
+ "CythonArray",
+ context=MemoryView.context,
+ requires=[view_utility_code])
diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py
index fb7ab564e..ab6dc7036 100755
--- a/Cython/Compiler/ExprNodes.py
+++ b/Cython/Compiler/ExprNodes.py
@@ -587,7 +587,12 @@ class ExprNode(Node):
if dst_type.is_memoryviewslice:
import MemoryView
if not src.type.is_memoryviewslice:
- src = CoerceToMemViewSliceNode(src, dst_type, env)
+ if src.type.is_pyobject:
+ src = CoerceToMemViewSliceNode(src, dst_type, env)
+ else:
+ error(self.pos,
+ "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))
@@ -1282,6 +1287,7 @@ class NameNode(AtomicExprNode):
# cf_is_null boolean Is uninitialized before this node
# cf_maybe_null boolean Maybe uninitialized before this node
# allow_null boolean Don't raise UnboundLocalError
+ # nogil boolean Whether it is used in a nogil context
is_name = True
is_cython_module = False
@@ -1293,6 +1299,7 @@ class NameNode(AtomicExprNode):
cf_maybe_null = True
cf_is_null = False
allow_null = False
+ nogil = False
def create_analysed_rvalue(pos, env, entry):
node = NameNode(pos)
@@ -1452,6 +1459,7 @@ class NameNode(AtomicExprNode):
self.is_used_as_rvalue = 1
def nogil_check(self, env):
+ self.nogil = True
if self.is_used_as_rvalue:
entry = self.entry
if entry.is_builtin:
@@ -1655,9 +1663,16 @@ class NameNode(AtomicExprNode):
else:
if self.type.is_memoryviewslice:
import MemoryView
- MemoryView.gen_acquire_memoryviewslice(rhs, self.type,
+ MemoryView.put_acquire_memoryviewslice(rhs, self.type,
self.entry.is_cglobal, self.result(), self.pos, code)
- # self.generate_acquire_memoryviewslice(rhs, code)
+
+ if isinstance(rhs, CoerceToMemViewSliceNode):
+ # We had a new reference, the lhs now has another,
+ # dispose of ours.
+ # code.put_xdecref_memoryviewslice(rhs.result())
+ code.put_decref("%s.memview" % rhs.result(),
+ cython_memoryview_ptr_type,
+ nanny=False)
elif self.type.is_buffer:
# Generate code for doing the buffer release/acquisition.
@@ -1736,12 +1751,17 @@ class NameNode(AtomicExprNode):
'__Pyx_DelAttrString(%s, "%s")' % (
Naming.module_cname,
self.entry.name))
- elif self.entry.type.is_pyobject:
+ elif self.entry.type.is_pyobject or self.entry.type.is_memoryviewslice:
if not self.cf_is_null:
if self.cf_maybe_null:
code.put_error_if_unbound(self.pos, self.entry)
- code.put_decref(self.result(), self.ctype())
- code.putln('%s = NULL;' % self.result())
+
+ if self.entry.type.is_pyobject:
+ code.put_decref(self.result(), self.ctype())
+ code.putln('%s = NULL;' % self.result())
+ else:
+ code.put_xdecref_memoryviewslice(self.entry.cname,
+ have_gil=not self.nogil)
else:
error(self.pos, "Deletion of C names not supported")
@@ -2347,10 +2367,12 @@ class IndexNode(ExprNode):
buffer_access = False
memoryviewslice_access = False
- if (self.base.type.is_memoryviewslice and not self.indices and
+ is_memslice = self.base.type.is_memoryviewslice
+
+ if (is_memslice and not self.indices and
isinstance(self.index, EllipsisNode)):
memoryviewslice_access = True
- elif self.base.type.is_buffer or self.base.type.is_memoryviewslice:
+ elif self.base.type.is_buffer or is_memslice:
if self.indices:
indices = self.indices
else:
@@ -2358,6 +2380,7 @@ class IndexNode(ExprNode):
indices = self.index.args
else:
indices = [self.index]
+
if len(indices) == self.base.type.ndim:
buffer_access = True
skip_child_analysis = True
@@ -2365,14 +2388,9 @@ class IndexNode(ExprNode):
x.analyse_types(env)
if not x.type.is_int:
buffer_access = False
+
if buffer_access:
assert hasattr(self.base, "entry") # Must be a NameNode-like node
-
-# if self.base.type.is_memoryviewslice:
-# assert hasattr(self.base, "entry")
-# if self.indices or not isinstance(self.index, EllipsisNode):
-# error(self.pos, "Memoryviews currently support ellipsis indexing only.")
-# else: memoryviewslice_access = True
# On cloning, indices is cloned. Otherwise, unpack index into indices
assert not (buffer_access and isinstance(self.index, CloneNode))
@@ -2386,7 +2404,10 @@ class IndexNode(ExprNode):
if getting and self.type.is_pyobject:
self.is_temp = True
- if setting:
+
+ if setting and self.base.type.is_memoryviewslice:
+ self.type.writable_needed = True
+ elif setting:
if not self.base.entry.type.writable:
error(self.pos, "Writing to readonly buffer")
else:
@@ -3990,8 +4011,8 @@ class AttributeNode(ExprNode):
import MemoryView
MemoryView.put_assign_to_memviewslice(select_code, rhs.result(), self.type,
pos=self.pos, code=code)
- if rhs.is_temp:
- code.put_xdecref_clear("%s.memview" % rhs.result(), cython_memoryview_ptr_type)
+ #if rhs.is_temp:
+ # code.put_xdecref_clear("%s.memview" % rhs.result(), cython_memoryview_ptr_type)
if not self.type.is_memoryviewslice:
code.putln(
"%s = %s;" % (
@@ -7647,46 +7668,14 @@ class CoerceToMemViewSliceNode(CoercionNode):
self.type = dst_type
self.env = env
self.is_temp = 1
+ self.arg = arg
def generate_result_code(self, code):
- import MemoryView, Buffer
- memviewobj = code.funcstate.allocate_temp(PyrexTypes.py_object_type, manage_ref=True)
- buf_flag = MemoryView.get_buf_flag(self.type.axes)
- code.putln("%s = (PyObject *) __pyx_memoryview_new(%s, %s);" %
- (memviewobj, self.arg.py_result(), buf_flag))
- code.putln(code.error_goto_if_PyErr(self.pos))
- ndim = len(self.type.axes)
- spec_int_arr = code.funcstate.allocate_temp(
- PyrexTypes.c_array_type(PyrexTypes.c_int_type, ndim),
- manage_ref=False)
- specs_code = MemoryView.specs_to_code(self.type.axes)
- for idx, cspec in enumerate(specs_code):
- code.putln("%s[%d] = %s;" % (spec_int_arr, idx, cspec))
-
- code.globalstate.use_utility_code(Buffer.acquire_utility_code)
- code.globalstate.use_utility_code(MemoryView.memviewslice_init_code)
- dtype_typeinfo = Buffer.get_type_information_cname(code, self.type.dtype)
-
- MemoryView.put_init_entry(self.result(), code)
- code.putln("{")
- code.putln("__Pyx_BufFmt_StackElem __pyx_stack[%d];" %
- self.type.dtype.struct_nesting_depth())
- result = self.result()
- if self.type.is_c_contig:
- c_or_f_flag = "__Pyx_IS_C_CONTIG"
- elif self.type.is_f_contig:
- c_or_f_flag = "__Pyx_IS_F_CONTIG"
- else:
- c_or_f_flag = "0"
- code.putln(code.error_goto_if("-1 == __Pyx_ValidateAndInit_memviewslice("
- "(struct __pyx_memoryview_obj *) %(memviewobj)s,"
- " %(spec_int_arr)s, %(c_or_f_flag)s, %(ndim)d,"
- " &%(dtype_typeinfo)s, __pyx_stack, &%(result)s)" % locals(), self.pos))
- code.putln("}")
- code.put_gotref(
- code.as_pyobject("%s.memview" % self.result(), cython_memoryview_ptr_type))
- code.funcstate.release_temp(memviewobj)
- code.funcstate.release_temp(spec_int_arr)
+ self.type.create_from_py_utility_code(self.env)
+ code.putln("%s = %s(%s);" % (self.result(),
+ self.type.from_py_function,
+ self.arg.py_result()))
+
class CastNode(CoercionNode):
# Wrap a node in a C type cast.
diff --git a/Cython/Compiler/Main.py b/Cython/Compiler/Main.py
index 0f8da7504..2f5da8b88 100644
--- a/Cython/Compiler/Main.py
+++ b/Cython/Compiler/Main.py
@@ -430,7 +430,7 @@ def create_default_resultobj(compilation_source, options):
def run_pipeline(source, options, full_module_name = None):
import Pipeline
- # Set up context
+
context = options.create_context()
# Set up source object
@@ -438,6 +438,7 @@ def run_pipeline(source, options, full_module_name = None):
abs_path = os.path.abspath(source)
source_ext = os.path.splitext(source)[1]
full_module_name = full_module_name or context.extract_module_name(source, options)
+
if options.relative_path_in_code_position_comments:
rel_path = full_module_name.replace('.', os.sep) + source_ext
if not abs_path.endswith(rel_path):
@@ -520,7 +521,7 @@ class CompilationOptions(object):
def create_context(self):
return Context(self.include_path, self.compiler_directives,
- self.cplus, self.language_level, options=self)
+ self.cplus, self.language_level, options=self)
class CompilationResult(object):
@@ -584,7 +585,8 @@ def compile_multiple(sources, options):
a CompilationResultSet. Performs timestamp checking and/or recursion
if these are specified in the options.
"""
- context = options.create_context()
+ # run_pipeline creates the context
+ # context = options.create_context()
sources = [os.path.abspath(source) for source in sources]
processed = set()
results = CompilationResultSet()
diff --git a/Cython/Compiler/MemoryView.py b/Cython/Compiler/MemoryView.py
index 5ebfabd85..c0d45f27b 100644
--- a/Cython/Compiler/MemoryView.py
+++ b/Cython/Compiler/MemoryView.py
@@ -46,17 +46,21 @@ _spec_to_const = {
'follow' : MEMVIEW_FOLLOW,
}
+_spec_to_abbrev = {
+ 'direct' : 'd',
+ 'ptr' : 'p',
+ 'full' : 'f',
+ 'contig' : 'c',
+ 'strided' : 's',
+ 'follow' : '_',
+}
+
memview_name = u'memoryview'
memview_typeptr_cname = '__pyx_memoryview_type'
memview_objstruct_cname = '__pyx_memoryview_obj'
memviewslice_cname = u'__Pyx_memviewslice'
-def specs_to_code(specs):
- arr = []
- for access, packing in specs:
- arr.append("(%s | %s)" % (_spec_to_const[access],
- _spec_to_const[packing]))
- return arr
+
def put_init_entry(mv_cname, code):
code.putln("%s.data = NULL;" % mv_cname)
@@ -70,7 +74,7 @@ def mangle_dtype_name(dtype):
def axes_to_str(axes):
return "".join([access[0].upper()+packing[0] for (access, packing) in axes])
-def gen_acquire_memoryviewslice(rhs, lhs_type, lhs_is_cglobal, lhs_result, lhs_pos, code):
+def put_acquire_memoryviewslice(rhs, lhs_type, lhs_is_cglobal, lhs_result, lhs_pos, code):
# import MemoryView
assert rhs.type.is_memoryviewslice
@@ -80,32 +84,17 @@ def gen_acquire_memoryviewslice(rhs, lhs_type, lhs_is_cglobal, lhs_result, lhs_p
else:
rhstmp = code.funcstate.allocate_temp(lhs_type, manage_ref=False)
code.putln("%s = %s;" % (rhstmp, rhs.result_as(lhs_type)))
- code.putln(code.error_goto_if_null("%s.memview" % rhstmp, lhs_pos))
-
- if not rhs.result_in_temp():
- code.put_incref("%s.memview" % rhstmp, cython_memoryview_ptr_type)
-
- if lhs_is_cglobal:
- code.put_gotref("%s.memview" % lhs_result)
-
- #XXX: this is here because self.lhs_of_first_assignment is not set correctly,
- # once that is working this should take that flag into account.
- # See NameNode.generate_assignment_code
- code.put_xdecref("%s.memview" % lhs_result, cython_memoryview_ptr_type)
-
- if lhs_is_cglobal:
- code.put_giveref("%s.memview" % rhstmp)
+ code.putln(code.error_goto_if_null("%s.memview" % rhstmp, lhs_pos))
put_assign_to_memviewslice(lhs_result, rhstmp, lhs_type,
lhs_pos, code=code)
- if rhs.result_in_temp() or not pretty_rhs:
- code.putln("%s.memview = 0;" % rhstmp)
-
if not pretty_rhs:
code.funcstate.release_temp(rhstmp)
def put_assign_to_memviewslice(lhs_cname, rhs_cname, memviewslicetype, pos, code):
+ code.put_xdecref_memoryviewslice(lhs_cname)
+ code.put_incref_memoryviewslice(rhs_cname)
code.putln("%s.memview = %s.memview;" % (lhs_cname, rhs_cname))
code.putln("%s.data = %s.data;" % (lhs_cname, rhs_cname))
@@ -195,6 +184,45 @@ class MemoryViewSliceBufferEntry(Buffer.BufferEntry):
def get_buf_shapevars(self):
return self._for_all_ndim("%s.shape[%d]")
+ def generate_buffer_lookup_code(self, code, index_cnames):
+ bufp = self.buf_ptr
+ type_decl = self.type.dtype.declaration_code("")
+
+ for dim, (access, packing) in enumerate(self.type.axes):
+ shape = "%s.shape[%d]" % (self.cname, dim)
+ stride = "%s.strides[%d]" % (self.cname, dim)
+ suboffset = "%s.suboffsets[%d]" % (self.cname, dim)
+ index = index_cnames[dim]
+
+ if access == 'full' and packing in ('strided', 'follow'):
+ code.globalstate.use_utility_code(memviewslice_index_helpers)
+ bufp = ('__pyx_memviewslice_index_full(%s, %s, %s)' %
+ (bufp, index, stride, suboffset))
+
+ elif access == 'full' and packing == 'contig':
+ # 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'):
+ bufp = ("(*((char **) %s + %s * %s) + %s)" %
+ (bufp, index, stride, suboffset))
+
+ elif access == 'ptr' and packing == 'contig':
+ bufp = "(*((char **) %s) + %s)" % (bufp, suboffset)
+
+ elif access == 'direct' and packing in ('strided', 'follow'):
+ bufp = "(%s + %s * %s)" % (bufp, index, stride)
+
+ else:
+ assert (access, packing) == ('direct', 'contig'), (access, packing)
+ bufp = '((char *) (((%s *) %s) + %s))' % (type_decl, bufp, index)
+
+ bufp = '( /* dim=%d */ %s )' % (dim, bufp)
+
+ return "((%s *) %s)" % (type_decl, bufp)
def get_copy_func_name(to_memview):
base = "__Pyx_BufferNew_%s_From_%s_%s"
@@ -370,10 +398,10 @@ class CopyFuncUtilCode(object):
if self.to_memview.is_c_contig:
mode = 'c'
- contig_flag = 'PyBUF_C_CONTIGUOUS'
+ contig_flag = memview_c_contiguous
elif self.to_memview.is_f_contig:
mode = 'fortran'
- contig_flag = "PyBUF_F_CONTIGUOUS"
+ contig_flag = memview_f_contiguous
context = dict(
copy_name=self.copy_func_name,
@@ -735,11 +763,13 @@ class MemoryViewSliceTransform(CythonTransform):
return node
-def load_memview_cy_utility(util_code_name, **kwargs):
- return CythonUtilityCode.load(util_code_name, "MemoryView.pyx", **kwargs)
+def load_memview_cy_utility(util_code_name, context=None, **kwargs):
+ return CythonUtilityCode.load(util_code_name, "MemoryView.pyx",
+ context=context, **kwargs)
-def load_memview_c_utility(util_code_name, **kwargs):
- return UtilityCode.load(util_code_name, "MemoryView_C.c", **kwargs)
+def load_memview_c_utility(util_code_name, context=None, **kwargs):
+ return UtilityCode.load(util_code_name, "MemoryView_C.c",
+ context=context, **kwargs)
context = {
'memview_struct_name': memview_objstruct_cname,
@@ -753,6 +783,8 @@ memviewslice_declare_code = load_memview_c_utility(
memviewslice_init_code = load_memview_c_utility(
"MemviewSliceInit",
- context={'BUF_MAX_NDIMS': Options.buffer_max_dims},
+ context=dict(context, BUF_MAX_NDIMS=Options.buffer_max_dims),
requires=[memviewslice_declare_code],
)
+
+memviewslice_index_helpers = load_memview_c_utility("MemviewSliceIndex") \ No newline at end of file
diff --git a/Cython/Compiler/ModuleNode.py b/Cython/Compiler/ModuleNode.py
index 715a6e9de..09b99e0bf 100644
--- a/Cython/Compiler/ModuleNode.py
+++ b/Cython/Compiler/ModuleNode.py
@@ -79,6 +79,10 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
scope.python_include_files)
if merge_scope:
+ # Ensure that we don't generate import code for these entries!
+ for entry in scope.c_class_entries:
+ entry.type.module_name = self.full_module_name
+
self.scope.merge_in(scope)
def analyse_declarations(self, env):
@@ -345,7 +349,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
self.generate_declarations_for_modules(env, modules, globalstate)
h_code.write('\n')
- for utilcode in env.utility_code_list:
+ for utilcode in env.utility_code_list[:]:
globalstate.use_utility_code(utilcode)
globalstate.finalize_main_c_code()
@@ -2255,7 +2259,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
# Generate type import code for extern extension types
# and type ready code for non-extern ones.
for entry in env.c_class_entries:
- if entry.visibility == 'extern':
+ if entry.visibility == 'extern' and not entry.utility_code_definition:
self.generate_type_import_code(env, entry.type, entry.pos, code)
else:
self.generate_base_type_import_code(env, entry, code)
@@ -2265,8 +2269,8 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
def generate_base_type_import_code(self, env, entry, code):
base_type = entry.type.base_type
- if base_type and base_type.module_name != env.qualified_name \
- and not base_type.is_builtin_type:
+ if (base_type and base_type.module_name != env.qualified_name and not
+ base_type.is_builtin_type and not entry.utility_code_definition):
self.generate_type_import_code(env, base_type, self.pos, code)
def use_type_import_utility_code(self, env):
diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py
index db30178bd..6eb0e62fa 100644
--- a/Cython/Compiler/Nodes.py
+++ b/Cython/Compiler/Nodes.py
@@ -1344,17 +1344,19 @@ class FuncDefNode(StatNode, BlockNode):
if not entry.in_closure:
code.put_var_declaration(entry)
+ # Initialize the return variable __pyx_r
init = ""
if not self.return_type.is_void:
if self.return_type.is_pyobject:
init = " = NULL"
+ elif self.return_type.is_memoryviewslice:
+ init = "= {0, 0}"
+
code.putln(
"%s%s;" %
(self.return_type.declaration_code(Naming.retval_cname),
init))
- if self.return_type.is_memoryviewslice:
- import MemoryView
- MemoryView.put_init_entry(Naming.retval_cname, code)
+
tempvardecl_code = code.insertion_point()
self.generate_keyword_list(code)
@@ -1431,15 +1433,18 @@ class FuncDefNode(StatNode, BlockNode):
if (acquire_gil or entry.assignments) and not entry.in_closure:
code.put_var_incref(entry)
if entry.type.is_memoryviewslice:
- code.put_incref("%s.memview" % entry.cname, cython_memoryview_ptr_type)
+ code.put_incref_memoryviewslice(entry.cname,
+ have_gil=not lenv.nogil)
+ #code.put_incref("%s.memview" % entry.cname, cython_memoryview_ptr_type)
# ----- 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:
Buffer.put_init_vars(entry, code)
# ----- Initialise local memoryviewslices
for entry in lenv.var_entries:
- if entry.type.is_memoryviewslice:
- MemoryView.put_init_entry(entry.cname, code)
+ if entry.visibility == "private" and not entry.used:
+ continue
+
# ----- Check and convert arguments
self.generate_argument_type_tests(code)
# ----- Acquire buffer arguments
@@ -1544,7 +1549,8 @@ class FuncDefNode(StatNode, BlockNode):
if not entry.used or entry.in_closure:
continue
if entry.type.is_memoryviewslice:
- code.put_xdecref("%s.memview" % entry.cname, cython_memoryview_ptr_type)
+ #code.put_xdecref("%s.memview" % entry.cname, cython_memoryview_ptr_type)
+ code.put_xdecref_memoryviewslice(entry.cname)
if entry.type.is_pyobject:
code.put_var_decref(entry)
@@ -1554,7 +1560,8 @@ class FuncDefNode(StatNode, BlockNode):
if (acquire_gil or entry.assignments) and not entry.in_closure:
code.put_var_decref(entry)
if entry.type.is_memoryviewslice:
- code.put_decref("%s.memview" % entry.cname, cython_memoryview_ptr_type)
+ code.put_xdecref_memoryviewslice(entry.cname)
+ #code.put_decref("%s.memview" % entry.cname, cython_memoryview_ptr_type)
if self.needs_closure:
code.put_decref(Naming.cur_scope_cname, lenv.scope_class.type)
@@ -1567,8 +1574,9 @@ class FuncDefNode(StatNode, BlockNode):
err_val = default_retval
if self.return_type.is_pyobject:
code.put_xgiveref(self.return_type.as_pyobject(Naming.retval_cname))
- elif self.return_type.is_memoryviewslice:
- code.put_xgiveref(code.as_pyobject("%s.memview" % Naming.retval_cname,cython_memoryview_ptr_type))
+ #elif self.return_type.is_memoryviewslice:
+ # code.put_xgiveref(code.as_pyobject("%s.memview" % Naming.retval_cname,cython_memoryview_ptr_type))
+ # code.put_xgiveref_memoryviewslice(Naming.retval_cname)
if self.entry.is_special and self.entry.name == "__hash__":
# Returning -1 for __hash__ is supposed to signal an error
@@ -4153,7 +4161,8 @@ class DelStatNode(StatNode):
def analyse_expressions(self, env):
for arg in self.args:
arg.analyse_target_expression(env, None)
- if arg.type.is_pyobject:
+ if arg.type.is_pyobject or (arg.is_name and
+ arg.type.is_memoryviewslice):
pass
elif arg.type.is_ptr and arg.type.base_type.is_cpp_class:
self.cpp_check(env)
@@ -4172,7 +4181,7 @@ class DelStatNode(StatNode):
def generate_execution_code(self, code):
for arg in self.args:
- if arg.type.is_pyobject:
+ if arg.type.is_pyobject or arg.type.is_memoryviewslice:
arg.generate_deletion_code(code)
elif arg.type.is_ptr and arg.type.base_type.is_cpp_class:
arg.generate_result_code(code)
@@ -4274,14 +4283,15 @@ class ReturnStatNode(StatNode):
code.put_xdecref(Naming.retval_cname,
self.return_type)
elif self.return_type.is_memoryviewslice:
- code.put_xdecref("%s.memview" % Naming.retval_cname,
- self.return_type)
+ code.put_xdecref_memoryviewslice(Naming.retval_cname)
+ #code.put_xdecref("%s.memview" % Naming.retval_cname,
+ # self.return_type)
if self.value:
self.value.generate_evaluation_code(code)
if self.return_type.is_memoryviewslice:
import MemoryView
- MemoryView.gen_acquire_memoryviewslice(self.value, self.return_type,
+ MemoryView.put_acquire_memoryviewslice(self.value, self.return_type,
False, Naming.retval_cname, None, code)
else:
self.value.make_owned_reference(code)
diff --git a/Cython/Compiler/PyrexTypes.py b/Cython/Compiler/PyrexTypes.py
index 0144414bd..a42a13573 100755
--- a/Cython/Compiler/PyrexTypes.py
+++ b/Cython/Compiler/PyrexTypes.py
@@ -2,12 +2,14 @@
# Cython/Python language types
#
-from Code import UtilityCode
+from Code import UtilityCode, LazyUtilityCode
import StringEncoding
import Naming
import copy
from Errors import error
+import cython
+
class BaseType(object):
#
# Base class for all Cython types including pseudo-types.
@@ -331,6 +333,15 @@ class MemoryViewSliceType(PyrexType):
has_attributes = 1
scope = None
+ # These are specialcased in Defnode
+ from_py_function = None
+ to_py_function = None
+
+ exception_value = None
+ exception_check = None
+
+ utility_counter = 0
+
def __init__(self, base_dtype, axes):
'''
MemoryViewSliceType(base, axes)
@@ -375,6 +386,7 @@ class MemoryViewSliceType(PyrexType):
assert not (self.is_c_contig and self.is_f_contig)
self.mode = MemoryView.get_mode(axes)
+ self.writable_needed = False
def same_as_resolved_type(self, other_type):
return ((other_type.is_memoryviewslice and
@@ -495,11 +507,68 @@ class MemoryViewSliceType(PyrexType):
def global_init_code(self, entry, code):
code.putln("%s.data = NULL;" % entry.cname)
- code.put_init_to_py_none("%s.memview" % entry.cname, cython_memoryview_ptr_type, nanny=False)
+ 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):
return cname + '.memview'
+ def create_from_py_utility_code(self, env):
+ import MemoryView, Buffer, Code
+
+ # We don't have 'code', so use a LazyUtilityCode with a callback.
+ def lazy_utility_callback(code):
+ context['dtype_typeinfo'] = Buffer.get_type_information_cname(
+ code, self.dtype)
+ return Code.ContentHashingUtilityCode.load(
+ "ObjectToMemviewSlice", "MemoryView_C.c", context)
+
+ env.use_utility_code(Buffer.acquire_utility_code)
+ env.use_utility_code(MemoryView.memviewslice_init_code)
+ env.use_utility_code(LazyUtilityCode(lazy_utility_callback))
+
+ if self.is_c_contig:
+ c_or_f_flag = "__Pyx_IS_C_CONTIG"
+ elif self.is_f_contig:
+ c_or_f_flag = "__Pyx_IS_F_CONTIG"
+ 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
+
+ context = dict(
+ MemoryView.context,
+ buf_flag = MemoryView.get_buf_flag(self.axes),
+ ndim = self.ndim,
+ axes_specs = ', '.join(self.axes_specs_to_code()),
+ dtype_typedecl = self.dtype.declaration_code(""),
+ struct_nesting_depth = self.dtype.struct_nesting_depth(),
+ c_or_f_flag = c_or_f_flag,
+ funcname = funcname,
+ )
+
+ self.from_py_function = funcname
+ MemoryViewSliceType.utility_counter += 1
+
+ return True
+
+ def axes_specs_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):
+ "Return an abbreviated name for our axes"
+ import MemoryView
+ d = MemoryView._spec_to_abbrev
+ return "".join(["%s%s" % (d[a], d[p]) for a, p in self.axes])
+
+ def error_condition(self, result_code):
+ return "!%s.memview" % result_code
+
class BufferType(BaseType):
#
@@ -2698,8 +2767,8 @@ cython_memoryview_type = CStructOrUnionType("__pyx_memoryview_obj", "struct",
cython_memoryview_ptr_type = CPtrType(cython_memoryview_type)
-memoryviewslice_type = CStructOrUnionType("__Pyx_memviewslice", "struct",
- None, 1, "__Pyx_memviewslice")
+memoryviewslice_type = CStructOrUnionType("memoryviewslice", "struct",
+ None, 1, "__Pyx_memviewslice")
error_type = ErrorType()
unspecified_type = UnspecifiedType()
diff --git a/Cython/Compiler/Symtab.py b/Cython/Compiler/Symtab.py
index 71a31fc7c..f35bdb81d 100644
--- a/Cython/Compiler/Symtab.py
+++ b/Cython/Compiler/Symtab.py
@@ -382,6 +382,10 @@ class Scope(object):
# entries[name] = entry
if not shadow:
entries[name] = entry
+
+ if type.is_memoryviewslice:
+ entry.init = "{ 0, 0 }"
+
entry.scope = self
entry.visibility = visibility
return entry
diff --git a/Cython/Compiler/UtilityCode.py b/Cython/Compiler/UtilityCode.py
index 685b229be..3329a7966 100644
--- a/Cython/Compiler/UtilityCode.py
+++ b/Cython/Compiler/UtilityCode.py
@@ -15,7 +15,7 @@ class NonManglingModuleScope(Symtab.ModuleScope):
entry.used = True
return super(NonManglingModuleScope, self).add_imported_entry(
name, entry, pos)
-
+
def mangle(self, prefix, name=None):
if name:
if prefix in (Naming.typeobj_prefix, Naming.func_prefix, Naming.var_prefix, Naming.pyfunc_prefix):
@@ -63,7 +63,8 @@ class CythonUtilityCode(Code.UtilityCodeBase):
is_cython_utility = True
- def __init__(self, impl, name="__pyxutil", prefix="", requires=None):
+ def __init__(self, impl, name="__pyxutil", prefix="", requires=None,
+ file=None):
# 1) We need to delay the parsing/processing, so that all modules can be
# imported without import loops
# 2) The same utility code object can be used for multiple source files;
@@ -72,6 +73,7 @@ class CythonUtilityCode(Code.UtilityCodeBase):
# Hence, delay any processing until later.
self.impl = impl
self.name = name
+ self.file = file
self.prefix = prefix
self.requires = requires or []
@@ -113,10 +115,11 @@ class CythonUtilityCode(Code.UtilityCodeBase):
def put_code(self, output):
pass
- def declare_in_scope(self, dest_scope, used=False):
+ def declare_in_scope(self, dest_scope, used=False, modname=None):
"""
Declare all entries from the utility code in dest_scope. Code will only
- be included for used entries.
+ be included for used entries. If module_name is given, declare the
+ type entries with that name.
"""
tree = self.get_tree(entries_only=True)
@@ -130,6 +133,10 @@ class CythonUtilityCode(Code.UtilityCodeBase):
entry.utility_code_definition = self
entry.used = used
+ if modname and entry.type.is_extension_type:
+ entry.qualified_name = modname
+ entry.type.module_name = modname
+
dest_scope.merge_in(tree.scope, merge_unused=True)
tree.scope = dest_scope
diff --git a/Cython/Utility/MemoryView.pyx b/Cython/Utility/MemoryView.pyx
index 1266b5287..4df327ea2 100644
--- a/Cython/Utility/MemoryView.pyx
+++ b/Cython/Utility/MemoryView.pyx
@@ -10,7 +10,10 @@ cdef extern from "Python.h":
PyBUF_C_CONTIGUOUS,
PyBUF_F_CONTIGUOUS,
PyBUF_ANY_CONTIGUOUS
+ PyBUF_FORMAT
+cdef extern from *:
+ object __pyx_memoryview_new(object obj, int flags)
@cname("__pyx_array")
cdef class array:
@@ -105,7 +108,12 @@ cdef class array:
info.strides = self.strides
info.suboffsets = NULL
info.itemsize = self.itemsize
- info.format = self.format
+
+ if flags & PyBUF_FORMAT:
+ info.format = self.format
+ else:
+ info.format = NULL
+
# we do not need to call releasebuffer
info.obj = None
@@ -130,6 +138,15 @@ cdef class array:
self.format = NULL
self.itemsize = 0
+ def __getitem__(self, index):
+ view = __pyx_memoryview_new(self, PyBUF_ANY_CONTIGUOUS|PyBUF_FORMAT)
+ return view[index]
+
+ def __setitem__(self, index, value):
+ view = __pyx_memoryview_new(self, PyBUF_ANY_CONTIGUOUS|PyBUF_FORMAT)
+ view[index] = value
+
+
@cname("__pyx_array_new")
cdef array array_cwrapper(tuple shape, Py_ssize_t itemsize, char *format, char *mode):
return array(shape, itemsize, format, mode.decode('ASCII'))
@@ -137,8 +154,10 @@ cdef array array_cwrapper(tuple shape, Py_ssize_t itemsize, char *format, char *
########## View.MemoryView ##########
# from cpython cimport ...
-cdef extern from "pythread.h":
+cdef extern from "Python.h":
+ int PyIndex_Check(object)
+cdef extern from "pythread.h":
ctypedef void *PyThread_type_lock
PyThread_type_lock PyThread_allocate_lock()
@@ -150,6 +169,12 @@ cdef extern from *:
int __Pyx_GetBuffer(object, Py_buffer *, int)
void __Pyx_ReleaseBuffer(Py_buffer *)
+ ctypedef struct {{memviewslice_name}}:
+ char *data
+ Py_ssize_t shape[{{max_dims}}]
+ Py_ssize_t strides[{{max_dims}}]
+ Py_ssize_t suboffsets[{{max_dims}}]
+
@cname('__pyx_MemviewEnum')
cdef class Enum(object):
@@ -173,23 +198,143 @@ cdef class memoryview(object):
cdef object obj
cdef Py_buffer view
- cdef PyThread_type_lock acqcnt_lock
+ cdef PyThread_type_lock lock
cdef int acquisition_count
+
def __cinit__(memoryview self, object obj, int flags):
self.obj = obj
- #self.acqcnt_lock = PyThread_allocate_lock()
- #if self.acqcnt_lock == NULL:
- # raise MemoryError
-
__Pyx_GetBuffer(obj, &self.view, flags)
+ self.lock = PyThread_allocate_lock()
+ if self.lock == NULL:
+ raise MemoryError
+
def __dealloc__(memoryview self):
- #PyThread_free_lock(self.acqcnt_lock)
- self.obj = None
__Pyx_ReleaseBuffer(&self.view)
+ PyThread_free_lock(self.lock)
+
+ @cname('__pyx_memoryview_getitem')
+ def __getitem__(memoryview self, object index):
+ # cdef Py_ssize_t idx
+ cdef char *itemp = <char *> self.view.buf
+ cdef bytes bytesitem
+ cdef str fmt = self.view.format
+
+ import struct
+ try:
+ itemsize = struct.calcsize(fmt)
+ except struct.error:
+ raise TypeError("Unsupported format: %r" % fmt)
+
+ if index is Ellipsis:
+ return self
+
+ elif isinstance(index, slice):
+ if index == slice(None):
+ return self
+
+ raise NotImplementedError
+
+ else:
+ if not isinstance(index, tuple):
+ index = (index,)
+
+ tup = _unellipsify(index, self.view.ndim)
+
+ if len(tup) != self.view.ndim:
+ raise NotImplementedError(
+ "Expected %d indices (got %d)" %
+ (self.view.ndim, len(tup)))
+
+ for dim, idx in enumerate(tup):
+ _check_index(idx)
+ itemp = pybuffer_index(&self.view, itemp, idx, dim + 1)
+
+ # Do a manual and complete check here instead of this easy hack
+ bytesitem = itemp[:self.view.itemsize]
+ return struct.unpack(fmt, bytesitem)
+
+
+@cname('__pyx_memoryviewslice')
+cdef class _memoryviewslice(memoryview):
+ "Internal class for passing memory view slices to Python"
+
+ # We need this to keep our shape/strides/suboffset pointers valid
+ cdef {{memviewslice_name}} from_slice
+ # Restore the original Py_buffer before releasing
+ cdef Py_buffer orig_view
+
+ def __cinit__(self, object obj, int flags):
+ self.orig_view = self.view
+
+ def __dealloc__(self):
+ self.view = self.orig_view
@cname('__pyx_memoryview_new')
-cdef memoryview memoryview_cwrapper(object o, int flags):
+cdef memoryview_cwrapper(object o, int flags):
return memoryview(o, flags)
+
+@cname('__pyx_memoryview_fromslice')
+cdef memoryview memoryview_from_memview_cwrapper(memoryview m, int flags,
+ int new_ndim, {{memviewslice_name}} *memviewslice):
+ cdef _memoryviewslice result = _memoryviewslice(m.obj, flags)
+
+ result.from_slice = memviewslice[0]
+
+ result.view.shape = <Py_ssize_t *> (&result.from_slice.shape + new_ndim)
+ result.view.strides = <Py_ssize_t *> (&result.from_slice.strides + new_ndim)
+ result.view.suboffsets = <Py_ssize_t *> (&result.from_slice.suboffsets + new_ndim)
+ result.view.ndim = new_ndim
+
+ return result
+
+cdef _check_index(index):
+ if not PyIndex_Check(index):
+ raise TypeError("Cannot index with %s" % type(index))
+
+cdef tuple _unellipsify(tuple tup, int ndim):
+ if Ellipsis in tup:
+ result = []
+ for idx, item in enumerate(tup):
+ if item is Ellipsis:
+ result.extend([slice(None)] * (ndim - len(tup) + 1))
+ result.extend(tup[idx + 1:])
+ break
+
+ result.append(item)
+
+ return tuple(result)
+
+ return tup
+
+@cname('__pyx_pybuffer_index')
+cdef char *pybuffer_index(Py_buffer *view, char *bufp, Py_ssize_t index, int dim) except NULL:
+ cdef Py_ssize_t shape, stride, suboffset = -1
+ cdef Py_ssize_t itemsize = view.itemsize
+ cdef char *resultp
+
+ if view.ndim == 0:
+ shape = view.len / itemsize
+ stride = itemsize
+ else:
+ shape = view.shape[dim]
+ stride = view.strides[dim]
+ if view.suboffsets != NULL:
+ suboffset = view.suboffsets[dim]
+
+ if index < 0:
+ index += view.shape[dim]
+ if index < 0:
+ raise IndexError("Out of bounds in dimension %d" % dim)
+
+ if index > shape:
+ raise IndexError("Out of bounds in dimension %d" % dim)
+
+ resultp = bufp + index * stride
+
+ if suboffset >= 0:
+ resultp = (<char **> resultp)[0] + suboffset
+
+ return resultp
diff --git a/Cython/Utility/MemoryView_C.c b/Cython/Utility/MemoryView_C.c
index 9e949134b..54005eebd 100644
--- a/Cython/Utility/MemoryView_C.c
+++ b/Cython/Utility/MemoryView_C.c
@@ -12,6 +12,9 @@ typedef struct {
Py_ssize_t suboffsets[{{max_dims}}];
} {{memviewslice_name}};
+/////////////// ObjectToMemviewSlice.proto ///////////////
+{{# __Pyx_PyObject_to_MemoryviewSlice_<count> }}
+static CYTHON_INLINE {{memviewslice_name}} {{funcname}}(PyObject *);
////////// MemviewSliceInit.proto //////////
@@ -27,6 +30,7 @@ typedef struct {
#define __Pyx_IS_C_CONTIG 1
#define __Pyx_IS_F_CONTIG 2
+
/* #define __PYX_MEMSLICE_GETDATA(SLICE) ((char *) SLICE->memview->view->buf) */
static int __Pyx_ValidateAndInit_memviewslice(struct __pyx_memoryview_obj *memview,
@@ -38,6 +42,49 @@ static int __Pyx_init_memviewslice(
int ndim,
__Pyx_memviewslice *memviewslice);
+#define __PYX_INC_MEMVIEW(slice, have_gil) __Pyx_INC_MEMVIEW(slice, have_gil, __LINE__)
+#define __PYX_XDEC_MEMVIEW(slice, have_gil) __Pyx_XDEC_MEMVIEW(slice, have_gil, __LINE__)
+static CYTHON_INLINE void __Pyx_INC_MEMVIEW({{memviewslice_name}} *, int, int);
+static CYTHON_INLINE void __Pyx_XDEC_MEMVIEW({{memviewslice_name}} *, int, int);
+
+/////////////// MemviewSliceIndex.proto ///////////////
+
+static CYTHON_INLINE char *__pyx_memviewslice_index_full(char *bufp, Py_ssize_t idx, Py_ssize_t stride, Py_ssize_t suboffset);
+static CYTHON_INLINE char *__pyx_memviewslice_index_full_contig(char *bufp, Py_ssize_t suboffset);
+
+
+/////////////// ObjectToMemviewSlice ///////////////
+
+{{#__Pyx_PyObject_to_MemoryviewSlice_<count>}}
+
+static CYTHON_INLINE {{memviewslice_name}} {{funcname}}(PyObject *obj) {
+ {{memviewslice_name}} result;
+ result.memview = NULL;
+ result.data = NULL;
+ struct __pyx_memoryview_obj *memview = \
+ (struct __pyx_memoryview_obj *) __pyx_memoryview_new(obj, {{buf_flag}});
+ __Pyx_BufFmt_StackElem stack[{{struct_nesting_depth}}];
+ int axes_specs[] = { {{axes_specs}} };
+ int retcode;
+
+ if (unlikely(!memview))
+ goto __pyx_fail;
+
+ retcode = __Pyx_ValidateAndInit_memviewslice(memview, axes_specs,
+ {{c_or_f_flag}}, {{ndim}}, &{{dtype_typeinfo}}, stack, &result);
+
+ if (unlikely(retcode == -1))
+ goto __pyx_fail;
+
+ memview->acquisition_count = 1;
+ return result;
+__pyx_fail:
+ Py_XDECREF(memview);
+ result.memview = NULL;
+ result.data = NULL;
+ return result;
+}
+
////////// MemviewSliceInit //////////
static int __Pyx_ValidateAndInit_memviewslice(
@@ -203,8 +250,6 @@ static int __Pyx_init_memviewslice(
}
}
- __Pyx_INCREF((PyObject *)memview);
- __Pyx_GIVEREF((PyObject *)memview);
memviewslice->memview = memview;
memviewslice->data = (char *)buf->buf;
retval = 0;
@@ -220,6 +265,62 @@ no_fail:
return retval;
}
+static CYTHON_INLINE void __Pyx_INC_MEMVIEW({{memviewslice_name}} *memslice,
+ int have_gil, int lineno) {
+ int first_time;
+ struct {{memview_struct_name}} *memview = memslice->memview;
+ if (!memview) {
+ char msg[50];
+ snprintf(msg, 50, "memoryslice is not initialized (line %d)", lineno);
+ Py_FatalError(msg);
+ }
+
+ PyThread_acquire_lock(memview->lock, 1);
+ first_time = (memview->acquisition_count++ == 0);
+ PyThread_release_lock(memview->lock);
+
+ /* printf("INCREF %d: acquisition_count=%d, refcount=%d\n", lineno,
+ memview->acquisition_count, memview->ob_refcnt); */
+
+ if (first_time) {
+ if (have_gil) {
+ Py_INCREF((PyObject *) memview);
+ } else {
+ PyGILState_STATE _gilstate = PyGILState_Ensure();
+ Py_INCREF((PyObject *) memview);
+ PyGILState_Release(_gilstate);
+ }
+ }
+}
+
+static CYTHON_INLINE void __Pyx_XDEC_MEMVIEW({{memviewslice_name}} *memslice,
+ int have_gil, int lineno) {
+ int last_time;
+ struct {{memview_struct_name}} *memview = memslice->memview;
+
+ if (!memview) {
+ return;
+ }
+
+ PyThread_acquire_lock(memview->lock, 1);
+ last_time = (memview->acquisition_count-- == 1);
+ PyThread_release_lock(memview->lock);
+
+ /* printf("DECREF %d: acquisition_count=%d, refcount=%d\n", lineno,
+ memview->acquisition_count, memview->ob_refcnt); */
+
+ if (last_time) {
+ if (have_gil) {
+ Py_CLEAR(memview);
+ } else {
+ PyGILState_STATE _gilstate = PyGILState_Ensure();
+ Py_CLEAR(memview);
+ PyGILState_Release(_gilstate);
+ }
+ memslice->data = NULL;
+ }
+}
+
////////// MemviewSliceCopyTemplate //////////
static __Pyx_memviewslice {{copy_name}}(const __Pyx_memviewslice from_mvs) {
@@ -259,7 +360,8 @@ static __Pyx_memviewslice {{copy_name}}(const __Pyx_memviewslice from_mvs) {
}
__Pyx_GOTREF(array_obj);
- memview_obj = __pyx_memoryview_new((PyObject *) array_obj, {{contig_flag}});
+ memview_obj = (struct __pyx_memoryview_obj *) __pyx_memoryview_new(
+ (PyObject *) array_obj, {{contig_flag}});
if (unlikely(!memview_obj)) {
goto fail;
}
@@ -292,3 +394,21 @@ no_fail:
}
+/////////////// MemviewSliceIndex ///////////////
+
+static CYTHON_INLINE char *__pyx_memviewslice_index_full(char *bufp, Py_ssize_t idx, Py_ssize_t stride, Py_ssize_t suboffset) {
+ bufp = bufp + idx * stride;
+ if (suboffset >= 0) {
+ bufp = *((char **) bufp) + suboffset;
+ }
+ return bufp;
+}
+
+/* The call has already done the indexing */
+static CYTHON_INLINE char *__pyx_memviewslice_index_full_contig(char *bufp, Py_ssize_t suboffset) {
+ if (suboffset >= 0) {
+ bufp = *((char **) bufp) + suboffset;
+ }
+ return bufp;
+}
+
diff --git a/Cython/Utils.py b/Cython/Utils.py
index 5dc2d1282..a5dbea36a 100644
--- a/Cython/Utils.py
+++ b/Cython/Utils.py
@@ -4,6 +4,7 @@
#
import os, sys, re, codecs
+from Cython import Tempita
def replace_suffix(path, newsuf):
base, _ = os.path.splitext(path)
@@ -215,10 +216,3 @@ def long_literal(value):
if isinstance(value, basestring):
value = str_to_number(value)
return not -2**31 <= value < 2**31
-
-def none_or_sub(s, data):
- if s is None:
- return s
- else:
- return s % data
-
diff --git a/tests/run/bufaccess.pyx b/tests/run/bufaccess.pyx
index 3f639b142..895b1d4f0 100644
--- a/tests/run/bufaccess.pyx
+++ b/tests/run/bufaccess.pyx
@@ -9,13 +9,6 @@
from __future__ import unicode_literals
-from libc cimport stdlib
-from libc cimport stdio
-cimport cpython.buffer
-cimport cython
-
-from cpython cimport PyObject, Py_INCREF, Py_DECREF
-
__test__ = {}
import sys
@@ -33,9 +26,8 @@ def testcase(func):
__test__[func.__name__] = doctest
return func
-def testcas(a):
- pass
+include "mockbuffers.pxi"
#
# Buffer acquire and release tests
@@ -966,246 +958,6 @@ def buffer_cast_fails(object[char, cast=True] buf):
"""
return buf[0]
-
-#
-# Testcase support code (more tests below!, because of scope rules)
-#
-
-
-available_flags = (
- ('FORMAT', cpython.buffer.PyBUF_FORMAT),
- ('INDIRECT', cpython.buffer.PyBUF_INDIRECT),
- ('ND', cpython.buffer.PyBUF_ND),
- ('STRIDES', cpython.buffer.PyBUF_STRIDES),
- ('C_CONTIGUOUS', cpython.buffer.PyBUF_C_CONTIGUOUS),
- ('F_CONTIGUOUS', cpython.buffer.PyBUF_F_CONTIGUOUS),
- ('WRITABLE', cpython.buffer.PyBUF_WRITABLE)
-)
-
-cdef class MockBuffer:
- cdef object format, offset
- cdef void* buffer
- cdef int len, itemsize, ndim
- cdef Py_ssize_t* strides
- cdef Py_ssize_t* shape
- cdef Py_ssize_t* suboffsets
- cdef object label, log
-
- cdef readonly object recieved_flags, release_ok
- cdef public object fail
-
- def __init__(self, label, data, shape=None, strides=None, format=None, offset=0):
- # It is important not to store references to data after the constructor
- # as refcounting is checked on object buffers.
- self.label = label
- self.release_ok = True
- self.log = ""
- self.offset = offset
- self.itemsize = self.get_itemsize()
- if format is None: format = self.get_default_format()
- if shape is None: shape = (len(data),)
- if strides is None:
- strides = []
- cumprod = 1
- rshape = list(shape)
- rshape.reverse()
- for s in rshape:
- strides.append(cumprod)
- cumprod *= s
- strides.reverse()
- strides = [x * self.itemsize for x in strides]
- suboffsets = [-1] * len(shape)
- datashape = [len(data)]
- p = data
- while True:
- p = p[0]
- if isinstance(p, list): datashape.append(len(p))
- else: break
- if len(datashape) > 1:
- # indirect access
- self.ndim = len(datashape)
- shape = datashape
- self.buffer = self.create_indirect_buffer(data, shape)
- suboffsets = [0] * (self.ndim-1) + [-1]
- strides = [sizeof(void*)] * (self.ndim-1) + [self.itemsize]
- self.suboffsets = self.list_to_sizebuf(suboffsets)
- else:
- # strided and/or simple access
- self.buffer = self.create_buffer(data)
- self.ndim = len(shape)
- self.suboffsets = NULL
-
- try:
- format = format.encode('ASCII')
- except AttributeError:
- pass
- self.format = format
- self.len = len(data) * self.itemsize
-
- self.strides = self.list_to_sizebuf(strides)
- self.shape = self.list_to_sizebuf(shape)
-
- def __dealloc__(self):
- stdlib.free(self.strides)
- stdlib.free(self.shape)
- if self.suboffsets != NULL:
- stdlib.free(self.suboffsets)
- # must recursively free indirect...
- else:
- stdlib.free(self.buffer)
-
- cdef void* create_buffer(self, data):
- cdef size_t n = <size_t>(len(data) * self.itemsize)
- cdef char* buf = <char*>stdlib.malloc(n)
- cdef char* it = buf
- for value in data:
- self.write(it, value)
- it += self.itemsize
- return buf
-
- cdef void* create_indirect_buffer(self, data, shape):
- cdef size_t n = 0
- cdef void** buf
- assert shape[0] == len(data)
- if len(shape) == 1:
- return self.create_buffer(data)
- else:
- shape = shape[1:]
- n = <size_t>len(data) * sizeof(void*)
- buf = <void**>stdlib.malloc(n)
- for idx, subdata in enumerate(data):
- buf[idx] = self.create_indirect_buffer(subdata, shape)
- return buf
-
- cdef Py_ssize_t* list_to_sizebuf(self, l):
- cdef size_t n = <size_t>len(l) * sizeof(Py_ssize_t)
- cdef Py_ssize_t* buf = <Py_ssize_t*>stdlib.malloc(n)
- for i, x in enumerate(l):
- buf[i] = x
- return buf
-
- def __getbuffer__(MockBuffer self, Py_buffer* buffer, int flags):
- if self.fail:
- raise ValueError("Failing on purpose")
-
- self.recieved_flags = []
- cdef int value
- for name, value in available_flags:
- if (value & flags) == value:
- self.recieved_flags.append(name)
-
- buffer.buf = <void*>(<char*>self.buffer + (<int>self.offset * self.itemsize))
- buffer.obj = self
- buffer.len = self.len
- buffer.readonly = 0
- buffer.format = <char*>self.format
- buffer.ndim = self.ndim
- buffer.shape = self.shape
- buffer.strides = self.strides
- buffer.suboffsets = self.suboffsets
- buffer.itemsize = self.itemsize
- buffer.internal = NULL
- if self.label:
- msg = "acquired %s" % self.label
- print msg
- self.log += msg + "\n"
-
- def __releasebuffer__(MockBuffer self, Py_buffer* buffer):
- if buffer.suboffsets != self.suboffsets:
- self.release_ok = False
- if self.label:
- msg = "released %s" % self.label
- print msg
- self.log += msg + "\n"
-
- def printlog(self):
- print self.log[:-1]
-
- def resetlog(self):
- self.log = ""
-
- cdef int write(self, char* buf, object value) except -1: raise Exception()
- cdef get_itemsize(self):
- print "ERROR, not subclassed", self.__class__
- cdef get_default_format(self):
- print "ERROR, not subclassed", self.__class__
-
-cdef class CharMockBuffer(MockBuffer):
- cdef int write(self, char* buf, object value) except -1:
- (<char*>buf)[0] = <char>value
- return 0
- cdef get_itemsize(self): return sizeof(char)
- cdef get_default_format(self): return b"@b"
-
-cdef class IntMockBuffer(MockBuffer):
- cdef int write(self, char* buf, object value) except -1:
- (<int*>buf)[0] = <int>value
- return 0
- cdef get_itemsize(self): return sizeof(int)
- cdef get_default_format(self): return b"@i"
-
-cdef class UnsignedIntMockBuffer(MockBuffer):
- cdef int write(self, char* buf, object value) except -1:
- (<unsigned int*>buf)[0] = <unsigned int>value
- return 0
- cdef get_itemsize(self): return sizeof(unsigned int)
- cdef get_default_format(self): return b"@I"
-
-cdef class ShortMockBuffer(MockBuffer):
- cdef int write(self, char* buf, object value) except -1:
- (<short*>buf)[0] = <short>value
- return 0
- cdef get_itemsize(self): return sizeof(short)
- cdef get_default_format(self): return b"h" # Try without endian specifier
-
-cdef class UnsignedShortMockBuffer(MockBuffer):
- cdef int write(self, char* buf, object value) except -1:
- (<unsigned short*>buf)[0] = <unsigned short>value
- return 0
- cdef get_itemsize(self): return sizeof(unsigned short)
- cdef get_default_format(self): return b"@1H" # Try with repeat count
-
-cdef class FloatMockBuffer(MockBuffer):
- cdef int write(self, char* buf, object value) except -1:
- (<float*>buf)[0] = <float>(<double>value)
- return 0
- cdef get_itemsize(self): return sizeof(float)
- cdef get_default_format(self): return b"f"
-
-cdef class DoubleMockBuffer(MockBuffer):
- cdef int write(self, char* buf, object value) except -1:
- (<double*>buf)[0] = <double>value
- return 0
- cdef get_itemsize(self): return sizeof(double)
- cdef get_default_format(self): return b"d"
-
-cdef extern from *:
- void* addr_of_pyobject "(void*)"(object)
-
-cdef class ObjectMockBuffer(MockBuffer):
- cdef int write(self, char* buf, object value) except -1:
- (<void**>buf)[0] = addr_of_pyobject(value)
- return 0
-
- cdef get_itemsize(self): return sizeof(void*)
- cdef get_default_format(self): return b"@O"
-
-
-cdef class IntStridedMockBuffer(IntMockBuffer):
- cdef __cythonbufferdefaults__ = {"mode" : "strided"}
-
-cdef class ErrorBuffer:
- cdef object label
-
- def __init__(self, label):
- self.label = label
-
- def __getbuffer__(ErrorBuffer self, Py_buffer* buffer, int flags):
- raise Exception("acquiring %s" % self.label)
-
- def __releasebuffer__(ErrorBuffer self, Py_buffer* buffer):
- raise Exception("releasing %s" % self.label)
-
#
# Typed buffers
#
@@ -1257,75 +1009,6 @@ def bufdefaults1(IntStridedMockBuffer[int, ndim=1] buf):
pass
-#
-# Structs
-#
-cdef struct MyStruct:
- char a
- char b
- long long int c
- int d
- int e
-
-cdef struct SmallStruct:
- int a
- int b
-
-cdef struct NestedStruct:
- SmallStruct x
- SmallStruct y
- int z
-
-cdef packed struct PackedStruct:
- char a
- int b
-
-cdef struct NestedPackedStruct:
- char a
- int b
- PackedStruct sub
- int c
-
-cdef class MyStructMockBuffer(MockBuffer):
- cdef int write(self, char* buf, object value) except -1:
- cdef MyStruct* s
- s = <MyStruct*>buf;
- s.a, s.b, s.c, s.d, s.e = value
- return 0
-
- cdef get_itemsize(self): return sizeof(MyStruct)
- cdef get_default_format(self): return b"2bq2i"
-
-cdef class NestedStructMockBuffer(MockBuffer):
- cdef int write(self, char* buf, object value) except -1:
- cdef NestedStruct* s
- s = <NestedStruct*>buf;
- s.x.a, s.x.b, s.y.a, s.y.b, s.z = value
- return 0
-
- cdef get_itemsize(self): return sizeof(NestedStruct)
- cdef get_default_format(self): return b"2T{ii}i"
-
-cdef class PackedStructMockBuffer(MockBuffer):
- cdef int write(self, char* buf, object value) except -1:
- cdef PackedStruct* s
- s = <PackedStruct*>buf;
- s.a, s.b = value
- return 0
-
- cdef get_itemsize(self): return sizeof(PackedStruct)
- cdef get_default_format(self): return b"^ci"
-
-cdef class NestedPackedStructMockBuffer(MockBuffer):
- cdef int write(self, char* buf, object value) except -1:
- cdef NestedPackedStruct* s
- s = <NestedPackedStruct*>buf;
- s.a, s.b, s.sub.a, s.sub.b, s.c = value
- return 0
-
- cdef get_itemsize(self): return sizeof(NestedPackedStruct)
- cdef get_default_format(self): return b"ci^ci@i"
-
@testcase
def basic_struct(object[MyStruct] buf):
"""
diff --git a/tests/run/cythonarray.pyx b/tests/run/cythonarray.pyx
index 34049b363..540e41967 100644
--- a/tests/run/cythonarray.pyx
+++ b/tests/run/cythonarray.pyx
@@ -2,10 +2,8 @@
from __future__ import unicode_literals
-# from cython cimport array
-# cimport cython.array as array
+from cython cimport array
cimport cython as cy
-# array = cython.array
def contiguity():
'''
@@ -69,3 +67,37 @@ def dont_allocate_buffer():
cdef void callback(char *data):
print "callback called %d" % <long> data
+
+cdef create_array(shape, mode):
+ cdef array result = array(shape, itemsize=sizeof(int), format='i', mode=mode)
+ cdef int *data = <int *> result.data
+ cdef int i, j, cidx, fidx
+
+ for i in range(shape[0]):
+ for j in range(shape[1]):
+ cidx = i * shape[1] + j
+ fidx = i + j * shape[0]
+
+ if mode == 'fortran':
+ data[fidx] = cidx
+ else:
+ data[cidx] = cidx
+
+ return result
+
+def test_cython_array():
+ """
+ >>> test_cython_array()
+ 98
+ 61
+ 98
+ 61
+ """
+ cdef int[:, ::1] carr = create_array((14, 10), 'c')
+ cdef int[::1, :] farr = create_array((14, 10), 'fortran')
+
+ print carr[9, 8]
+ print carr[6, 1]
+
+ print farr[9, 8]
+ print farr[6, 1]
diff --git a/tests/run/memslice.pyx b/tests/run/memslice.pyx
new file mode 100644
index 000000000..0d64f093b
--- /dev/null
+++ b/tests/run/memslice.pyx
@@ -0,0 +1,1102 @@
+# 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.
+#
+
+from __future__ import unicode_literals
+
+from cython cimport view
+
+__test__ = {}
+
+import sys
+import re
+exclude = []#re.compile('object').search]
+
+def testcase(func):
+ for e in exclude:
+ if e(func.__name__):
+ return func
+ doctest = func.__doc__
+ if sys.version_info >= (3,1,1):
+ doctest = doctest.replace('does not have the buffer interface',
+ 'does not support the buffer interface')
+ __test__[func.__name__] = doctest
+ return func
+
+
+include "mockbuffers.pxi"
+
+#
+# Buffer acquire and release tests
+#
+
+def nousage():
+ """
+ The challenge here is just compilation.
+ """
+ cdef int[:, :] buf
+
+@testcase
+def acquire_release(o1, o2):
+ """
+ >>> A = IntMockBuffer("A", range(6))
+ >>> B = IntMockBuffer("B", range(6))
+ >>> acquire_release(A, B)
+ acquired A
+ released A
+ acquired B
+ released B
+ >>> acquire_release(None, None)
+ >>> acquire_release(None, B)
+ acquired B
+ released B
+ """
+ cdef int[:] buf
+ buf = o1
+ buf = o2
+
+@testcase
+def acquire_raise(o):
+ """
+ Apparently, doctest won't handle mixed exceptions and print
+ stats, so need to circumvent this.
+
+ >>> A = IntMockBuffer("A", range(6))
+ >>> A.resetlog()
+ >>> acquire_raise(A)
+ Traceback (most recent call last):
+ ...
+ Exception: on purpose
+ >>> A.printlog()
+ acquired A
+ released A
+
+ """
+ cdef int[:] buf
+ buf = o
+ raise Exception("on purpose")
+
+@testcase
+def acquire_failure1():
+ """
+ >>> acquire_failure1()
+ acquired working
+ 0 3
+ 0 3
+ released working
+ """
+ cdef int[:] buf
+ buf = IntMockBuffer("working", range(4))
+ print buf[0], buf[3]
+ try:
+ buf = ErrorBuffer()
+ assert False
+ except Exception:
+ print buf[0], buf[3]
+
+@testcase
+def acquire_failure2():
+ """
+ >>> acquire_failure2()
+ acquired working
+ 0 3
+ 0 3
+ released working
+ """
+ cdef int[:] buf = IntMockBuffer("working", range(4))
+ print buf[0], buf[3]
+ try:
+ buf = ErrorBuffer()
+ assert False
+ except Exception:
+ print buf[0], buf[3]
+
+@testcase
+def acquire_failure3():
+ """
+ >>> acquire_failure3()
+ acquired working
+ 0 3
+ released working
+ acquired working
+ 0 3
+ released working
+ """
+ cdef int[:] buf
+ buf = IntMockBuffer("working", range(4))
+ print buf[0], buf[3]
+ try:
+ buf = object()
+ assert False
+ except Exception:
+ print buf[0], buf[3]
+
+
+@testcase
+def acquire_nonbuffer1(first, second=None):
+ """
+ >>> acquire_nonbuffer1(3)
+ Traceback (most recent call last):
+ ...
+ TypeError: 'int' does not have the buffer interface
+ >>> acquire_nonbuffer1(type)
+ Traceback (most recent call last):
+ ...
+ TypeError: 'type' does not have the buffer interface
+ >>> acquire_nonbuffer1(None, 2)
+ Traceback (most recent call last):
+ ...
+ TypeError: 'int' does not have the buffer interface
+ """
+ cdef int[:] buf
+ buf = first
+ buf = second
+
+@testcase
+def acquire_nonbuffer2():
+ """
+ >>> acquire_nonbuffer2()
+ acquired working
+ 0 3
+ released working
+ acquired working
+ 0 3
+ released working
+ """
+ cdef int[:] buf = IntMockBuffer("working", range(4))
+ print buf[0], buf[3]
+ try:
+ buf = ErrorBuffer
+ assert False
+ except Exception:
+ print buf[0], buf[3]
+
+
+@testcase
+def as_argument(int[:] bufarg, int n):
+ """
+ >>> A = IntMockBuffer("A", range(6))
+ >>> as_argument(A, 6)
+ acquired A
+ 0 1 2 3 4 5 END
+ released A
+ """
+ cdef int i
+ for i in range(n):
+ print bufarg[i],
+ print 'END'
+
+@testcase
+def as_argument_defval(int[:] bufarg=IntMockBuffer('default', range(6)), int n=6):
+ """
+ >>> as_argument_defval()
+ acquired default
+ 0 1 2 3 4 5 END
+ released default
+ >>> A = IntMockBuffer("A", range(6))
+ >>> as_argument_defval(A, 6)
+ acquired A
+ 0 1 2 3 4 5 END
+ released A
+ """
+ cdef int i
+ for i in range(n):
+ print bufarg[i],
+ print 'END'
+
+@testcase
+def cdef_assignment(obj, n):
+ """
+ >>> A = IntMockBuffer("A", range(6))
+ >>> cdef_assignment(A, 6)
+ acquired A
+ 0 1 2 3 4 5 END
+ released A
+
+ """
+ cdef int[:] buf = obj
+ cdef int i
+ for i in range(n):
+ print buf[i],
+ print 'END'
+
+@testcase
+def forin_assignment(objs, int pick):
+ """
+ >>> A = IntMockBuffer("A", range(6))
+ >>> B = IntMockBuffer("B", range(6))
+ >>> forin_assignment([A, B, A, A], 2)
+ acquired A
+ 2
+ released A
+ acquired B
+ 2
+ released B
+ acquired A
+ 2
+ released A
+ acquired A
+ 2
+ released A
+ """
+ cdef int[:] buf
+ for buf in objs:
+ print buf[pick]
+
+@testcase
+def cascaded_buffer_assignment(obj):
+ """
+ >>> A = IntMockBuffer("A", range(6))
+ >>> cascaded_buffer_assignment(A)
+ acquired A
+ acquired A
+ released A
+ released A
+ """
+ cdef int[:] a, b
+ a = b = obj
+
+@testcase
+def tuple_buffer_assignment1(a, b):
+ """
+ >>> A = IntMockBuffer("A", range(6))
+ >>> B = IntMockBuffer("B", range(6))
+ >>> tuple_buffer_assignment1(A, B)
+ acquired A
+ acquired B
+ released A
+ released B
+ """
+ cdef int[:] x, y
+ x, y = a, b
+
+@testcase
+def tuple_buffer_assignment2(tup):
+ """
+ >>> A = IntMockBuffer("A", range(6))
+ >>> B = IntMockBuffer("B", range(6))
+ >>> tuple_buffer_assignment2((A, B))
+ acquired A
+ acquired B
+ released A
+ released B
+ """
+ cdef int[:] x, y
+ x, y = tup
+
+@testcase
+def explicitly_release_buffer():
+ """
+ >>> explicitly_release_buffer()
+ acquired A
+ released A
+ After release
+ """
+ cdef int[:] x = IntMockBuffer("A", range(10))
+ del x
+ print "After release"
+
+#
+# Getting items and index bounds checking
+#
+@testcase
+def get_int_2d(int[:, :] buf, int i, int j):
+ """
+ >>> C = IntMockBuffer("C", range(6), (2,3))
+ >>> get_int_2d(C, 1, 1)
+ acquired C
+ released C
+ 4
+
+ Check negative indexing:
+ >>> get_int_2d(C, -1, 0)
+ acquired C
+ released C
+ 3
+ >>> get_int_2d(C, -1, -2)
+ acquired C
+ released C
+ 4
+ >>> get_int_2d(C, -2, -3)
+ acquired C
+ released C
+ 0
+
+ Out-of-bounds errors:
+ >>> get_int_2d(C, 2, 0)
+ Traceback (most recent call last):
+ ...
+ IndexError: Out of bounds on buffer access (axis 0)
+ >>> get_int_2d(C, 0, -4)
+ Traceback (most recent call last):
+ ...
+ IndexError: Out of bounds on buffer access (axis 1)
+ """
+ return buf[i, j]
+
+@testcase
+def get_int_2d_uintindex(int[:, :] buf, unsigned int i, unsigned int j):
+ """
+ Unsigned indexing:
+ >>> C = IntMockBuffer("C", range(6), (2,3))
+ >>> get_int_2d_uintindex(C, 0, 0)
+ acquired C
+ released C
+ 0
+ >>> get_int_2d_uintindex(C, 1, 2)
+ acquired C
+ released C
+ 5
+ """
+ # This is most interesting with regards to the C code
+ # generated.
+ return buf[i, j]
+
+@testcase
+def set_int_2d(int[:, :] buf, int i, int j, int value):
+ """
+ Uses get_int_2d to read back the value afterwards. For pure
+ unit test, one should support reading in MockBuffer instead.
+
+ >>> C = IntMockBuffer("C", range(6), (2,3))
+ >>> set_int_2d(C, 1, 1, 10)
+ acquired C
+ released C
+ >>> get_int_2d(C, 1, 1)
+ acquired C
+ released C
+ 10
+
+ Check negative indexing:
+ >>> set_int_2d(C, -1, 0, 3)
+ acquired C
+ released C
+ >>> get_int_2d(C, -1, 0)
+ acquired C
+ released C
+ 3
+
+ >>> set_int_2d(C, -1, -2, 8)
+ acquired C
+ released C
+ >>> get_int_2d(C, -1, -2)
+ acquired C
+ released C
+ 8
+
+ >>> set_int_2d(C, -2, -3, 9)
+ acquired C
+ released C
+ >>> get_int_2d(C, -2, -3)
+ acquired C
+ released C
+ 9
+
+ Out-of-bounds errors:
+ >>> set_int_2d(C, 2, 0, 19)
+ Traceback (most recent call last):
+ ...
+ IndexError: Out of bounds on buffer access (axis 0)
+ >>> set_int_2d(C, 0, -4, 19)
+ Traceback (most recent call last):
+ ...
+ IndexError: Out of bounds on buffer access (axis 1)
+
+ """
+ buf[i, j] = value
+
+@testcase
+def list_comprehension(int[:] buf, len):
+ """
+ >>> list_comprehension(IntMockBuffer(None, [1,2,3]), 3)
+ 1|2|3
+ """
+ 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):
+ """
+ Again, the most interesting thing here is to inspect the C source.
+
+ >>> A = IntMockBuffer(None, range(4))
+ >>> wraparound_directive(A, 2, -1)
+ 5
+ >>> wraparound_directive(A, -1, 2)
+ Traceback (most recent call last):
+ ...
+ IndexError: Out of bounds on buffer access (axis 0)
+ """
+ cdef int byneg
+ with cython.wraparound(True):
+ byneg = buf[neg_idx]
+ return buf[pos_idx] + byneg
+
+
+#
+# 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):
+ """
+ >>> R = UnsignedShortMockBuffer("R", range(27), shape=(3, 3, 3))
+ >>> writable(R)
+ acquired R
+ released R
+ >>> [str(x) for x in R.recieved_flags] # Py2/3
+ ['FORMAT', 'ND', 'STRIDES', 'WRITABLE']
+ """
+ cdef unsigned short int[:, :, :] buf = obj
+ buf[2, 2, 1] = 23
+
+@testcase
+def strided(int[:] buf):
+ """
+ >>> A = IntMockBuffer("A", range(4))
+ >>> strided(A)
+ acquired A
+ released A
+ 2
+ >>> [str(x) for x in A.recieved_flags] # Py2/3
+ ['FORMAT', 'ND', 'STRIDES', 'WRITABLE']
+
+ Check that the suboffsets were patched back prior to release.
+ >>> A.release_ok
+ True
+ """
+ return buf[2]
+
+@testcase
+def c_contig(int[::1] buf):
+ """
+ >>> A = IntMockBuffer(None, range(4))
+ >>> c_contig(A)
+ 2
+ >>> [str(x) for x in A.recieved_flags]
+ ['FORMAT', 'ND', 'STRIDES', 'C_CONTIGUOUS']
+ """
+ return buf[2]
+
+@testcase
+def c_contig_2d(int[:, ::1] buf):
+ """
+ Multi-dim has seperate implementation
+
+ >>> A = IntMockBuffer(None, range(12), shape=(3,4))
+ >>> c_contig_2d(A)
+ 7
+ >>> [str(x) for x in A.recieved_flags]
+ ['FORMAT', 'ND', 'STRIDES', 'C_CONTIGUOUS']
+ """
+ return buf[1, 3]
+
+@testcase
+def f_contig(int[::1, :] buf):
+ """
+ >>> A = IntMockBuffer(None, range(4), shape=(2, 2))
+ >>> f_contig(A)
+ 2
+ >>> [str(x) for x in A.recieved_flags]
+ ['FORMAT', 'ND', 'STRIDES', 'F_CONTIGUOUS']
+ """
+ return buf[0, 1]
+
+@testcase
+def f_contig_2d(object[int, ndim=2, mode='fortran'] buf):
+ """
+ Must set up strides manually to ensure Fortran ordering.
+
+ >>> A = IntMockBuffer(None, range(12), shape=(4,3), strides=(1, 4))
+ >>> f_contig_2d(A)
+ 7
+ >>> [str(x) for x in A.recieved_flags]
+ ['FORMAT', 'ND', 'STRIDES', 'F_CONTIGUOUS']
+ """
+ return buf[3, 1]
+
+#
+# Test compiler options for bounds checking. We create an array with a
+# safe "boundary" (memory
+# allocated outside of what it published) and then check whether we get back
+# what we stored in the memory or an error.
+
+@testcase
+def safe_get(int[:] buf, int idx):
+ """
+ >>> A = IntMockBuffer(None, range(10), shape=(3,), offset=5)
+
+ Validate our testing buffer...
+ >>> safe_get(A, 0)
+ 5
+ >>> safe_get(A, 2)
+ 7
+ >>> safe_get(A, -3)
+ 5
+
+ Access outside it. This is already done above for bounds check
+ testing but we include it to tell the story right.
+
+ >>> safe_get(A, -4)
+ Traceback (most recent call last):
+ ...
+ IndexError: Out of bounds on buffer access (axis 0)
+ >>> safe_get(A, 3)
+ Traceback (most recent call last):
+ ...
+ IndexError: Out of bounds on buffer access (axis 0)
+ """
+ return buf[idx]
+
+@testcase
+@cython.boundscheck(False) # outer decorators should take precedence
+@cython.boundscheck(True)
+def unsafe_get(int[:] buf, int idx):
+ """
+ Access outside of the area the buffer publishes.
+ >>> A = IntMockBuffer(None, range(10), shape=(3,), offset=5)
+ >>> unsafe_get(A, -4)
+ 4
+ >>> unsafe_get(A, -5)
+ 3
+ >>> unsafe_get(A, 3)
+ 8
+ """
+ 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):
+ """
+ >>> A = IntMockBuffer(None, range(10), shape=(3,), offset=5)
+ >>> mixed_get(A, -4, 0)
+ (4, 5)
+ >>> mixed_get(A, 0, -4)
+ Traceback (most recent call last):
+ ...
+ IndexError: Out of bounds on buffer access (axis 0)
+ """
+ with cython.boundscheck(False):
+ one = buf[unsafe_idx]
+ with cython.boundscheck(True):
+ 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.
+#
+
+def printbuf_int(int[:] buf, shape):
+ # Utility func
+ cdef int i
+ for i in range(shape[0]):
+ print buf[i],
+ print 'END'
+
+
+@testcase
+def printbuf_int_2d(o, shape):
+ """
+ Strided:
+
+ >>> printbuf_int_2d(IntMockBuffer("A", range(6), (2,3)), (2,3))
+ acquired A
+ 0 1 2 END
+ 3 4 5 END
+ released A
+ >>> printbuf_int_2d(IntMockBuffer("A", range(100), (3,3), strides=(20,5)), (3,3))
+ acquired A
+ 0 5 10 END
+ 20 25 30 END
+ 40 45 50 END
+ released A
+
+ Indirect:
+ >>> printbuf_int_2d(IntMockBuffer("A", [[1,2],[3,4]]), (2,2))
+ acquired A
+ 1 2 END
+ 3 4 END
+ released A
+ """
+ # should make shape builtin
+ cdef int[:, :] buf
+ buf = o
+ cdef int i, j
+ for i in range(shape[0]):
+ for j in range(shape[1]):
+ print buf[i, j],
+ print 'END'
+
+@testcase
+def printbuf_float(o, shape):
+ """
+ >>> printbuf_float(FloatMockBuffer("F", [1.0, 1.25, 0.75, 1.0]), (4,))
+ acquired F
+ 1.0 1.25 0.75 1.0 END
+ released F
+ """
+
+ # should make shape builtin
+ cdef float[:] buf
+ buf = o
+ cdef int i, j
+ for i in range(shape[0]):
+ print buf[i],
+ print "END"
+
+
+#
+# Test assignments
+#
+@testcase
+def inplace_operators(int[:] buf):
+ """
+ >>> buf = IntMockBuffer(None, [2, 2])
+ >>> inplace_operators(buf)
+ >>> printbuf_int(buf, (2,))
+ 0 3 END
+ """
+ cdef int j = 0
+ buf[1] += 1
+ buf[j] *= 2
+ buf[0] -= 4
+
+
+
+#
+# Typedefs
+#
+# Test three layers of typedefs going through a h file for plain int, and
+# simply a header file typedef for floats and unsigned.
+
+ctypedef int td_cy_int
+cdef extern from "bufaccess.h":
+ ctypedef td_cy_int td_h_short # Defined as short, but Cython doesn't know this!
+ ctypedef float td_h_double # Defined as double
+ ctypedef unsigned int td_h_ushort # Defined as unsigned short
+ctypedef td_h_short td_h_cy_short
+
+@testcase
+def printbuf_td_cy_int(td_cy_int[:] buf, shape):
+ """
+ >>> printbuf_td_cy_int(IntMockBuffer(None, range(3)), (3,))
+ 0 1 2 END
+ >>> printbuf_td_cy_int(ShortMockBuffer(None, range(3)), (3,))
+ Traceback (most recent call last):
+ ...
+ ValueError: Buffer dtype mismatch, expected 'td_cy_int' but got 'short'
+ """
+ cdef int i
+ for i in range(shape[0]):
+ print buf[i],
+ print 'END'
+
+@testcase
+def printbuf_td_h_short(object[td_h_short] buf, shape):
+ """
+ >>> printbuf_td_h_short(ShortMockBuffer(None, range(3)), (3,))
+ 0 1 2 END
+ >>> printbuf_td_h_short(IntMockBuffer(None, range(3)), (3,))
+ Traceback (most recent call last):
+ ...
+ ValueError: Buffer dtype mismatch, expected 'td_h_short' but got 'int'
+ """
+ cdef int i
+ for i in range(shape[0]):
+ print buf[i],
+ print 'END'
+
+@testcase
+def printbuf_td_h_cy_short(object[td_h_cy_short] buf, shape):
+ """
+ >>> printbuf_td_h_cy_short(ShortMockBuffer(None, range(3)), (3,))
+ 0 1 2 END
+ >>> printbuf_td_h_cy_short(IntMockBuffer(None, range(3)), (3,))
+ Traceback (most recent call last):
+ ...
+ ValueError: Buffer dtype mismatch, expected 'td_h_cy_short' but got 'int'
+ """
+ cdef int i
+ for i in range(shape[0]):
+ print buf[i],
+ print 'END'
+
+@testcase
+def printbuf_td_h_ushort(object[td_h_ushort] buf, shape):
+ """
+ >>> printbuf_td_h_ushort(UnsignedShortMockBuffer(None, range(3)), (3,))
+ 0 1 2 END
+ >>> printbuf_td_h_ushort(ShortMockBuffer(None, range(3)), (3,))
+ Traceback (most recent call last):
+ ...
+ ValueError: Buffer dtype mismatch, expected 'td_h_ushort' but got 'short'
+ """
+ cdef int i
+ for i in range(shape[0]):
+ print buf[i],
+ print 'END'
+
+@testcase
+def printbuf_td_h_double(object[td_h_double] buf, shape):
+ """
+ >>> printbuf_td_h_double(DoubleMockBuffer(None, [0.25, 1, 3.125]), (3,))
+ 0.25 1.0 3.125 END
+ >>> printbuf_td_h_double(FloatMockBuffer(None, [0.25, 1, 3.125]), (3,))
+ Traceback (most recent call last):
+ ...
+ ValueError: Buffer dtype mismatch, expected 'td_h_double' but got 'float'
+ """
+ cdef int i
+ for i in range(shape[0]):
+ print buf[i],
+ print 'END'
+
+
+#
+# Object access
+#
+def addref(*args):
+ for item in args: Py_INCREF(item)
+def decref(*args):
+ for item in args: Py_DECREF(item)
+
+def get_refcount(x):
+ return (<PyObject*>x).ob_refcnt
+
+@testcase
+def printbuf_object(object[object] buf, shape):
+ """
+ Only play with unique objects, interned numbers etc. will have
+ unpredictable refcounts.
+
+ ObjectMockBuffer doesn't do anything about increfing/decrefing,
+ we to the "buffer implementor" refcounting directly in the
+ testcase.
+
+ >>> a, b, c = "globally_unique_string_23234123", {4:23}, [34,3]
+ >>> get_refcount(a), get_refcount(b), get_refcount(c)
+ (2, 2, 2)
+ >>> A = ObjectMockBuffer(None, [a, b, c])
+ >>> printbuf_object(A, (3,))
+ 'globally_unique_string_23234123' 2
+ {4: 23} 2
+ [34, 3] 2
+ """
+ cdef int i
+ for i in range(shape[0]):
+ print repr(buf[i]), (<PyObject*>buf[i]).ob_refcnt
+
+@testcase
+def assign_to_object(object[object] buf, int idx, obj):
+ """
+ See comments on printbuf_object above.
+
+ >>> a, b = [1, 2, 3], [4, 5, 6]
+ >>> get_refcount(a), get_refcount(b)
+ (2, 2)
+ >>> addref(a)
+ >>> A = ObjectMockBuffer(None, [1, a]) # 1, ...,otherwise it thinks nested lists...
+ >>> get_refcount(a), get_refcount(b)
+ (3, 2)
+ >>> assign_to_object(A, 1, b)
+ >>> get_refcount(a), get_refcount(b)
+ (2, 3)
+ >>> decref(b)
+ """
+ buf[idx] = obj
+
+@testcase
+def assign_temporary_to_object(object[object] buf):
+ """
+ See comments on printbuf_object above.
+
+ >>> a, b = [1, 2, 3], {4:23}
+ >>> get_refcount(a)
+ 2
+ >>> addref(a)
+ >>> A = ObjectMockBuffer(None, [b, a])
+ >>> get_refcount(a)
+ 3
+ >>> assign_temporary_to_object(A)
+ >>> get_refcount(a)
+ 2
+
+ >>> printbuf_object(A, (2,))
+ {4: 23} 2
+ {1: 8} 2
+
+ To avoid leaking a reference in our testcase we need to
+ replace the temporary with something we can manually decref :-)
+ >>> assign_to_object(A, 1, a)
+ >>> decref(a)
+ """
+ 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):
+ """
+ For IntStridedMockBuffer, mode should be
+ "strided" by defaults which should show
+ up in the flags.
+
+ >>> A = IntStridedMockBuffer("A", range(10))
+ >>> bufdefaults1(A)
+ acquired A
+ released A
+ >>> [str(x) for x in A.recieved_flags]
+ ['FORMAT', 'ND', 'STRIDES']
+ """
+ pass
+
+
+@testcase
+def basic_struct(object[MyStruct] buf):
+ """
+ See also buffmt.pyx
+
+ >>> basic_struct(MyStructMockBuffer(None, [(1, 2, 3, 4, 5)]))
+ 1 2 3 4 5
+ >>> basic_struct(MyStructMockBuffer(None, [(1, 2, 3, 4, 5)], format="bbqii"))
+ 1 2 3 4 5
+ """
+ print buf[0].a, buf[0].b, buf[0].c, buf[0].d, buf[0].e
+
+@testcase
+def nested_struct(object[NestedStruct] buf):
+ """
+ See also buffmt.pyx
+
+ >>> nested_struct(NestedStructMockBuffer(None, [(1, 2, 3, 4, 5)]))
+ 1 2 3 4 5
+ >>> nested_struct(NestedStructMockBuffer(None, [(1, 2, 3, 4, 5)], format="T{ii}T{2i}i"))
+ 1 2 3 4 5
+ """
+ print buf[0].x.a, buf[0].x.b, buf[0].y.a, buf[0].y.b, buf[0].z
+
+@testcase
+def packed_struct(object[PackedStruct] buf):
+ """
+ See also buffmt.pyx
+
+ >>> packed_struct(PackedStructMockBuffer(None, [(1, 2)]))
+ 1 2
+ >>> packed_struct(PackedStructMockBuffer(None, [(1, 2)], format="T{c^i}"))
+ 1 2
+ >>> packed_struct(PackedStructMockBuffer(None, [(1, 2)], format="T{c=i}"))
+ 1 2
+
+ """
+ print buf[0].a, buf[0].b
+
+@testcase
+def nested_packed_struct(object[NestedPackedStruct] buf):
+ """
+ See also buffmt.pyx
+
+ >>> nested_packed_struct(NestedPackedStructMockBuffer(None, [(1, 2, 3, 4, 5)]))
+ 1 2 3 4 5
+ >>> nested_packed_struct(NestedPackedStructMockBuffer(None, [(1, 2, 3, 4, 5)], format="ci^ci@i"))
+ 1 2 3 4 5
+ >>> nested_packed_struct(NestedPackedStructMockBuffer(None, [(1, 2, 3, 4, 5)], format="^c@i^ci@i"))
+ 1 2 3 4 5
+ """
+ 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):
+ """
+ >>> complex_dtype(LongComplexMockBuffer(None, [(0, -1)]))
+ -1j
+ """
+ print buf[0]
+
+@testcase
+def complex_inplace(object[long double complex] buf):
+ """
+ >>> complex_inplace(LongComplexMockBuffer(None, [(0, -1)]))
+ (1+1j)
+ """
+ buf[0] = buf[0] + 1 + 2j
+ print buf[0]
+
+@testcase
+def complex_struct_dtype(object[LongComplex] buf):
+ """
+ Note that the format string is "Zg" rather than "2g", yet a struct
+ is accessed.
+ >>> complex_struct_dtype(LongComplexMockBuffer(None, [(0, -1)]))
+ 0.0 -1.0
+ """
+ print buf[0].real, buf[0].imag
+
+@testcase
+def complex_struct_inplace(object[LongComplex] buf):
+ """
+ >>> complex_struct_inplace(LongComplexMockBuffer(None, [(0, -1)]))
+ 1.0 1.0
+ """
+ buf[0].real += 1
+ buf[0].imag += 2
+ print buf[0].real, buf[0].imag
+
+#
+# Nogil
+#
+@testcase
+@cython.boundscheck(False)
+def buffer_nogil():
+ """
+ >>> buffer_nogil()
+ 10
+ """
+ cdef int[:] buf = IntMockBuffer(None, [1,2,3])
+ with nogil:
+ buf[1] = 10
+ return buf[1]
diff --git a/tests/run/memslice_indexing.pyx b/tests/run/memslice_indexing.pyx
deleted file mode 100644
index 08f519e1a..000000000
--- a/tests/run/memslice_indexing.pyx
+++ /dev/null
@@ -1,40 +0,0 @@
-cimport cython
-from cython cimport array
-
-from libc.stdlib cimport malloc, free
-
-def create_array(shape, mode='c'):
- cdef array result = array(shape, itemsize=sizeof(int), format='i', mode=mode)
- cdef int *data = <int *> result.data
- cdef int i, j, value
-
- for i in range(shape[0]):
- for j in range(shape[1]):
- value = i * shape[0] + j
- if mode == 'fortran':
- data[i + j * 10] = value
- else:
- data[value] = value
-
- return result
-
-def slice_contig_indexing():
- """
- >>> print("disabled")
- disabled
-
- slice_contig_indexing()
- 98
- 61
- 98
- 61
- """
- cdef int[:, ::1] carr = create_array((14, 10))
- cdef int[::1, :] farr = create_array((10, 14), mode='fortran')
-
- print carr[9, 8]
- print carr[6, 1]
-
- print farr[9, 8]
- print farr[6, 1]
-
diff --git a/tests/run/mockbuffers.pxi b/tests/run/mockbuffers.pxi
new file mode 100644
index 000000000..acfb813da
--- /dev/null
+++ b/tests/run/mockbuffers.pxi
@@ -0,0 +1,313 @@
+from libc cimport stdlib
+from libc cimport stdio
+cimport cpython.buffer
+cimport cython
+
+from cpython cimport PyObject, Py_INCREF, Py_DECREF
+
+
+available_flags = (
+ ('FORMAT', cpython.buffer.PyBUF_FORMAT),
+ ('INDIRECT', cpython.buffer.PyBUF_INDIRECT),
+ ('ND', cpython.buffer.PyBUF_ND),
+ ('STRIDES', cpython.buffer.PyBUF_STRIDES),
+ ('C_CONTIGUOUS', cpython.buffer.PyBUF_C_CONTIGUOUS),
+ ('F_CONTIGUOUS', cpython.buffer.PyBUF_F_CONTIGUOUS),
+ ('WRITABLE', cpython.buffer.PyBUF_WRITABLE)
+)
+
+cdef class MockBuffer:
+ cdef object format, offset
+ cdef void* buffer
+ cdef int len, itemsize, ndim
+ cdef Py_ssize_t* strides
+ cdef Py_ssize_t* shape
+ cdef Py_ssize_t* suboffsets
+ cdef object label, log
+
+ cdef readonly object recieved_flags, release_ok
+ cdef public object fail
+
+ def __init__(self, label, data, shape=None, strides=None, format=None, offset=0):
+ # It is important not to store references to data after the constructor
+ # as refcounting is checked on object buffers.
+ self.label = label
+ self.release_ok = True
+ self.log = ""
+ self.offset = offset
+ self.itemsize = self.get_itemsize()
+ if format is None: format = self.get_default_format()
+ if shape is None: shape = (len(data),)
+ if strides is None:
+ strides = []
+ cumprod = 1
+ rshape = list(shape)
+ rshape.reverse()
+ for s in rshape:
+ strides.append(cumprod)
+ cumprod *= s
+ strides.reverse()
+ strides = [x * self.itemsize for x in strides]
+ suboffsets = [-1] * len(shape)
+ datashape = [len(data)]
+ p = data
+ while True:
+ p = p[0]
+ if isinstance(p, list): datashape.append(len(p))
+ else: break
+ if len(datashape) > 1:
+ # indirect access
+ self.ndim = len(datashape)
+ shape = datashape
+ self.buffer = self.create_indirect_buffer(data, shape)
+ suboffsets = [0] * (self.ndim-1) + [-1]
+ strides = [sizeof(void*)] * (self.ndim-1) + [self.itemsize]
+ self.suboffsets = self.list_to_sizebuf(suboffsets)
+ else:
+ # strided and/or simple access
+ self.buffer = self.create_buffer(data)
+ self.ndim = len(shape)
+ self.suboffsets = NULL
+
+ try:
+ format = format.encode('ASCII')
+ except AttributeError:
+ pass
+ self.format = format
+ self.len = len(data) * self.itemsize
+
+ self.strides = self.list_to_sizebuf(strides)
+ self.shape = self.list_to_sizebuf(shape)
+
+ def __dealloc__(self):
+ stdlib.free(self.strides)
+ stdlib.free(self.shape)
+ if self.suboffsets != NULL:
+ stdlib.free(self.suboffsets)
+ # must recursively free indirect...
+ else:
+ stdlib.free(self.buffer)
+
+ cdef void* create_buffer(self, data) except NULL:
+ cdef size_t n = <size_t>(len(data) * self.itemsize)
+ cdef char* buf = <char*>stdlib.malloc(n)
+ if buf == NULL:
+ raise MemoryError
+ cdef char* it = buf
+ for value in data:
+ self.write(it, value)
+ it += self.itemsize
+ return buf
+
+ cdef void* create_indirect_buffer(self, data, shape):
+ cdef size_t n = 0
+ cdef void** buf
+ assert shape[0] == len(data)
+ if len(shape) == 1:
+ return self.create_buffer(data)
+ else:
+ shape = shape[1:]
+ n = <size_t>len(data) * sizeof(void*)
+ buf = <void**>stdlib.malloc(n)
+ for idx, subdata in enumerate(data):
+ buf[idx] = self.create_indirect_buffer(subdata, shape)
+ return buf
+
+ cdef Py_ssize_t* list_to_sizebuf(self, l):
+ cdef size_t n = <size_t>len(l) * sizeof(Py_ssize_t)
+ cdef Py_ssize_t* buf = <Py_ssize_t*>stdlib.malloc(n)
+ for i, x in enumerate(l):
+ buf[i] = x
+ return buf
+
+ def __getbuffer__(MockBuffer self, Py_buffer* buffer, int flags):
+ if self.fail:
+ raise ValueError("Failing on purpose")
+
+ self.recieved_flags = []
+ cdef int value
+ for name, value in available_flags:
+ if (value & flags) == value:
+ self.recieved_flags.append(name)
+
+ buffer.buf = <void*>(<char*>self.buffer + (<int>self.offset * self.itemsize))
+ buffer.obj = self
+ buffer.len = self.len
+ buffer.readonly = 0
+ buffer.format = <char*>self.format
+ buffer.ndim = self.ndim
+ buffer.shape = self.shape
+ buffer.strides = self.strides
+ buffer.suboffsets = self.suboffsets
+ buffer.itemsize = self.itemsize
+ buffer.internal = NULL
+ if self.label:
+ msg = "acquired %s" % self.label
+ print msg
+ self.log += msg + "\n"
+
+ def __releasebuffer__(MockBuffer self, Py_buffer* buffer):
+ if buffer.suboffsets != self.suboffsets:
+ self.release_ok = False
+ if self.label:
+ msg = "released %s" % self.label
+ print msg
+ self.log += msg + "\n"
+
+ def printlog(self):
+ print self.log[:-1]
+
+ def resetlog(self):
+ self.log = ""
+
+ cdef int write(self, char* buf, object value) except -1: raise Exception()
+ cdef get_itemsize(self):
+ print "ERROR, not subclassed", self.__class__
+ cdef get_default_format(self):
+ print "ERROR, not subclassed", self.__class__
+
+cdef class CharMockBuffer(MockBuffer):
+ cdef int write(self, char* buf, object value) except -1:
+ (<char*>buf)[0] = <char>value
+ return 0
+ cdef get_itemsize(self): return sizeof(char)
+ cdef get_default_format(self): return b"@b"
+
+cdef class IntMockBuffer(MockBuffer):
+ cdef int write(self, char* buf, object value) except -1:
+ (<int*>buf)[0] = <int>value
+ return 0
+ cdef get_itemsize(self): return sizeof(int)
+ cdef get_default_format(self): return b"@i"
+
+cdef class UnsignedIntMockBuffer(MockBuffer):
+ cdef int write(self, char* buf, object value) except -1:
+ (<unsigned int*>buf)[0] = <unsigned int>value
+ return 0
+ cdef get_itemsize(self): return sizeof(unsigned int)
+ cdef get_default_format(self): return b"@I"
+
+cdef class ShortMockBuffer(MockBuffer):
+ cdef int write(self, char* buf, object value) except -1:
+ (<short*>buf)[0] = <short>value
+ return 0
+ cdef get_itemsize(self): return sizeof(short)
+ cdef get_default_format(self): return b"h" # Try without endian specifier
+
+cdef class UnsignedShortMockBuffer(MockBuffer):
+ cdef int write(self, char* buf, object value) except -1:
+ (<unsigned short*>buf)[0] = <unsigned short>value
+ return 0
+ cdef get_itemsize(self): return sizeof(unsigned short)
+ cdef get_default_format(self): return b"@1H" # Try with repeat count
+
+cdef class FloatMockBuffer(MockBuffer):
+ cdef int write(self, char* buf, object value) except -1:
+ (<float*>buf)[0] = <float>(<double>value)
+ return 0
+ cdef get_itemsize(self): return sizeof(float)
+ cdef get_default_format(self): return b"f"
+
+cdef class DoubleMockBuffer(MockBuffer):
+ cdef int write(self, char* buf, object value) except -1:
+ (<double*>buf)[0] = <double>value
+ return 0
+ cdef get_itemsize(self): return sizeof(double)
+ cdef get_default_format(self): return b"d"
+
+cdef extern from *:
+ void* addr_of_pyobject "(void*)"(object)
+
+cdef class ObjectMockBuffer(MockBuffer):
+ cdef int write(self, char* buf, object value) except -1:
+ (<void**>buf)[0] = addr_of_pyobject(value)
+ return 0
+
+ cdef get_itemsize(self): return sizeof(void*)
+ cdef get_default_format(self): return b"@O"
+
+
+cdef class IntStridedMockBuffer(IntMockBuffer):
+ cdef __cythonbufferdefaults__ = {"mode" : "strided"}
+
+cdef class ErrorBuffer:
+ cdef object label
+
+ def __init__(self, label):
+ self.label = label
+
+ def __getbuffer__(ErrorBuffer self, Py_buffer* buffer, int flags):
+ raise Exception("acquiring %s" % self.label)
+
+ def __releasebuffer__(ErrorBuffer self, Py_buffer* buffer):
+ raise Exception("releasing %s" % self.label)
+
+#
+# Structs
+#
+cdef struct MyStruct:
+ char a
+ char b
+ long long int c
+ int d
+ int e
+
+cdef struct SmallStruct:
+ int a
+ int b
+
+cdef struct NestedStruct:
+ SmallStruct x
+ SmallStruct y
+ int z
+
+cdef packed struct PackedStruct:
+ char a
+ int b
+
+cdef struct NestedPackedStruct:
+ char a
+ int b
+ PackedStruct sub
+ int c
+
+cdef class MyStructMockBuffer(MockBuffer):
+ cdef int write(self, char* buf, object value) except -1:
+ cdef MyStruct* s
+ s = <MyStruct*>buf;
+ s.a, s.b, s.c, s.d, s.e = value
+ return 0
+
+ cdef get_itemsize(self): return sizeof(MyStruct)
+ cdef get_default_format(self): return b"2bq2i"
+
+cdef class NestedStructMockBuffer(MockBuffer):
+ cdef int write(self, char* buf, object value) except -1:
+ cdef NestedStruct* s
+ s = <NestedStruct*>buf;
+ s.x.a, s.x.b, s.y.a, s.y.b, s.z = value
+ return 0
+
+ cdef get_itemsize(self): return sizeof(NestedStruct)
+ cdef get_default_format(self): return b"2T{ii}i"
+
+cdef class PackedStructMockBuffer(MockBuffer):
+ cdef int write(self, char* buf, object value) except -1:
+ cdef PackedStruct* s
+ s = <PackedStruct*>buf;
+ s.a, s.b = value
+ return 0
+
+ cdef get_itemsize(self): return sizeof(PackedStruct)
+ cdef get_default_format(self): return b"^ci"
+
+cdef class NestedPackedStructMockBuffer(MockBuffer):
+ cdef int write(self, char* buf, object value) except -1:
+ cdef NestedPackedStruct* s
+ s = <NestedPackedStruct*>buf;
+ s.a, s.b, s.sub.a, s.sub.b, s.c = value
+ return 0
+
+ cdef get_itemsize(self): return sizeof(NestedPackedStruct)
+ cdef get_default_format(self): return b"ci^ci@i"
+