summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/source/reference/arrays.nditer.rst25
-rw-r--r--numpy/add_newdocs.py28
-rw-r--r--numpy/core/src/multiarray/nditer_pywrap.c38
-rw-r--r--numpy/core/tests/test_nditer.py13
4 files changed, 64 insertions, 40 deletions
diff --git a/doc/source/reference/arrays.nditer.rst b/doc/source/reference/arrays.nditer.rst
index 911ff6671..acad29b11 100644
--- a/doc/source/reference/arrays.nditer.rst
+++ b/doc/source/reference/arrays.nditer.rst
@@ -394,10 +394,10 @@ parameter support.
.. admonition:: Example
>>> def square(a):
- ... it = np.nditer([a, None])
- ... for x, y in it:
- ... y[...] = x*x
- ... return it.operands[1]
+ ... with np.nditer([a, None]) as it:
+ ... for x, y in it:
+ ... y[...] = x*x
+ ... return it.operands[1]
...
>>> square([1,2,3])
array([1, 4, 9])
@@ -490,10 +490,12 @@ Everything to do with the outer product is handled by the iterator setup.
>>> b = np.arange(8).reshape(2,4)
>>> it = np.nditer([a, b, None], flags=['external_loop'],
... op_axes=[[0, -1, -1], [-1, 0, 1], None])
- >>> for x, y, z in it:
- ... z[...] = x*y
+ >>> with it:
+ ... for x, y, z in it:
+ ... z[...] = x*y
+ ... result = it.operands[2] # same as z
...
- >>> it.operands[2]
+ >>> result
array([[[ 0, 0, 0, 0],
[ 0, 0, 0, 0]],
[[ 0, 1, 2, 3],
@@ -501,6 +503,9 @@ Everything to do with the outer product is handled by the iterator setup.
[[ 0, 2, 4, 6],
[ 8, 10, 12, 14]]])
+Note that once the iterator is closed we can not access :func:`operands <nditer.operands>`
+and must use a reference created inside the context manager.
+
Reduction Iteration
-------------------
@@ -540,8 +545,9 @@ sums along the last axis of `a`.
... it.operands[1][...] = 0
... for x, y in it:
... y[...] += x
+ ... result = it.operands[1]
...
- ... it.operands[1]
+ >>> result
array([[ 6, 22, 38],
[54, 70, 86]])
>>> np.sum(a, axis=2)
@@ -575,8 +581,9 @@ buffering.
... it.reset()
... for x, y in it:
... y[...] += x
+ ... result = it.operands[1]
...
- ... it.operands[1]
+ >>> result
array([[ 6, 22, 38],
[54, 70, 86]])
diff --git a/numpy/add_newdocs.py b/numpy/add_newdocs.py
index 8e8339355..93a521658 100644
--- a/numpy/add_newdocs.py
+++ b/numpy/add_newdocs.py
@@ -257,6 +257,7 @@ add_newdoc('numpy.core', 'nditer',
dtypes : tuple of dtype(s)
The data types of the values provided in `value`. This may be
different from the operand data types if buffering is enabled.
+ Valid only before the iterator is closed.
finished : bool
Whether the iteration over the operands is finished or not.
has_delayed_bufalloc : bool
@@ -282,7 +283,8 @@ add_newdoc('numpy.core', 'nditer',
Size of the iterator.
itviews
Structured view(s) of `operands` in memory, matching the reordered
- and optimized iterator access pattern.
+ and optimized iterator access pattern. Valid only before the iterator
+ is closed.
multi_index
When the "multi_index" flag was used, this property
provides access to the index. Raises a ValueError if accessed
@@ -292,7 +294,8 @@ add_newdoc('numpy.core', 'nditer',
nop : int
The number of iterator operands.
operands : tuple of operand(s)
- The array(s) to be iterated over.
+ The array(s) to be iterated over. Valid only before the iterator is
+ closed.
shape : tuple of ints
Shape tuple, the shape of the iterator.
value
@@ -331,12 +334,12 @@ add_newdoc('numpy.core', 'nditer',
it = np.nditer([x, y, out], [],
[['readonly'], ['readonly'], ['writeonly','allocate']])
+ with it:
+ while not it.finished:
+ addop(it[0], it[1], out=it[2])
+ it.iternext()
- while not it.finished:
- addop(it[0], it[1], out=it[2])
- it.iternext()
-
- return it.operands[2]
+ return it.operands[2]
Here is an example outer product function::
@@ -351,7 +354,7 @@ add_newdoc('numpy.core', 'nditer',
with it:
for (a, b, c) in it:
mulop(a, b, out=c)
- return it.operands[2]
+ return it.operands[2]
>>> a = np.arange(2)+1
>>> b = np.arange(3)+1
@@ -374,7 +377,7 @@ add_newdoc('numpy.core', 'nditer',
while not it.finished:
it[0] = lamdaexpr(*it[1:])
it.iternext()
- return it.operands[0]
+ return it.operands[0]
>>> a = np.arange(5)
>>> b = np.ones(5)
@@ -430,6 +433,13 @@ add_newdoc('numpy.core', 'nditer', ('copy',
"""))
+add_newdoc('numpy.core', 'nditer', ('operands',
+ """
+ operands[`Slice`]
+
+ The array(s) to be iterated over. Valid only before the iterator is closed.
+ """))
+
add_newdoc('numpy.core', 'nditer', ('debug_print',
"""
debug_print()
diff --git a/numpy/core/src/multiarray/nditer_pywrap.c b/numpy/core/src/multiarray/nditer_pywrap.c
index d36be61f5..4505e645b 100644
--- a/numpy/core/src/multiarray/nditer_pywrap.c
+++ b/numpy/core/src/multiarray/nditer_pywrap.c
@@ -20,16 +20,14 @@
typedef struct NewNpyArrayIterObject_tag NewNpyArrayIterObject;
-enum NPYITER_CONTEXT {CONTEXT_NOTENTERED, CONTEXT_INSIDE, CONTEXT_EXITED};
-
struct NewNpyArrayIterObject_tag {
PyObject_HEAD
/* The iterator */
NpyIter *iter;
/* Flag indicating iteration started/stopped */
char started, finished;
- /* iter must used as a context manager if writebackifcopy semantics used */
- char managed;
+ /* iter operands cannot be referenced if iter is closed */
+ npy_bool is_closed;
/* Child to update for nested iteration */
NewNpyArrayIterObject *nested_child;
/* Cached values from the iterator */
@@ -89,7 +87,7 @@ npyiter_new(PyTypeObject *subtype, PyObject *args, PyObject *kwds)
if (self != NULL) {
self->iter = NULL;
self->nested_child = NULL;
- self->managed = CONTEXT_NOTENTERED;
+ self->is_closed = 0;
}
return (PyObject *)self;
@@ -1419,7 +1417,7 @@ static PyObject *npyiter_value_get(NewNpyArrayIterObject *self)
ret = npyiter_seq_item(self, 0);
}
else {
- if (self->managed == CONTEXT_EXITED) {
+ if (self->is_closed) {
PyErr_SetString(PyExc_ValueError,
"Iterator is closed");
return NULL;
@@ -1454,7 +1452,7 @@ static PyObject *npyiter_operands_get(NewNpyArrayIterObject *self)
"Iterator is invalid");
return NULL;
}
- if (self->managed == CONTEXT_EXITED) {
+ if (self->is_closed) {
PyErr_SetString(PyExc_ValueError,
"Iterator is closed");
return NULL;
@@ -1489,7 +1487,7 @@ static PyObject *npyiter_itviews_get(NewNpyArrayIterObject *self)
return NULL;
}
- if (self->managed == CONTEXT_EXITED) {
+ if (self->is_closed) {
PyErr_SetString(PyExc_ValueError,
"Iterator is closed");
return NULL;
@@ -1517,7 +1515,8 @@ static PyObject *npyiter_itviews_get(NewNpyArrayIterObject *self)
static PyObject *
npyiter_next(NewNpyArrayIterObject *self)
{
- if (self->iter == NULL || self->iternext == NULL || self->finished) {
+ if (self->iter == NULL || self->iternext == NULL ||
+ self->finished || self->is_closed) {
return NULL;
}
@@ -1912,7 +1911,7 @@ static PyObject *npyiter_dtypes_get(NewNpyArrayIterObject *self)
return NULL;
}
- if (self->managed == CONTEXT_EXITED) {
+ if (self->is_closed) {
PyErr_SetString(PyExc_ValueError,
"Iterator is closed");
return NULL;
@@ -2014,7 +2013,7 @@ npyiter_seq_item(NewNpyArrayIterObject *self, Py_ssize_t i)
return NULL;
}
- if (self->managed == CONTEXT_EXITED) {
+ if (self->is_closed) {
PyErr_SetString(PyExc_ValueError,
"Iterator is closed");
return NULL;
@@ -2104,7 +2103,7 @@ npyiter_seq_slice(NewNpyArrayIterObject *self,
return NULL;
}
- if (self->managed == CONTEXT_EXITED) {
+ if (self->is_closed) {
PyErr_SetString(PyExc_ValueError,
"Iterator is closed");
return NULL;
@@ -2170,7 +2169,7 @@ npyiter_seq_ass_item(NewNpyArrayIterObject *self, Py_ssize_t i, PyObject *v)
return -1;
}
- if (self->managed == CONTEXT_EXITED) {
+ if (self->is_closed) {
PyErr_SetString(PyExc_ValueError,
"Iterator is closed");
return -1;
@@ -2250,7 +2249,7 @@ npyiter_seq_ass_slice(NewNpyArrayIterObject *self, Py_ssize_t ilow,
return -1;
}
- if (self->managed == CONTEXT_EXITED) {
+ if (self->is_closed) {
PyErr_SetString(PyExc_ValueError,
"Iterator is closed");
return -1;
@@ -2307,7 +2306,7 @@ npyiter_subscript(NewNpyArrayIterObject *self, PyObject *op)
return NULL;
}
- if (self->managed == CONTEXT_EXITED) {
+ if (self->is_closed) {
PyErr_SetString(PyExc_ValueError,
"Iterator is closed");
return NULL;
@@ -2362,7 +2361,7 @@ npyiter_ass_subscript(NewNpyArrayIterObject *self, PyObject *op,
return -1;
}
- if (self->managed == CONTEXT_EXITED) {
+ if (self->is_closed) {
PyErr_SetString(PyExc_ValueError,
"Iterator is closed");
return -1;
@@ -2402,11 +2401,10 @@ npyiter_enter(NewNpyArrayIterObject *self)
PyErr_SetString(PyExc_RuntimeError, "operation on non-initialized iterator");
return NULL;
}
- if (self->managed == CONTEXT_EXITED) {
- PyErr_SetString(PyExc_ValueError, "cannot reuse iterator after exit");
+ if (self->is_closed) {
+ PyErr_SetString(PyExc_ValueError, "cannot reuse closed iterator");
return NULL;
}
- self->managed = CONTEXT_INSIDE;
Py_INCREF(self);
return (PyObject *)self;
}
@@ -2420,6 +2418,7 @@ npyiter_close(NewNpyArrayIterObject *self)
Py_RETURN_NONE;
}
ret = NpyIter_Close(iter);
+ self->is_closed = 1;
if (ret < 0) {
return NULL;
}
@@ -2429,7 +2428,6 @@ npyiter_close(NewNpyArrayIterObject *self)
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);
}
diff --git a/numpy/core/tests/test_nditer.py b/numpy/core/tests/test_nditer.py
index bc9456536..77c26eacf 100644
--- a/numpy/core/tests/test_nditer.py
+++ b/numpy/core/tests/test_nditer.py
@@ -2847,7 +2847,7 @@ def test_writebacks():
enter = it.__enter__
assert_raises(ValueError, enter)
-def test_close():
+def test_close_equivalent():
''' using a context amanger and using nditer.close are equivalent
'''
def add_close(x, y, out=None):
@@ -2856,8 +2856,10 @@ def test_close():
[['readonly'], ['readonly'], ['writeonly','allocate']])
for (a, b, c) in it:
addop(a, b, out=c)
+ ret = it.operands[2]
it.close()
- return it.operands[2]
+ return ret
+
def add_context(x, y, out=None):
addop = np.add
it = np.nditer([x, y, out], [],
@@ -2871,6 +2873,13 @@ def test_close():
z = add_context(range(5), range(5))
assert_equal(z, range(0, 10, 2))
+def test_close_raises():
+ it = np.nditer(np.arange(3))
+ assert_equal (next(it), 0)
+ it.close()
+ assert_raises(StopIteration, next, it)
+ assert_raises(ValueError, getattr, it, 'operands')
+
def test_warn_noclose():
a = np.arange(6, dtype='f4')
au = a.byteswap().newbyteorder()