-
-
Notifications
You must be signed in to change notification settings - Fork 10.8k
DOC: cleanup documentation, continuation of nditer PR #9998 #10949
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
doc/release/1.15.0-notes.rst
Outdated
@@ -118,7 +118,7 @@ C API changes | |||
============= | |||
|
|||
``NpyIter_Close`` has been added and should be called before | |||
``NpyIter_Dealloc`` to resolve possible writeback-enabled arrays. | |||
``NpyIter_Deallocate`` to resolve possible writeback-enabled arrays. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would be tempted to add a bullet point to match the other sections without headers in this document.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added
with it: | ||
with it: | ||
for x in it: | ||
x[...] = 123 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What should happen if the outer with
continues to use it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it will error out that it is closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That seems to be consistent with file
and contextlib.closing
. Can you test that too then?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added
numpy/core/tests/test_nditer.py
Outdated
with it: | ||
for x in it: | ||
x[...] = 123 | ||
# make sure we cannot reenter the iterand | ||
# make sure we cannot reenter the closed iterand |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd argue this should be iterator
, not iterand
- I'd call it.operands
the iterands.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
11c71b9
to
2e3e96b
Compare
squashed after last fix from review |
In addition to these fixups, I think I had in mind some other additions to the docs which would be good to put in this PR.... I need to go back and review what those were |
@@ -78,6 +78,8 @@ order='C' for C order and order='F' for Fortran order. | |||
... | |||
0 3 1 4 2 5 | |||
|
|||
.. _nditer-context-manager: | |||
|
|||
Modifying Array Values | |||
---------------------- | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The next few paragraph below this point could use some editing. It uses the term "view" in a possbily misleading way, since the writeback arrays are not actually views, they are temp buffers.
Here's my try going through it:
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.
When assigning to the buffers returned by the nditer, also remember that you must
perform the assignment using the ellipsis index, as in `x[...] = `, rather than
simply reassigning the variable name as in `x = `.
[Note: Consider deleting this last paragaph entirely?]
Also some tweaks in I was originally thinking of adding more wordy description of the need for |
I guess maybe in the I think that covers all the doc changes I had in mind. |
a5c2458
to
c410fdc
Compare
Also see #10951 which adds more documentation for |
Needs a rebase. |
rebased off master and squashed |
``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` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is NpyIter_Copy
exposed to python?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, as it.copy()
. Should I expand the documentation of it.copy
to mention writeback considerations?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Won'y np.nditer.__del__
complain if you only call close
on the original and not the copy?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no, the complaint is on the operands, which are shared:
def test_copy():
a = np.arange(6, dtype='f4')
au = a.byteswap().newbyteorder()
it1 = np.nditer(au, [], [['readwrite', 'updateifcopy']],
casting='equiv', op_dtypes=[np.dtype('f4')])
it2 = it1.copy()
x = it1.operands[0]
assert x is it2.operands[0]
assert x.flags.writebackifcopy is True
it1.close()
assert x.flags.writebackifcopy is False
commenting the last two lines does emit a warning
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So requiring a context manager wouldn't have worked here anyway?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure what you mean. The context manager resolves operands on exit:
def test_copy():
a = np.arange(6, dtype='f4')
au = a.byteswap().newbyteorder()
it1 = np.nditer(au, [], [['readwrite', 'updateifcopy']],
casting='equiv', op_dtypes=[np.dtype('f4')])
with it1:
it2 = it1.copy()
x = it1.operands[0]
assert x is it2.operands[0]
assert x.flags.writebackifcopy is True
assert x.flags.writebackifcopy is False
# no warning on it2 deallocate, the operand is resolved via it1
Note that nesting the context managers, it1
is still open even though it2
is closed
def test_copy():
a = np.arange(6, dtype='f4')
au = a.byteswap().newbyteorder()
it1 = np.nditer(au, [], [['readwrite', 'updateifcopy']],
casting='equiv', op_dtypes=[np.dtype('f4')])
with it1:
with it1.copy() as it2:
x = it1.operands[0]
assert x is it2.operands[0]
assert x.flags.writebackifcopy is True
assert x.flags.writebackifcopy is False
y = it1.operands[0] # can still access it1's operand, it is not closed
assert y is x
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we allow access to it1.operands
and forbid access to it2.operands
on that line, when they point to the same object? Either they're both invalid, or neither of them are invalid.
Ie, hasn't the writeback already happened, in which case it.operands
is now temporary garbage that won't ever make it back to au
?
It seems like preventing access to it.operands
after it.close()
is called has failed to actually protect against access to a post-writeback array (and so there's no point in imposing the restriction at all)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Disallowing new access to operands is a best-effort attempt, and is not fool-proof. x
also no longer points to au
after writeback resolution. Issue #10956 proposed a change to deal with that. This PR was meant to document the way it works now, with the warts.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, this PR looks good to me as a doc improvement, but I'm worried that writebackifcopy might have been the wrong design after all. In my opinion, you should have to call close on both iterators for writebacks to be resolved, which would require a reference count.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
note added to issue #10958
@ahaldane, I'm going to leave this for you to merge |
All right, looks good. Thanks Matti! |
After merging PR #9998, careful review picked up some mistakes in documentation