From c72654556e337e64ed967ed118df8e7f510e3382 Mon Sep 17 00:00:00 2001 From: Stefan Behnel <stefan_ml@behnel.de> Date: Thu, 15 Feb 2018 21:22:02 +0100 Subject: [PATCH] Disable auto-detection of read-only memory views (too unsafe) and acquire them only as read-only if the memory view dtype is a "const" type. --- CHANGES.rst | 6 ++-- Cython/Compiler/PyrexTypes.py | 33 +++++++++++-------- docs/src/userguide/memoryviews.rst | 16 ++++----- .../memoryview/numpy_memoryview_readonly.pyx | 7 ++-- 4 files changed, 33 insertions(+), 29 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 3bfd60e46..19fbba126 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -15,10 +15,8 @@ Features added * Type inference is now supported for Pythran compiled NumPy expressions. Patch by Nils Braun. (Github issue #1954) -* Read-only buffers are automatically supported for memoryviews if it can be - determined at compile time that the code does not need write access. - They can also be allowed explicitly by adding the ``const`` modifier to their - declaration. (Github issues #1605, #1869) +* The ``const`` modifier can be applied to memoryview declarations to allow + read-only buffers as input. (Github issues #1605, #1869) * When compiling with gcc, the module init function is now tuned for small code size instead of whatever compile flags were provided externally. diff --git a/Cython/Compiler/PyrexTypes.py b/Cython/Compiler/PyrexTypes.py index 8f61107e8..c4dc1176d 100644 --- a/Cython/Compiler/PyrexTypes.py +++ b/Cython/Compiler/PyrexTypes.py @@ -907,9 +907,12 @@ class MemoryViewSliceType(PyrexType): def from_py_call_code(self, source_code, result_code, error_pos, code, from_py_function=None, error_condition=None): + # NOTE: auto-detection of readonly buffers is disabled: + # writable = self.writable_needed or not self.dtype.is_const + writable = not self.dtype.is_const return self._assign_from_py_code( source_code, result_code, error_pos, code, from_py_function, error_condition, - extra_args=['PyBUF_WRITABLE' if self.writable_needed else '0']) + extra_args=['PyBUF_WRITABLE' if writable else '0']) def create_to_py_utility_code(self, env): self._dtype_to_py_func, self._dtype_from_py_func = self.dtype_object_conversion_funcs(env) @@ -937,25 +940,29 @@ class MemoryViewSliceType(PyrexType): if self.dtype.is_pyobject: utility_name = "MemviewObjectToObject" else: - to_py = self.dtype.create_to_py_utility_code(env) - from_py = self.dtype.create_from_py_utility_code(env) - if not (to_py or from_py): - return "NULL", "NULL" + self.dtype.create_to_py_utility_code(env) + to_py_function = self.dtype.to_py_function - if not self.dtype.to_py_function: - get_function = "NULL" + from_py_function = None + if not self.dtype.is_const: + self.dtype.create_from_py_utility_code(env) + from_py_function = self.dtype.from_py_function - if not self.dtype.from_py_function: + if not (to_py_function or from_py_function): + return "NULL", "NULL" + if not to_py_function: + get_function = "NULL" + if not from_py_function: set_function = "NULL" utility_name = "MemviewDtypeToObject" error_condition = (self.dtype.error_condition('value') or 'PyErr_Occurred()') context.update( - to_py_function = self.dtype.to_py_function, - from_py_function = self.dtype.from_py_function, - dtype = self.dtype.empty_declaration_code(), - error_condition = error_condition, + to_py_function=to_py_function, + from_py_function=from_py_function, + dtype=self.dtype.empty_declaration_code(), + error_condition=error_condition, ) utility = TempitaUtilityCode.load_cached( @@ -2456,7 +2463,7 @@ class CArrayType(CPointerBaseType): def from_py_call_code(self, source_code, result_code, error_pos, code, from_py_function=None, error_condition=None): - assert not error_condition + assert not error_condition, '%s: %s' % (error_pos, error_condition) call_code = "%s(%s, %s, %s)" % ( from_py_function or self.from_py_function, source_code, result_code, self.size) diff --git a/docs/src/userguide/memoryviews.rst b/docs/src/userguide/memoryviews.rst index 8ccb9e144..6bb2b4da1 100644 --- a/docs/src/userguide/memoryviews.rst +++ b/docs/src/userguide/memoryviews.rst @@ -244,14 +244,14 @@ Note that this does not *require* the input to be read-only:: a = np.linspace(0, 10, num=50) myslice = a -Writable buffers will still be accepted by ``const`` views, but read-only -buffers are not accepted for non-const, writable views. - -Cython will also request a read-only view automatically if it can determine -at compile time that a writable buffer is not required. The support for -automatically distinguishing between buffer usage types, and the compile -time correctness checks for read-only views are expected to further improve -over time. +Writable buffers are still accepted by ``const`` views, but read-only +buffers are not accepted for non-const, writable views:: + + cdef double[:] myslice # a normal read-write memory view + + a = np.linspace(0, 10, num=50) + a.setflags(write=False) + myslice = a # ERROR: requesting writable memory view from read-only buffer! Comparison to the old buffer support diff --git a/tests/memoryview/numpy_memoryview_readonly.pyx b/tests/memoryview/numpy_memoryview_readonly.pyx index 195b66809..f59339f54 100644 --- a/tests/memoryview/numpy_memoryview_readonly.pyx +++ b/tests/memoryview/numpy_memoryview_readonly.pyx @@ -9,9 +9,8 @@ def new_array(): ARRAY = new_array() -cdef getmax(double[:] x): - """Example code, should work with both - ro and rw memoryviews""" +cdef getmax(const double[:] x): + """Example code, should work with both ro and rw memoryviews""" cdef double max_val = -float('inf') for val in x: if val > max_val: @@ -25,7 +24,7 @@ cdef update_array(double [:] x): cdef getconst(const double [:] x): - """Should only accept ro memoryviews""" + """Should accept ro memoryviews""" return x[0] -- 2.30.9