-
-
Notifications
You must be signed in to change notification settings - Fork 10.8k
MAINT: Use the same exception for all bad axis requests #8584
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
730f87c
to
7ec4d8f
Compare
* 0 <= *axis < ndim, and returns 0. | ||
*/ | ||
static NPY_INLINE int | ||
check_and_adjust_axis(int *axis, int ndim) |
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.
adjust
seems to imply something always happens; how about check_and_normalize_axis
. Or just normalize_axis
?
PyErr_Format(PyExc_ValueError, | ||
"'axis' entry %d is out of bounds [-%d, %d)", | ||
axis_orig, ndim, ndim); | ||
if (check_and_adjust_axis(&axis, ndim) < 0) { |
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.
Here and below: check for status using != 0
? Or just omit the comparison altogether (as above in error_converting(axis)
axis_orig, ndim, ndim); | ||
return NPY_FAIL; | ||
if (check_and_adjust_axis(&axis, ndim) < 0) { | ||
return NULL; |
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.
Shouldn't one still return NPY_FAIL
here?
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.
Good catch
PyErr_Format(PyExc_ValueError, "axis(=%d) out of bounds", axis_orig); | ||
return -1; | ||
if (check_and_adjust_axis(&axis, n) < 0) { | ||
return NULL; |
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.
Same here: I think the return argument shouldn't change, i.e., return -1
here?
PyErr_Format(PyExc_ValueError, "axis(=%d) out of bounds", axis_orig); | ||
return -1; | ||
if (check_and_adjust_axis(&axis, n) < 0) { | ||
return NULL; |
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.
idem
"axis(=%d) out of bounds", axis); | ||
goto fail; | ||
if (check_and_adjust_axis(&axis, nd) < 0) { | ||
return NULL; |
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.
Definitely should be goto fail
here, since several DECREF
s need to be done.
@@ -4109,6 +4103,24 @@ array_may_share_memory(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject * | |||
return array_shares_memory_impl(args, kwds, NPY_MAY_SHARE_BOUNDS, 0); | |||
} | |||
|
|||
static PyObject * |
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 presume this is for follow up in python code? If so, I think it should be part of a follow-up PR, not be done here (if only to give a chance to discuss whether it is best to go through C
for something like this).
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've added the python stuff to this PR now...
This makes sense to me, but note that I think you'll have to ensure the code keeps behaving the same in case of failure. As you will have seen, I suggest postponing the method to a possible python follow-up PR. There are also a few more places where the code could be used: |
Actually, looking more closely at anything that includes |
p.s. Well, the function is very similar but doesn't exactly do the same thing, since that one checks an index rather than an axis, but with the same logic. I'm not sure I understand the threading stuff... |
p.p.s. And I really should learn to read commit messages -- you of course refer to |
7ec4d8f
to
29c22a7
Compare
Wow, that was a clumsy bunch of mistakes. Thanks for catching me out!
Yep - I've addressed your concerns in an edit to the original post.
Great, thanks for spotting those - I'll add a fixup commit for those shortly
Meaning what? Produce the same error message? |
No, just that what the code returns (error code or otherwise) remains the same (see in-line comments) |
a9c76e6
to
353107a
Compare
I like how much the code cleans up using this! A few things I'm uncertain about:
|
|
Grrr, github is now notifying me of commits without referencing a PR. It's an "improvement" too far. |
@charris : that's my fault for doing some url mangling in order to comment on a commit and not a diff line |
Hmm, possibly one needs |
More seriously: which |
@mhvk: Look in the test results before the commit that claims to fix them ;) |
numpy/core/numeric.py
Outdated
@@ -1527,15 +1527,12 @@ def rollaxis(a, axis, start=0): | |||
|
|||
""" | |||
n = a.ndim | |||
if axis < 0: | |||
axis += n | |||
axis = resolve_axis(axis, n) | |||
if start < 0: | |||
start += n |
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.
This has always struck me as a bug - IMO, rollaxis(np.zeros(3,4,5), 0, -1).shape == (4, 5, 3)
, ie -1
should mean "move this axis to the end".
This would be consistent with the way that np.stack(..., axis=-1)
means insert an axis at the end. The fact that this is the only place that can't use resolve_axis
sort of backs up the fact that this is non-intuitive and breaks the pattern.
I would like this to be written start = resolve_axis(axis, n+1)
, but that would be a breaking change.
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, I have been caught by that too, realising only slowly that I had to put array.ndim
if I wanted something moved to the last axis. I think this is partially why we now have moveaxis
-- in any case, I think it is fine to leave this one untouched!
I did... E.g., https://travis-ci.org/numpy/numpy/jobs/199817411 (2 errors only). But now I look at the appveyor results, I see that there are also 3 errors in |
This appveyor page shows the bug travis now fails due to warnings. |
@eric-wieser - with #8590, you should not need to fix |
004f72c
to
de3a75f
Compare
I still need to swap |
I don't think |
Are we not essentially subscripting What would be an appropriate name/prefix for |
I think I'd also pick |
Ok, renamed to |
I did not follow this much, but I have a believe that someone will find any weird function and think its fine to use it. So, unless we are fine with people possibly starting to use it, adding an underscore to underscore ;) that its considered private just seems like a save bet. |
@seberg: I'd argue that this is something we actually want people to use - for anyone writing their own function taking an |
Ok, sounds fine to me, it was more of an "if we are not sure its stable API" thing, to be honest. |
I think I agree with @eric-wieser - it is good if this gets used (but would not quite yet put it at the top level) |
Thanks for adding the docstring; I think all is set now, so will merge. |
Should this have had a release note, or will that be handled during the release stage? |
Hmm, yes, probably big enough to have a release note... Now best to do separately? |
Yes, should have a release note as it is a behavior change. The type warning for the |
@mhvk It's in, so will need a separate PR for the release note. |
This appears to cause a crash in scipy, gh-8671 |
if (axis < 0 || axis >= PyArray_NDIM(ap)) { | ||
PyErr_SetString(PyExc_ValueError, | ||
"invalid axis for this array"); | ||
if (check_and_adjust_axis(&axis, PyArray_NDIM(ap)) < 0) { |
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.
axis
is npy_intp
, not int
.
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.
Maybe useful to double check this everywhere.
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.
Which do you think I should accept in check_and_adjust_axis
? Sometimes axis can be int
, sometimes it is intp
. Obviously I can introduce some locals for conversion, but I still need to choose which is right
@eric-wieser Time to get going on that fix. |
DOC: Add missing release note for #8584
Lots of functions accept an axis argument that can be negative, implying wraparound. The same few lines of code are repeated all over the place to implement this.
When the axis is just plain invalid, an error is raised. However:
IndexError
, others raiseValueError
. This PR somehwat arbitrarily assumes thatIndexError
is the right type of exception to raise.axis < -ndim
It seems it would be better to put all this in one place.
This was previously filed as an issue at #8583. Function name and location inspired by
check_and_adjust_index
.check_and_adjust_index(...) < 0
is used to matchcheck_and_adjust_axis(...) < 0
.This was previously discussed on the mailing list by @charris and @seberg