Commit 5fbfb6f6 authored by Mark Florisson's avatar Mark Florisson

Support None memoryview slices

parent 3aa13fa7
...@@ -2886,8 +2886,7 @@ class IndexNode(ExprNode): ...@@ -2886,8 +2886,7 @@ class IndexNode(ExprNode):
def generate_result_code(self, code): def generate_result_code(self, code):
if self.is_buffer_access or self.memslice_index: if self.is_buffer_access or self.memslice_index:
if code.globalstate.directives['nonecheck']: self.nonecheck(code)
self.put_nonecheck(code)
buffer_entry, self.buffer_ptr_code = self.buffer_lookup_code(code) buffer_entry, self.buffer_ptr_code = self.buffer_lookup_code(code)
if self.type.is_pyobject: if self.type.is_pyobject:
# is_temp is True, so must pull out value and incref it. # is_temp is True, so must pull out value and incref it.
...@@ -2895,6 +2894,7 @@ class IndexNode(ExprNode): ...@@ -2895,6 +2894,7 @@ class IndexNode(ExprNode):
code.putln("__Pyx_INCREF((PyObject*)%s);" % self.result()) code.putln("__Pyx_INCREF((PyObject*)%s);" % self.result())
elif self.memslice_slice: elif self.memslice_slice:
self.nonecheck(code)
self.put_memoryviewslice_slice_code(code) self.put_memoryviewslice_slice_code(code)
elif self.is_temp: elif self.is_temp:
...@@ -2969,9 +2969,7 @@ class IndexNode(ExprNode): ...@@ -2969,9 +2969,7 @@ class IndexNode(ExprNode):
def generate_buffer_setitem_code(self, rhs, code, op=""): def generate_buffer_setitem_code(self, rhs, code, op=""):
# Used from generate_assignment_code and InPlaceAssignmentNode # Used from generate_assignment_code and InPlaceAssignmentNode
if code.globalstate.directives['nonecheck'] and not self.memslice_index: self.nonecheck(code)
self.put_nonecheck(code)
buffer_entry, ptrexpr = self.buffer_lookup_code(code) buffer_entry, ptrexpr = self.buffer_lookup_code(code)
if self.buffer_type.dtype.is_pyobject: if self.buffer_type.dtype.is_pyobject:
...@@ -3133,10 +3131,23 @@ class IndexNode(ExprNode): ...@@ -3133,10 +3131,23 @@ class IndexNode(ExprNode):
import MemoryView import MemoryView
MemoryView.assign_scalar(self, rhs, code) MemoryView.assign_scalar(self, rhs, code)
def nonecheck(self, code):
if code.globalstate.directives['nonecheck']:
self.put_nonecheck(code)
def put_nonecheck(self, code): def put_nonecheck(self, code):
if self.base.type.is_memoryviewslice:
code.globalstate.use_utility_code(
raise_noneindex_memview_error_utility_code)
code.putln("if (unlikely((PyObject *) %s.memview == Py_None)) {" %
self.base.result())
code.putln("__Pyx_RaiseNoneMemviewIndexingError();")
else:
code.globalstate.use_utility_code(raise_noneindex_error_utility_code) code.globalstate.use_utility_code(raise_noneindex_error_utility_code)
code.putln("if (%s) {" % code.unlikely("%s == Py_None") % self.base.result_as(PyrexTypes.py_object_type)) code.putln("if (%s) {" % code.unlikely("%s == Py_None") %
self.base.result_as(PyrexTypes.py_object_type))
code.putln("__Pyx_RaiseNoneIndexingError();") code.putln("__Pyx_RaiseNoneIndexingError();")
code.putln(code.error_goto(self.pos)) code.putln(code.error_goto(self.pos))
code.putln("}") code.putln("}")
...@@ -4515,6 +4526,9 @@ class AttributeNode(ExprNode): ...@@ -4515,6 +4526,9 @@ class AttributeNode(ExprNode):
code.error_goto_if_null(self.result(), self.pos))) code.error_goto_if_null(self.result(), self.pos)))
code.put_gotref(self.py_result()) code.put_gotref(self.py_result())
elif self.type.is_memoryviewslice: elif self.type.is_memoryviewslice:
if code.globalstate.directives['nonecheck']:
self.put_nonecheck(code)
if self.is_memslice_transpose: if self.is_memslice_transpose:
# transpose the slice # transpose the slice
for access, packing in self.type.axes: for access, packing in self.type.axes:
...@@ -4537,6 +4551,9 @@ class AttributeNode(ExprNode): ...@@ -4537,6 +4551,9 @@ class AttributeNode(ExprNode):
'"Memoryview is not initialized");' '"Memoryview is not initialized");'
'%s' '%s'
'}' % (self.result(), code.error_goto(self.pos))) '}' % (self.result(), code.error_goto(self.pos)))
elif (self.obj.type.is_memoryviewslice and
code.globalstate.directives['nonecheck']):
self.put_nonecheck(code)
else: else:
# result_code contains what is needed, but we may need to insert # result_code contains what is needed, but we may need to insert
# a check and raise an exception # a check and raise an exception
...@@ -4614,7 +4631,7 @@ class AttributeNode(ExprNode): ...@@ -4614,7 +4631,7 @@ class AttributeNode(ExprNode):
if self.obj.type.is_extension_type: if self.obj.type.is_extension_type:
test = "%s == Py_None" % self.obj.result_as(PyrexTypes.py_object_type) test = "%s == Py_None" % self.obj.result_as(PyrexTypes.py_object_type)
elif self.obj.type.is_memoryviewslice: elif self.obj.type.is_memoryviewslice:
test = "!%s.memview" % self.obj.result() test = "(PyObject *) %s.memview == Py_None" % self.obj.result()
else: else:
assert False assert False
code.putln("if (%s) {" % code.unlikely(test)) code.putln("if (%s) {" % code.unlikely(test))
...@@ -9647,6 +9664,16 @@ static CYTHON_INLINE void __Pyx_RaiseNoneIndexingError(void) { ...@@ -9647,6 +9664,16 @@ static CYTHON_INLINE void __Pyx_RaiseNoneIndexingError(void) {
} }
''') ''')
raise_noneindex_memview_error_utility_code = UtilityCode(
proto = """
static CYTHON_INLINE void __Pyx_RaiseNoneMemviewIndexingError(void);
""",
impl = '''
static CYTHON_INLINE void __Pyx_RaiseNoneMemviewIndexingError(void) {
PyErr_SetString(PyExc_TypeError, "Cannot index None memoryview slice");
}
''')
raise_none_iter_error_utility_code = UtilityCode( raise_none_iter_error_utility_code = UtilityCode(
proto = """ proto = """
static CYTHON_INLINE void __Pyx_RaiseNoneNotIterableError(void); static CYTHON_INLINE void __Pyx_RaiseNoneNotIterableError(void);
......
...@@ -921,6 +921,9 @@ cdef memoryview_fromslice({{memviewslice_name}} *memviewslice, ...@@ -921,6 +921,9 @@ cdef memoryview_fromslice({{memviewslice_name}} *memviewslice,
cdef _memoryviewslice result cdef _memoryviewslice result
cdef int i cdef int i
if <PyObject *> memviewslice.memview == Py_None:
return None
# assert 0 < ndim <= memviewslice.memview.view.ndim, ( # assert 0 < ndim <= memviewslice.memview.view.ndim, (
# ndim, memviewslice.memview.view.ndim) # ndim, memviewslice.memview.view.ndim)
......
...@@ -163,9 +163,15 @@ static CYTHON_INLINE char *__pyx_memviewslice_index_full(const char *bufp, Py_ss ...@@ -163,9 +163,15 @@ static CYTHON_INLINE char *__pyx_memviewslice_index_full(const char *bufp, Py_ss
static CYTHON_INLINE {{memviewslice_name}} {{funcname}}(PyObject *obj) { static CYTHON_INLINE {{memviewslice_name}} {{funcname}}(PyObject *obj) {
{{memviewslice_name}} result = {{memslice_init}}; {{memviewslice_name}} result = {{memslice_init}};
struct __pyx_memoryview_obj *memview;
struct __pyx_memoryview_obj *memview = \ if (obj == Py_None) {
(struct __pyx_memoryview_obj *) __pyx_memoryview_new(obj, {{buf_flag}}, 0); /* We don't bother to refcount None */
result.memview = (struct __pyx_memoryview_obj *) Py_None;
return result;
}
memview = (struct __pyx_memoryview_obj *) __pyx_memoryview_new(obj, {{buf_flag}}, 0);
__Pyx_BufFmt_StackElem stack[{{struct_nesting_depth}}]; __Pyx_BufFmt_StackElem stack[{{struct_nesting_depth}}];
int axes_specs[] = { {{axes_specs}} }; int axes_specs[] = { {{axes_specs}} };
int retcode; int retcode;
...@@ -419,7 +425,7 @@ __Pyx_INC_MEMVIEW({{memviewslice_name}} *memslice, int have_gil, int lineno) ...@@ -419,7 +425,7 @@ __Pyx_INC_MEMVIEW({{memviewslice_name}} *memslice, int have_gil, int lineno)
{ {
int first_time; int first_time;
struct {{memview_struct_name}} *memview = memslice->memview; struct {{memview_struct_name}} *memview = memslice->memview;
if (!memview) if (!memview || (PyObject *) memview == Py_None)
return; /* allow uninitialized memoryview assignment */ return; /* allow uninitialized memoryview assignment */
if (memview->acquisition_count < 0) if (memview->acquisition_count < 0)
...@@ -444,7 +450,7 @@ static CYTHON_INLINE void __Pyx_XDEC_MEMVIEW({{memviewslice_name}} *memslice, ...@@ -444,7 +450,7 @@ static CYTHON_INLINE void __Pyx_XDEC_MEMVIEW({{memviewslice_name}} *memslice,
int last_time; int last_time;
struct {{memview_struct_name}} *memview = memslice->memview; struct {{memview_struct_name}} *memview = memslice->memview;
if (!memview) if (!memview || (PyObject *) memview == Py_None)
return; return;
if (memview->acquisition_count <= 0) if (memview->acquisition_count <= 0)
......
...@@ -218,11 +218,15 @@ e.g. do:: ...@@ -218,11 +218,15 @@ e.g. do::
Of course, you are not restricted to using NumPy's type (such as ``np.int32_t`` here), you can use any usable type. Of course, you are not restricted to using NumPy's type (such as ``np.int32_t`` here), you can use any usable type.
The future None Slices
========== ===========
In the future some functionality may be added for convenience, like Although memoryview slices are not objects they can be set to None and they can be be
checked for being None as well::
1. A numpy-like `.flat` attribute (that allows efficient iteration) def func(double[:] myarray = None):
2. Indexing with newaxis or None to introduce a new axis print myarray is None
Unlike object attributes of extension classes, memoryview slices are not initialized
to None.
.. _NumPy: http://docs.scipy.org/doc/numpy/reference/arrays.ndarray.html#memory-layout .. _NumPy: http://docs.scipy.org/doc/numpy/reference/arrays.ndarray.html#memory-layout
...@@ -52,9 +52,8 @@ def acquire_release(o1, o2): ...@@ -52,9 +52,8 @@ def acquire_release(o1, o2):
released A released A
released B released B
>>> acquire_release(None, B) >>> acquire_release(None, B)
Traceback (most recent call last): acquired B
... released B
TypeError: 'NoneType' does not have the buffer interface
""" """
cdef int[:] buf cdef int[:] buf
buf = o1 buf = o1
...@@ -148,7 +147,7 @@ def acquire_nonbuffer1(first, second=None): ...@@ -148,7 +147,7 @@ def acquire_nonbuffer1(first, second=None):
>>> acquire_nonbuffer1(None, 2) >>> acquire_nonbuffer1(None, 2)
Traceback (most recent call last): Traceback (most recent call last):
... ...
TypeError: 'NoneType' does not have the buffer interface TypeError: 'int' does not have the buffer interface
>>> acquire_nonbuffer1(4, object()) >>> acquire_nonbuffer1(4, object())
Traceback (most recent call last): Traceback (most recent call last):
... ...
...@@ -2086,3 +2085,95 @@ def test_dtype_object_scalar_assignment(): ...@@ -2086,3 +2085,95 @@ def test_dtype_object_scalar_assignment():
(<object> m)[:] = SingleObject(3) (<object> m)[:] = SingleObject(3)
assert m[0] == m[4] == m[-1] == 3 assert m[0] == m[4] == m[-1] == 3
#
### Test slices that are set to None
#
@testcase
def test_coerce_to_from_None(double[:] m1, double[:] m2 = None):
"""
>>> test_coerce_to_from_None(None)
(None, None)
>>> test_coerce_to_from_None(None, None)
(None, None)
"""
return m1, m2
@testcase
def test_noneslice_attrib(double[:] m):
"""
>>> test_noneslice_attrib(None)
'NoneType' object has no attribute 'copy'
'NoneType' object has no attribute 'T'
"""
cdef double[:] m2
with cython.nonecheck(True):
try:
m2 = m.copy()
except Exception, e:
print e.args[0]
try:
m2 = m.T
except Exception, e:
print e.args[0]
@testcase
def test_noneslice_index(double[:] m):
"""
>>> test_noneslice_index(None)
Cannot index None memoryview slice
Cannot index None memoryview slice
Cannot index None memoryview slice
Cannot index None memoryview slice
"""
with cython.nonecheck(True):
try:
m[10]
except Exception, e:
print e.args[0]
try:
m[:]
except Exception, e:
print e.args[0]
try:
m[10] = 2
except Exception, e:
print e.args[0]
try:
m[:] = 2
except Exception, e:
print e.args[0]
@testcase
def test_noneslice_compare(double[:] m):
"""
>>> test_noneslice_compare(None)
(True, True)
"""
with cython.nonecheck(True):
result = m is None
return result, m is None
cdef class NoneSliceAttr(object):
cdef double[:] m
@testcase
def test_noneslice_ext_attr():
"""
>>> test_noneslice_ext_attr()
AttributeError Memoryview is not initialized
None
"""
cdef NoneSliceAttr obj = NoneSliceAttr()
with cython.nonecheck(True):
try: print obj.m
except Exception, e: print type(e).__name__, e.args[0]
obj.m = None
print obj.m
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment