summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/release/1.15.0-notes.rst6
-rw-r--r--doc/source/reference/arrays.nditer.rst39
-rw-r--r--doc/source/reference/c-api.iterator.rst16
-rw-r--r--numpy/add_newdocs.py16
-rw-r--r--numpy/core/code_generators/numpy_api.py2
-rw-r--r--numpy/core/src/multiarray/arrayobject.c3
-rw-r--r--numpy/core/tests/test_nditer.py24
7 files changed, 69 insertions, 37 deletions
diff --git a/doc/release/1.15.0-notes.rst b/doc/release/1.15.0-notes.rst
index d076074ce..68e8566ee 100644
--- a/doc/release/1.15.0-notes.rst
+++ b/doc/release/1.15.0-notes.rst
@@ -61,7 +61,7 @@ Deprecations
anytime one of the iterator operands is writeable, so that numpy can
manage writeback semantics, or should call ``it.close()``. A
`RuntimeWarning` will be emitted otherwise in these cases. Users of the C-API
- should call ``NpyIter_Close`` before ``NpyIter_Dealloc``.
+ should call ``NpyIter_Close`` before ``NpyIter_Deallocate``.
Future Changes
@@ -117,8 +117,8 @@ using the old API.
C API changes
=============
-``NpyIter_Close`` has been added and should be called before
-``NpyIter_Dealloc`` to resolve possible writeback-enabled arrays.
+* ``NpyIter_Close`` has been added and should be called before
+ ``NpyIter_Deallocate`` to resolve possible writeback-enabled arrays.
New Features
============
diff --git a/doc/source/reference/arrays.nditer.rst b/doc/source/reference/arrays.nditer.rst
index acad29b11..239f4296b 100644
--- a/doc/source/reference/arrays.nditer.rst
+++ b/doc/source/reference/arrays.nditer.rst
@@ -78,27 +78,28 @@ order='C' for C order and order='F' for Fortran order.
...
0 3 1 4 2 5
+.. _nditer-context-manager:
+
Modifying Array Values
----------------------
-By default, the :class:`nditer` treats the input array as a read-only
-object. To modify the array elements, you must specify either read-write
-or write-only mode. This is controlled with per-operand flags. The
-operands may be created as views into the original data with the
-`WRITEBACKIFCOPY` flag. In this case the iterator must either
-
-- be used as a context manager, and the temporary data will be written back
- to the original array when the `__exit__` function is called.
-- have a call to the iterator's `close` function to ensure the modified data
- is written back to the original array.
-
-Regular assignment in Python simply changes a reference in the local or
-global variable dictionary instead of modifying an existing variable in
-place. This means that simply assigning to `x` will not place the value
-into the element of the array, but rather switch `x` from being an array
-element reference to being a reference to the value you assigned. To
-actually modify the element of the array, `x` should be indexed with
-the ellipsis.
+By default, the :class:`nditer` treats the input operand as a read-only
+object. To be able to modify the array elements, you must specify either
+read-write or write-only mode using the `'readwrite'` or `'writeonly'`
+per-operand flags.
+
+The nditer will then yield writeable buffer arrays which you may modify. However,
+because the nditer must copy this buffer data back to the original array once
+iteration is finished, you must signal when the iteration is ended, by one of two
+methods. You may either:
+
+ - used the nditer as a context manager using the `with` statement, and
+ the temporary data will be written back when the context is exited.
+ - call the iterator's `close` method once finished iterating, which will trigger
+ the write-back.
+
+The nditer can no longer be iterated once either `close` is called or its
+context is exited.
.. admonition:: Example
@@ -186,7 +187,7 @@ construct in order to be more readable.
0 <(0, 0)> 1 <(0, 1)> 2 <(0, 2)> 3 <(1, 0)> 4 <(1, 1)> 5 <(1, 2)>
>>> it = np.nditer(a, flags=['multi_index'], op_flags=['writeonly'])
- >>> with it:
+ >>> with it:
.... while not it.finished:
... it[0] = it.multi_index[1] - it.multi_index[0]
... it.iternext()
diff --git a/doc/source/reference/c-api.iterator.rst b/doc/source/reference/c-api.iterator.rst
index 314b62a16..6a9ec6005 100644
--- a/doc/source/reference/c-api.iterator.rst
+++ b/doc/source/reference/c-api.iterator.rst
@@ -709,6 +709,10 @@ Construction and Destruction
the functions will pass back errors through it instead of setting
a Python exception.
+ :c:func:`NpyIter_Deallocate` must be called for each copy. One call to
+ :c:func:`NpyIter_Close` is sufficient to trigger writeback resolution for
+ all copies since they share buffers.
+
.. c:function:: int NpyIter_RemoveAxis(NpyIter* iter, int axis)``
Removes an axis from iteration. This requires that
@@ -761,8 +765,10 @@ Construction and Destruction
.. c:function:: int NpyIter_Close(NpyIter* iter)
- Resolves any needed writeback resolution. Must be called before
- ``NpyIter_Deallocate``. After this call it is not safe to use the operands.
+ Resolves any needed writeback resolution. Should be called before
+ :c:func::`NpyIter_Deallocate`. After this call it is not safe to use the operands.
+ When using :c:func:`NpyIter_Copy`, only one call to :c:func:`NpyIter_Close`
+ is sufficient to resolve any writebacks, since the copies share buffers.
Returns ``0`` or ``-1`` if unsuccessful.
@@ -770,10 +776,10 @@ Construction and Destruction
Deallocates the iterator object.
- `NpyIter_Close` should be called before this. If not, and if writeback is
- needed, it will be performed at this point in order to maintain
+ :c:func:`NpyIter_Close` should be called before this. If not, and if
+ writeback is needed, it will be performed at this point in order to maintain
backward-compatibility with older code, and a deprecation warning will be
- emmitted. Old code shuold be updated to call `NpyIter_Close` beforehand.
+ emmitted. Old code should be updated to call `NpyIter_Close` beforehand.
Returns ``NPY_SUCCEED`` or ``NPY_FAIL``.
diff --git a/numpy/add_newdocs.py b/numpy/add_newdocs.py
index 93a521658..eb76dc045 100644
--- a/numpy/add_newdocs.py
+++ b/numpy/add_newdocs.py
@@ -385,10 +385,11 @@ add_newdoc('numpy.core', 'nditer',
array([ 0.5, 1.5, 4.5, 9.5, 16.5])
If operand flags `"writeonly"` or `"readwrite"` are used the operands may
- be views into the original data with the WRITEBACKIFCOPY flag. In this case
- nditer must be used as a context manager. The temporary
- data will be written back to the original data when the `` __exit__``
- function is called but not before::
+ be views into the original data with the `WRITEBACKIFCOPY` flag. In this case
+ nditer must be used as a context manager or the nditer.close
+ method must be called before using the result. The temporary
+ data will be written back to the original data when the `__exit__`
+ function is called but not before:
>>> a = np.arange(6, dtype='i4')[::-2]
>>> with nditer(a, [],
@@ -405,7 +406,7 @@ add_newdoc('numpy.core', 'nditer',
references (like `x` in the example) may or may not share data with
the original data `a`. If writeback semantics were active, i.e. if
`x.base.flags.writebackifcopy` is `True`, then exiting the iterator
- will sever the connection between `x` and `a`, writing to `x` will
+ will sever the connection between `x` and `a`, writing to `x` will
no longer write to `a`. If writeback semantics are not active, then
`x.data` will still point at some part of `a.data`, and writing to
one will affect the other.
@@ -566,6 +567,11 @@ add_newdoc('numpy.core', 'nditer', ('close',
Resolve all writeback semantics in writeable operands.
+ See Also
+ --------
+
+ :ref:`nditer-context-manager`
+
"""))
diff --git a/numpy/core/code_generators/numpy_api.py b/numpy/core/code_generators/numpy_api.py
index 157fa3447..6cfbbbcc7 100644
--- a/numpy/core/code_generators/numpy_api.py
+++ b/numpy/core/code_generators/numpy_api.py
@@ -6,7 +6,7 @@ Each dictionary contains name -> index pair.
Whenever you change one index, you break the ABI (and the ABI version number
should be incremented). Whenever you add an item to one of the dict, the API
needs to be updated in both setup_common.py and by adding an appropriate
-entry to cversion.txt (generate the hash via "python cversions.py".
+entry to cversion.txt (generate the hash via "python cversions.py").
When adding a function, make sure to use the next integer not used as an index
(in case you use an existing index or jump, the build will stop and raise an
diff --git a/numpy/core/src/multiarray/arrayobject.c b/numpy/core/src/multiarray/arrayobject.c
index 69538c6b7..6f4d3d349 100644
--- a/numpy/core/src/multiarray/arrayobject.c
+++ b/numpy/core/src/multiarray/arrayobject.c
@@ -86,7 +86,8 @@ NPY_NO_EXPORT int
PyArray_SetUpdateIfCopyBase(PyArrayObject *arr, PyArrayObject *base)
{
int ret;
- /* 2017-Nov-10 1.14 */
+ /* 2017-Nov -10 1.14 (for PyPy only) */
+ /* 2018-April-21 1.15 (all Python implementations) */
if (DEPRECATE("PyArray_SetUpdateIfCopyBase is deprecated, use "
"PyArray_SetWritebackIfCopyBase instead, and be sure to call "
"PyArray_ResolveWritebackIfCopy before the array is deallocated, "
diff --git a/numpy/core/tests/test_nditer.py b/numpy/core/tests/test_nditer.py
index 77c26eacf..b6b1c0f31 100644
--- a/numpy/core/tests/test_nditer.py
+++ b/numpy/core/tests/test_nditer.py
@@ -811,7 +811,7 @@ def test_iter_nbo_align_contig():
assert_equal(i.operands[0], a)
i.operands[0][:] = 2
assert_equal(au, [2]*6)
- i = None # should not raise a DeprecationWarning
+ del i # should not raise a warning
# Byte order change by requesting NBO
a = np.arange(6, dtype='f4')
au = a.byteswap().newbyteorder()
@@ -2838,12 +2838,30 @@ def test_writebacks():
it = nditer(au, [],
[['readwrite', 'updateifcopy']],
casting='equiv', op_dtypes=[np.dtype('f4')])
- au = None
+ # reentering works
+ with it:
+ with it:
+ for x in it:
+ x[...] = 123
+
+ it = nditer(au, [],
+ [['readwrite', 'updateifcopy']],
+ casting='equiv', op_dtypes=[np.dtype('f4')])
+ # make sure exiting the inner context manager closes the iterator
+ with it:
+ with it:
+ for x in it:
+ x[...] = 123
+ assert_raises(ValueError, getattr, it, 'operands')
# do not crash if original data array is decrefed
+ it = nditer(au, [],
+ [['readwrite', 'updateifcopy']],
+ casting='equiv', op_dtypes=[np.dtype('f4')])
+ del au
with it:
for x in it:
x[...] = 123
- # make sure we cannot reenter the iterand
+ # make sure we cannot reenter the closed iterator
enter = it.__enter__
assert_raises(ValueError, enter)