MAINT: Unify casting error creation (outside the iterator)#16133
Conversation
e9d6813 to
fa5d523
Compare
There was a problem hiding this comment.
Python 3 has a much shorter way to write all this, with %R:
| errmsg = PyUString_FromString("Cannot cast scalar from "); | |
| PyErr_Format(PyExc_TypeError, "Cannot cast scalar from %R to %R according to the rule %s", ...); |
There was a problem hiding this comment.
Good point, didn't really try to fix up that code, had just moved it.
cbc582a to
63f54de
Compare
| * PyArray_CanCastArrayTo result). | ||
| */ | ||
| NPY_NO_EXPORT void | ||
| npy_set_invalid_cast_error( |
There was a problem hiding this comment.
I wonder if this function is even worth it at this point, given it would be a one-liner at each call site.
There was a problem hiding this comment.
Yeah, have to admit, I am not sure. On the other hand, that way at least the error message is not spelled out in 5 places.
There was a problem hiding this comment.
Missed how many times this was used, that seems fair. Maybe replace scalar with char const *what, which makes it possible for the caller to pass more info to put in the message, which will also make the implementation shorter.
There was a problem hiding this comment.
To be fair, 2 of those would be the concatenate fixup...
There was a problem hiding this comment.
Made that simplifictaion for the iterator code (which is arguably very similar), and adds another 1.5 occurances, which then are not covered here (so we have 2 "places" in the code).
The only real reason for this being useful is probably the scalar distinction, which is correct, but a bit wonky admittedly.
EDIT: Trivial duplication is at least super easy to grep for after all...
There was a problem hiding this comment.
Doesn't this now describe a 0d array as a scalar?
There was a problem hiding this comment.
Yes, but maybe no... that is what PyArray_CanCastArrayTo does. Its a bit annoying, it can be incorrect from the user-input perspective, but more correct due to value based casting of 0-D arrays. I.e. the 0-D array uses "scalar casting" rules right now, but the original input typically could have been either a scalar or an array.
There was a problem hiding this comment.
What's the code path that hits this error?
There was a problem hiding this comment.
Pretty much none... The only paths that should hit this, are arguably nonsense:
np.take([2], [1], out=np.array([1], dtype=np.uint64))
but even that example cannot get a "scalar" case.
from numpy.core import _rational_tests as rt
np.array(np.array(rt.rational(2)), dtype="M8")
should hit it IMO, but we currently consider all unsafe casts as possible, and in this case error when we notice that there is no way defined to do the actual casts. That is: In almost all cases we use force-casting, so it just doesn't matter.
There was a problem hiding this comment.
Presumably np.array(rt.rational(2), dtype="M8") is a case when the scalar message is justified?
There was a problem hiding this comment.
Mind adding one of these as a test?
There was a problem hiding this comment.
I can add the test, but it is basically adding it for the future (or excersizing a different error). Because "unsafe" casts are currently always considered possible, it errors later with:
ValueError: No cast function available
There was a problem hiding this comment.
I added a test for a value error, I am not sure its useful here to be honest. But likely enough we never had a test for that in any case...
There was a problem hiding this comment.
Just to be clear, my comment above from mobile (where multi-line comments are not possible) was questioning whether this would be better as:
| NPY_CASTING casting, npy_bool scalar) | |
| { | |
| char *msg; | |
| if (!scalar) { | |
| msg = "Cannot cast array data from %R to %R according to the rule %s"; | |
| } | |
| else { | |
| msg = "Cannot cast scalar from %R to %R according to the rule %s"; | |
| } | |
| PyErr_Format(PyExc_TypeError, | |
| msg, src_dtype, dst_dtype, npy_casting_to_string(casting)); | |
| NPY_CASTING casting, char const *what) | |
| { | |
| PyErr_Format(PyExc_TypeError, | |
| "Cannot cast %s from %R to %R according to the rule %s, | |
| what, src_dtype, dst_dtype, npy_casting_to_string(casting)); |
There was a problem hiding this comment.
if this is going to be limited to different msg's for scalar vs non scalar, i prefer the current way, where the msg is decided by npy_set_invalid_cast_error. If there are more else if switches added here though, maybe what as the argument would be better.
eric-wieser
left a comment
There was a problem hiding this comment.
Basically look fine, just a few nits. Nice to remove the duplication either way.
|
@seberg I think this is ready except for a few small nits. Should we put it in as-is? |
|
@seberg ping. |
|
Well, I don't really think there is anything to do, unless you dislike calling a 0D array "scalar", because in theory that is the casting rule we use (and typically we do not know if it used to be a scalar). I honestly do not care either way, and yes, I admit in most of the cases it is either clear cut, or just does not matter, so I can change it. |
|
LGTM - less code and more tests. |
|
Thanks @seberg |
This unifies the casting error machinery a bit (deleting some annoying duplication). In rare cases the error message is fixed (scalar -> array, in copyto). In other cases it will now print
array datainstead of justarray(arr.astype). Overall, it should be a simple code duplication-reduction though.