-
-
Notifications
You must be signed in to change notification settings - Fork 2k
API: align default value for copy arguments with numpy at runtime (copy=False raises an error if copies cannot be avoided) #16181
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
API: align default value for copy arguments with numpy at runtime (copy=False raises an error if copies cannot be avoided) #16181
Conversation
|
Thank you for your contribution to Astropy! 🌌 This checklist is meant to remind the package maintainers who will review this pull request of some common things to look for.
|
|
👋 Thank you for your draft pull request! Do you know that you can use |
|
Note to self (and reviewers): if this is turns out preferred over #16174, I'll need to update docstrings where due. I'm avoiding doing it now so rebasing stays as easy a possible. |
|
FWIW, I vote for this one but you should wait a bit more before pushing more changes, just in case. Thanks! |
|
Well, as long as behavior does not change for numpy 1.x, is that correct? |
|
That's right. The title is maybe misleading, but this patch just delegates all copy decisions to numpy, which, with numpy 1 installed, isn't a change at all. |
7299fdf to
5a76d55
Compare
|
@neutrinoceros - I took some time to try getting up to speed on the huge refactors to address the numpy 2.0 My question here is process. The PR description mentions that this PR is something to "ask downstream stakeholders to test from". I would assume you mean to merge this to main and let people test with astropy dev (main). Overall everything I see in this PR looks good and uncontroversial at this point. However there is a conflict that needs to be resolved. |
Actually what I meant was to offer downstream users to test the unmerged branch, because I was initially more worried about breaking stuff than I am now. In hindsight, merging first, as you suggest, would probably be easier for everyone. Since I haven't heard back from any one yet, I'll assume nobody's currently relying on this branch's state, so let me rebase to fix the one conflict ! |
5a76d55 to
e7d531d
Compare
|
@taldcroft there was also the fact that I initially conceived downstream testing as a mean to choose our API change (with #16174 as the alternative), but I think I convinced myself that #16174 wasn't really a viable option while implementing it. |
mhvk
left a comment
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.
See long in-line comment about the changelog-entry, and a general worry that something like Table(..., copy=None) may not work...
It may be best, however, to worry about those other things in a different issue/PR. 6.1 is not that far away and it might be good if people's CI can start testing against this...
docs/changes/16181.other.rst
Outdated
| Public APIs that expose a ``copy`` argument and that previously set ``False`` | ||
| as a default value now use ``None`` instead if Numpy 2.0 or newer is installed. | ||
| Default behaviour is unaffected, however explicitly passing ``copy=False`` will | ||
| now raise an error if copying cannot be avoided. |
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 be explicit and say "will raise an error under numpy 2.0 or newer if ...".
It is a bit double, but makes clear that numpy 1.x is not affected.
Actually, looking at the list that follows, I first thought this cannot be true - see comment on the differential - but then worried more generally about it. I think this change really affects everything, including SkyCoord, for which copy=False would just get passed down until it hits the representations. So, I think we should rephrase this as a new paragraph (and really this needs to be in some form of what's new for 6.1 too...), like
Public APIs that expose a ``copy`` argument and that previously set ``False``
as a default value now use ``None`` instead if Numpy 2.0 or newer is installed.
This is because in numpy 2.0, the meaning of the ``copy`` argument has changed,
with ``copy=False`` now indicating that a copy should never be made, while
``copy=None`` is used for the previous meaning of "avoid a copy if possible".
While this change ensures the default behaviour of astropy has not changed,
code that explicitly passes ``copy=False`` to many of astropy's classes
will need to be adjusted. This includes:
(and then the list should not include CylindricalRepresentation but should probably include SkyCoord)
A logical question here is whether we should include how to adjust it. Should we link to astropy.utils.compat.COPY_IF_NEEDED?
A more pragmatic question is whether we need tests to check that things do not percolate badly. E.g., I'm not sure that Table really belongs in the list.
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.
A logical question here is whether we should include how to adjust it. Should we link to astropy.utils.compat.COPY_IF_NEEDED?
I had thought the right answer is to not pass a copy=... argument at all, deferring to the default. But this may be upsetting to people that want to be explicit and not rely on a default.
I had also wondered about copy=None. The table says that the behavior is formally undefined but in practice for numpy 1.x it is the same as False.
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'll adjust the note, and I agree it belongs in the what's new.
A logical question here is whether we should include how to adjust it. Should we link to
astropy.utils.compat.COPY_IF_NEEDED?
As far as I understand, this module isn't considered public API (we just removed PYTHON_LT_* constants from it without a warning), so I wouldn't want to encourage downstream to rely on it. That said, we could recommend copying its implementation (the tricky part, I think, is just in how to properly check numpy's version at runtime using packaging.version). What do you think ?
A more pragmatic question is whether we need tests to check that things do not percolate badly.
Could you clarify what you mean here ? From my standpoint, the "percolating badly" scenario would be #16174 😅 .
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.
Oops, going on a race condition with @taldcroft .
I had thought the right answer is to not pass a
copy=...argument at all, deferring to the default. But this may be upsetting to people that want to be explicit and not rely on a default.
Well it is documented as mostly for internal usage, and people who want to be explicit about not copying have no recourse to do so in numpy 1, so I'm not too worried.
I had also wondered about
copy=None. The table says that the behavior is formally undefined but in practice for numpy 1.x it is the same as False.
unless you're using a type checker, but essentially yes ! We could even recommend using a plain None as a backward compatible and cheap replacement.
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 don't think one can just use copy=None on all numpy versions:
In [2]: np.__version__
Out[2]: '1.24.2'
In [3]: np.array([1, 2, 3], copy=None)
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
Cell In [3], line 1
----> 1 np.array([1, 2, 3], copy=None)
ValueError: NoneType copy mode not allowed.
This is also what I meant by tests. Since we use copy=False basically only internally, I'm not so sure that we cover most cases users might do. I think we should be OK, though, since in most cases one would not be asking for a dtype change, so, say, SkyCoord(ra_array, dec_array, copy=False) should just continue to work.
Anyway, bottom line to me seems that we should just try to make sure this is out when numpy 2.0 comes out, so that we can get reports from users. Indeed, perhaps this is the one time that we should ask people to run their test suites on a point release...
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.
Actually I think there are conflicting desires here: if we're adding a what's new, then backporting (along with supporting PRs) becomes impossible (patch releases don't have what's news).
So, the easiest way to resolve this tension would probably be to not have a what's new, and instead make the changelog entry even longer. As long as we make sure that the release team is aware of the importance of this patch and can properly emphasize it as part of our numpy 2.0 compatibility layer, it should be good ?
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, makes sense!
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 really think we should only release all these changes in 6.1.0 and have a What's New.
If there is a chance that numpy 2.0 will get released before early May then we should move the astropy release forward. Having these changes in a bug fix release is (IMO) totally incompatible with our policy for such releases. Despite a lot of effort and review, I think it is credible that the numpy 2.0 compatibility changes will break some existing code somewhere. Users don't expect this for a bug fix release.
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.
OK, that makes sense too. So, we basically will make clear that astropy 6.0 requires numpy < 2.
I had vaguely considered the idea of an early 6.1 as well - seems worth a new issue! #16200
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.
ok then I'll make a what's new from the changelog
| base_representation = CylindricalRepresentation | ||
|
|
||
| def __init__(self, d_rho, d_phi=None, d_z=None, copy=False): | ||
| def __init__(self, d_rho, d_phi=None, d_z=None, copy=COPY_IF_NEEDED): |
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 is actually really weird - why would the Differential have copy=False as a default when the representation does not (and indeed all representations and differentials have copy=True, though mostly implicitly via BaseRep*; but a concrete example is PhysicsSphericalDifferential).
It maybe should be in a different PR, but let's at least not mention this in the changelog entry...
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.
How about I revert this bit here and open a dedicated issue for 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.
Yes, good idea, let's just have a PR that changes it to True - this is the default for essentially all astropy classes (except Masked, where False makes much more sense).
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.
done in #16198
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.
(and reverted 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.
No, it shouldn't be reverted - just use copy=True 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.
Oops, sorry, commenting on something that's out of date! All OK.
af47249 to
0a8d367
Compare
mhvk
left a comment
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'm happy with this! I have a suggestion to simplify the version check but OK to leave as is too.
docs/changes/16181.other.rst
Outdated
| instances of ``False`` be replaced with a ``COPY_IF_NEEDED`` constant defined | ||
| as follow | ||
|
|
||
| def NUMPY_GE_2_0() -> bool: |
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.
Can we just recommend a simpler version?
COPY_IF_NEEDED = False if np.__version__[0] == "1" else None
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.
Given how these compatibility layers tend to be left to rot in unmaintained software, I'm slightly uneasy to recommend something that would break in the event of numpy going to 3.0. I'm also hesitant to recommend more than one way to do it, which is why I went with the more robust alternative.
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.
hum, on second thought I think this would only break if there was a Numpy 10.0 ... and by then, anything that still has compatibility code for Numpy 1.x is likely to be broken anyway so, let's just roll with 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.
and on third thought, we can make it even more portable at no real addtional cost:
COPY_IF_NEEDED = False if np.__version__.startswith("1.") else None0a8d367 to
f8b1b9a
Compare
|
I moved the large changelog fragment to a What's New section, and replaced the changelog with a one liner version of its past self. Should be good now ? |
|
Failure in devdeps seem unrelated but it's still kinda scary (and unfamiliar to me). |
docs/changes/16181.other.rst
Outdated
| @@ -0,0 +1,3 @@ | |||
| Change the default value of ``copy`` arguments in public APIs from ``False`` to | |||
| ``None`` if Numpy 2.0 or newer is installed. | |||
| For details, see :ref:`What's New in Astropy 6.1?<_whatsnew-6.1-copy-semantics>`_ | |||
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 think we generally try to avoid putting links in changelog fragments (because they are more likely to get out of date), but I'm not sure whether this one is OK - let's ping @pllim!
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.
Yeah:
- That ref is wrong. Should have used
whatsnew-6.1-copy-semantics(without whitespace maybe because RTD has a cryptic warning). - I also remember some past pain where we linked to something in What's New but then the link broke after release. But I don't remember exactly what broke. @saimn , do you?
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.
p.s. Maybe it is not the change log fragment but the what's new. See #16181 (comment)
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.
Ah, so the problem is having both :ref: and trailing _.
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.
Ah, that would do 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.
thank you ! should be fixed now !
mhvk
left a comment
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.
Looks great, modulo the one worry about the link in the changelog fragment. Approving assuming that is actually OK.
|
Re: devdeps -- Looks like transient network error but I restarted it, just in case. I also added |
|
devdeps failure is yet another bunch of str ufuncs being added to numpy 2.0 (probably the last bunch to come...) doc link test I didn't quite have the patience to try to understand what exactly went wrong - rather a lot of less relevant information to parse through... |
|
linkcheck isn't related, so you can ignore that one. RTD warning is related: |
949584e to
c58abc2
Compare
c58abc2 to
f5f3fff
Compare
|
rebased to fix a merge conflict |
|
failures due to #16251 |
docs/changes/16181.other.rst
Outdated
| @@ -0,0 +1,3 @@ | |||
| Change the default value of ``copy`` arguments in public APIs from ``False`` to | |||
| ``None`` if Numpy 2.0 or newer is installed. | |||
| For details, see :ref:`What's New in Astropy 6.1?<_whatsnew-6.1-copy-semantics>` | |||
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 failed RTD build with a warning:
WARNING: undefined label: '_whatsnew-6.1-copy-semantics'
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.
Also I don't think intersphinx is going to last forever in What's New as we "archive" the page at release time. For example, please see https://github.com/astropy/astropy/blob/main/docs/whatsnew/6.0.rst
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.
Thanks for pointing it out, how can I fix this ? Should I just refer to the what's new page in text and avoid any linking ?
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 am okay with a mention without any link. What do you think, @mhvk ?
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.
Agreed, let's not over{th|l}ink.
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.
thanks, I removed the link ! Let me know if it's ok now
f5f3fff to
8016bd2
Compare
Co-authored-by: P. L. Lim <[email protected]>
|
I really can't understand how docs are still failing (I can't even find where the error is in the log) |
|
I can try and investigate this morning |
|
The RTD docs build is fine, the one that is failing is the link check, but I think these are unrelated transient issues. The log is hard to parse but by grepping All are |
astrofrog
left a comment
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.
Failures are unrelated, looks good to me!
Description
This is based off #16166 and #16170 and adds a minor but crucial API change to align the default value of our
copyarguments with whatever numpy API is installed. See #16167 for the broader context.Alternative to #16174:
Passing
copy=Falseimmediately breaks if copies cannot be avoided (in alignment with numpy 2.0)This is meant to be a small self-contained PR that we can ask downstream stakeholders to test from, but this will only make sense once #16166 and #16170 are merged. At that point, I'll call for feedback on the dev mailing list.
Close #16167