summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormattip <matti.picus@gmail.com>2018-04-20 13:11:32 +0300
committermattip <matti.picus@gmail.com>2018-04-20 13:11:32 +0300
commitc26d273c1204d75fe5ab2ce9591e1b0b0b0880e1 (patch)
treed6fe1169553146618706c6fbce7c9eae193e384c
parent894dcab37ea2df285c6f48eb9b019a528b803cb5 (diff)
downloadnumpy-c26d273c1204d75fe5ab2ce9591e1b0b0b0880e1.tar.gz
ENH: add nditer.close as per review
-rw-r--r--doc/release/1.15.0-notes.rst15
-rw-r--r--doc/source/reference/arrays.nditer.rst9
-rw-r--r--numpy/add_newdocs.py2
-rw-r--r--numpy/core/src/multiarray/nditer_pywrap.c21
-rw-r--r--numpy/core/tests/test_nditer.py36
5 files changed, 61 insertions, 22 deletions
diff --git a/doc/release/1.15.0-notes.rst b/doc/release/1.15.0-notes.rst
index 963f72209..1bd0673b5 100644
--- a/doc/release/1.15.0-notes.rst
+++ b/doc/release/1.15.0-notes.rst
@@ -52,10 +52,6 @@ Deprecations
In the future, it might return a different result. Use `np.sum(np.from_iter(generator))`
or the built-in Python `sum` instead.
-* ``nditer`` use outside of a context manager where the ``nditer`` has writable
- arrays is deprecated. This situation proves difficult to detect, the
- ``DeprecationWarning`` may only be emitted when deallocating the array.
-
* Users of the C-API should call ``PyArrayResolveWriteBackIfCopy`` or
``PyArray_DiscardWritbackIfCopy`` on any array with the ``WRITEBACKIFCOPY``
flag set, before the array is deallocated. A deprecation warning will be
@@ -63,9 +59,9 @@ Deprecations
* Users of ``nditer`` should use the nditer object as a context manager
anytime one of the iterator operands is writeable, so that numpy can
- manage writeback semantics. A deprecation warning will be emitted if a
- context manager is not used in these cases. Users of the C-API should call
- ``NpyIter_Close`` before ``NpyIter_Dealloc``.
+ 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``.
Future Changes
@@ -84,7 +80,10 @@ copy, nditer later writes those changes back into your actual array. Currently,
this writeback occurs when the array objects are garbage collected, which makes
this API error-prone on CPython and entirely broken on PyPy. Therefore,
``nditer`` should now be used as a context manager whenever using ``nditer``
-with writeable arrays (``with np.nditer(...) as it: ...``).
+with writeable arrays (``with np.nditer(...) as it: ...``). You may also
+explicitly call ``it.close()`` for cases where a context manager is unusable,
+for instance in generator expressions.
+
Numpy has switched to using pytest instead of nose for testing
--------------------------------------------------------------
The last nose release was 1.3.7 in June, 2015, and development of that tool has
diff --git a/doc/source/reference/arrays.nditer.rst b/doc/source/reference/arrays.nditer.rst
index 710ad8be6..911ff6671 100644
--- a/doc/source/reference/arrays.nditer.rst
+++ b/doc/source/reference/arrays.nditer.rst
@@ -85,9 +85,12 @@ 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 be used as a context
-manager, and the temporary data will be written back to the original array
-when the `__exit__` function is called.
+`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
diff --git a/numpy/add_newdocs.py b/numpy/add_newdocs.py
index 193093109..a48b76a8d 100644
--- a/numpy/add_newdocs.py
+++ b/numpy/add_newdocs.py
@@ -553,7 +553,7 @@ add_newdoc('numpy.core', 'nditer', ('close',
"""
close()
- Resolve all writeback semantics in operands.
+ Resolve all writeback semantics in writeable operands.
"""))
diff --git a/numpy/core/src/multiarray/nditer_pywrap.c b/numpy/core/src/multiarray/nditer_pywrap.c
index 7e4e715f0..8efae59a6 100644
--- a/numpy/core/src/multiarray/nditer_pywrap.c
+++ b/numpy/core/src/multiarray/nditer_pywrap.c
@@ -2421,21 +2421,28 @@ npyiter_enter(NewNpyArrayIterObject *self)
}
static PyObject *
-npyiter_exit(NewNpyArrayIterObject *self, PyObject *args)
+npyiter_close(NewNpyArrayIterObject *self)
{
- int retval;
+ NpyIter *iter = self->iter;
+ int ret;
if (self->iter == NULL) {
Py_RETURN_NONE;
}
- self->managed = CONTEXT_EXITED;
- /* even if called via exception handling, writeback any data */
- retval = NpyIter_Close(self->iter);
- if (retval < 0) {
+ ret = NpyIter_Close(iter);
+ if (ret < 0) {
return NULL;
}
Py_RETURN_NONE;
}
+static PyObject *
+npyiter_exit(NewNpyArrayIterObject *self, PyObject *args)
+{
+ self->managed = CONTEXT_EXITED;
+ /* even if called via exception handling, writeback any data */
+ return npyiter_close(self);
+}
+
static PyMethodDef npyiter_methods[] = {
{"reset",
(PyCFunction)npyiter_reset,
@@ -2465,6 +2472,8 @@ static PyMethodDef npyiter_methods[] = {
METH_NOARGS, NULL},
{"__exit__", (PyCFunction)npyiter_exit,
METH_VARARGS, NULL},
+ {"close", (PyCFunction)npyiter_close,
+ METH_VARARGS, NULL},
{NULL, NULL, 0, NULL},
};
diff --git a/numpy/core/tests/test_nditer.py b/numpy/core/tests/test_nditer.py
index 1973c73a2..bc9456536 100644
--- a/numpy/core/tests/test_nditer.py
+++ b/numpy/core/tests/test_nditer.py
@@ -2330,8 +2330,7 @@ class TestIterNested(object):
assert_equal(vals, [[0, 1, 2], [3, 4, 5]])
vals = None
- # writebackifcopy
- # XXX ugly - is there a better way? np.nested_iter returns a tuple
+ # writebackifcopy - using conext manager
a = arange(6, dtype='f4').reshape(2, 3)
i, j = np.nested_iters(a, [[0], [1]],
op_flags=['readwrite', 'updateifcopy'],
@@ -2345,6 +2344,22 @@ class TestIterNested(object):
assert_equal(a, [[0, 1, 2], [3, 4, 5]])
assert_equal(a, [[1, 2, 3], [4, 5, 6]])
+ # writebackifcopy - using close()
+ a = arange(6, dtype='f4').reshape(2, 3)
+ i, j = np.nested_iters(a, [[0], [1]],
+ op_flags=['readwrite', 'updateifcopy'],
+ casting='same_kind',
+ op_dtypes='f8')
+ assert_equal(j[0].dtype, np.dtype('f8'))
+ for x in i:
+ for y in j:
+ y[...] += 1
+ assert_equal(a, [[0, 1, 2], [3, 4, 5]])
+ i.close()
+ j.close()
+ assert_equal(a, [[1, 2, 3], [4, 5, 6]])
+
+
def test_dtype_buffered(self):
# Test nested iteration with buffering to change dtype
@@ -2833,7 +2848,17 @@ def test_writebacks():
assert_raises(ValueError, enter)
def test_close():
- def iter_add_py(x, y, out=None):
+ ''' using a context amanger and using nditer.close are equivalent
+ '''
+ def add_close(x, y, out=None):
+ addop = np.add
+ it = np.nditer([x, y, out], [],
+ [['readonly'], ['readonly'], ['writeonly','allocate']])
+ for (a, b, c) in it:
+ addop(a, b, out=c)
+ it.close()
+ return it.operands[2]
+ def add_context(x, y, out=None):
addop = np.add
it = np.nditer([x, y, out], [],
[['readonly'], ['readonly'], ['writeonly','allocate']])
@@ -2841,7 +2866,10 @@ def test_close():
for (a, b, c) in it:
addop(a, b, out=c)
return it.operands[2]
- z = iter_add_py(range(5), range(5))
+ z = add_close(range(5), range(5))
+ assert_equal(z, range(0, 10, 2))
+ z = add_context(range(5), range(5))
+ assert_equal(z, range(0, 10, 2))
def test_warn_noclose():
a = np.arange(6, dtype='f4')