BUG: fix uint input for triu_indices and deprecate non-int - #30869
#30869BUG: fix uint input for triu_indices and deprecate non-int#30869
Conversation
|
|
||
| m = convert_if_unsigned(m) | ||
| n = convert_if_unsigned(n) | ||
| k = convert_if_unsigned(k) |
There was a problem hiding this comment.
Generally we don't like to force users who aren't affected by issues to pay the cost of fixing an issue. It seems better to fix this another way...
| def convert_if_unsigned(val): | ||
| if (hasattr(val, "dtype") and issubdtype(val.dtype, unsignedinteger) | ||
| and ndim(val) == 0): | ||
| return operator.index(val) |
There was a problem hiding this comment.
just a general note: this function should be defined at module level. You only do stuff like this if you're defining a closure that captures values in the enclusing scope. This function doesn't do that.
There was a problem hiding this comment.
I guess this is basically @seberg's solution from #29488 (comment) but it's not quite as clean as he envisioned.
There was a problem hiding this comment.
I get it and agree that the code should have been cleaner. Please tell me what approach can I take for this solution.
There was a problem hiding this comment.
Actually, I did not see any way of solving this without checking for the dtype (without changing the current behavior).
And, in the first commit I did not define any function but just checked for n's and m's dtype. But, then I thought that if k is an unsigned integer, it will again raise an error.
So, I defined the function to reduce the lines of code.
Now, if I remove the function and check the dtype of n, m and k because these kinds of conditional statements are used in some places, will it be okay?
There was a problem hiding this comment.
Maybe see the other PR, you could even pick up there. It was already very good, there was just one question that we either have to decide to break:
np.tri(3.5)
or add a DeprecationWarning. I would probably not add a helper, except maybe to give the warning itself (because we need it in a few places here). All it needs is a try/except.
It would be awesome if you could take the branch from the other closed PR and add commits to implement the DeprecationWarning.
b3115a4 to
2ddde15
Compare
|
@antareepsarkar can you please add a test (ideally in It isn't actually about floats as such, float is just the most likely kind of object coming in hitting this. Also add a comment |
@seberg |
|
There is actually a new mechanism that I think we can use now: Otherwise, it would be 2 typically, then +1 for every layer of helper function (the smallest one we can find), so in this case 3. But, the skip function is nicer (and the only thing that I might worry about is if I thought it was a warning that could happen in very hot paths, this isn't). |
Yes, but warnings at present require |
|
Is this from the So yeah, I think we can go with |
|
@seberg |
seberg
left a comment
There was a problem hiding this comment.
Thanks, great you tracked down how to keep CI passing! I would like to go back to the old operator.index pattern with a try/except though.
| if dtype_not_int and not isinstance(n, int): | ||
| warnings.warn( | ||
| (f"Cannot convert {type(n).__name__} safely to an integer." | ||
| "This will raise an error in future versions(Deprecated NumPy 2.4)"), |
There was a problem hiding this comment.
| "This will raise an error in future versions(Deprecated NumPy 2.4)"), | |
| "This will raise an error in future versions (Deprecated NumPy 2.5)"), |
| if hasattr(n, "dtype") and n.dtype.kind in "iu": | ||
| dtype_not_int = False | ||
|
|
||
| if dtype_not_int and not isinstance(n, int): |
There was a problem hiding this comment.
Nit, add a # Deprecated NumPy 2.5, 2026-...
| dtype_not_int = True | ||
|
|
||
| if hasattr(n, "dtype") and n.dtype.kind in "iu": | ||
| dtype_not_int = False |
There was a problem hiding this comment.
I suspect you have a reason, but why did you get away from operator.index?
What I wanted to nudge towards (sorry, sometimes I try to be a bit fuzzy), was to do:
try:
n = operator.index(n)
except TypeError:
n = int(n)
# successfully converted, but not via `operator.index()`
warnings.warn(...)
There was a problem hiding this comment.
I thought you wanted to use int() rather than operator.index #30869 (comment).
I will use operator.index now. The thing is, if we do int(n), it takes the floor of floats. But, previously the ceil was being taken i.e. now np.tri(3.14) will be np.tri(3) but before the change it was np.tri(4). So, it will be kind of a breaking change.
I just want to be sure from you what needs to be done in this case.
There was a problem hiding this comment.
Actually, I think we can just warn in the except block and not do anything as we just want to warn as of now. Anyways, it's upto you to decide what should be done.
There was a problem hiding this comment.
Ah! I had not realized that the arange led to the ceil being taken.
Sorry, probably the earlier iteration was actually the closest and I didn't think right. If operator.index() fails, we should just keep doing exactly what we were doing before.
(And I think that was right as nothing at all, I guess... sorry for slipping into thinking that wasn't right)
There was a problem hiding this comment.
Thanks.
Now, I have kind of a clear picture of what to do.
|
@seberg So, as you said, I will make things as before. Please tell me whether this needs a release note or not. And, does both the fix for |
|
|
||
| """ | ||
|
|
||
| if isinstance(k, unsignedinteger): |
There was a problem hiding this comment.
This unsignedinteger check is new, it looks a bit like a left-over?
As to your note about giving warning + error. Great observation! I could live with it, but you are right it isn't great in the, maybe unlikely, case anyone actually relies on the TypeError.
This would not have been a problem if int() wasn't a regression due to ceil.
I suppose we could change things to do something like:
try:
n = operator.index(n)
k = operator.index(k)
except TypeError:
deprecation_warning = True
else:
deprecation_warning = False
<old code at least until it converts>
if deprecation_warning:
_give_int_warning()
Although if we want to print the type, we would have to remember which failed and the above doesn't do it.
Maybe you want to try that and decide for yourself if it it doesn't get too annoying? These functions themself fairly simply, so I think it would be fine.
(We have been iterating a quite quickly, I probably should slow down a bit :))
|
@seberg Please tell me whether things need to be changed. |
seberg
left a comment
There was a problem hiding this comment.
Thanks, two comments, but honestly this is fine and if you prefer I might just push something (it's my problem if I have the urge to change it to something I feel is slightly prettier :)).
|
|
||
| # To make sure unsigned integers, etc. get converted to int | ||
| if isinstance(k, integer): | ||
| k = operator.index(k) |
There was a problem hiding this comment.
If you just do the try/except dance here, then that seems just as well? The worst case would be that the deprecation warning is given twice.
(that will also mean we'll start to reject e.g. NumPy booleans just like in tri itself. -- for better or worse)
| m = m.astype(dtype, copy=False) | ||
|
|
||
| # warn for all deprecated | ||
| for i in w: |
There was a problem hiding this comment.
Thanks, FWIW, it seems fine to me to just the single first warning. Which then allows you to write it as:
warning_for_type = False
try: ...
except:
warning_for_type = warning_for_type or type(N)
|
So, I just did the changes you told. I am just trying to avoid the double warning for |
|
@seberg |
seberg
left a comment
There was a problem hiding this comment.
Yeah, thanks @antareepsarkar. let's go with this.
resolves numpy#29488 Does not raise an error when any or all of of n, m and k are unsigned integers by converting them to integers. If inputs do not convert via `operator.index()` give a DeprecationWarning.
resolves #29488
Does not raise an error when any or all of of
n,mandkare unsigned integers.