Repository navigation
Auto-generate units stubs for wheels - #18573
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 |
|
Thanks @mhvk! |
|
Thanks. Your comment about meson/hatch in the other thread actually was what got me thinking that surely |
|
ping also @neutrinoceros, since he was interested in getting the typing working too. |
b6ad186 to
b881407
Compare
|
OK, I think this now works well enough, so marking it ready for review. Thanks, @mikez, for getting this going! The script is now used for building the |
9451907 to
470dd37
Compare
|
The reason for the |
|
|
||
| def test_typestubs_for_units_script(tmp_path: Path, monkeypatch): | ||
| def test_typestubs_installed(): | ||
| spot_check_stubs(Path(u.__file__).parent) |
There was a problem hiding this comment.
Note that our tests cover editable installs:
astropy/.github/workflows/ci_workflows.yml
Lines 72 to 81 in 1ba18e8
|
There are three options worth considering for distributing the stub files for unit definitions.
If we were to have a vote about this pull request then I would vote in favor of merging it, but if this proves to be too controversial then I don't intend to get involved in any arguments over this. The tradeoffs involved in creating a stub-only package are acceptable to me. |
|
Just to make sure I understand, is the long term plan to just ship stubs for units or for other subpackages too? |
Just the units. We need to do those because they are auto-generated, but at the moment at least there is nothing else in astropy that gets generated on the fly. (Even for the units, if I were to start from scratch, I would not create public unit instances, but always create with |
nstarman
left a comment
There was a problem hiding this comment.
IMO this looks great. I'm no expert in setup.py and defer detailed judgement of the install commands to those that are. To reiterate on a related note, there has been continued interest in following numpy, scipy et al away from setuptools and to e.g. Meson. With Meson, etc a very similar build pattern is supported, so this PR should not make any future transition more challenging.
There was a problem hiding this comment.
Delaying the erfa imports is the part I'm definitely least happy about, but can live with :).
I think fixing this is a challenging followup process that will likely involve refactoring units and constants to have a more integrated structure (even if the public presentation as two subpackages remains unchanged).
Anyway, that is best discussed elsewhere!
There was a problem hiding this comment.
In follow-up, I'd like the helpers for both scipy and erfa to do all the helper definitions only when the functions are actually needed, i.e., they would be moved to _scipy and _erfa modules that get imported on demand.
The advantage is that in those delayed-import modules, one simply has all units available, so the local imports of those in helper functions would disappear -- hence, it is a cleanup that is good independent of what's here!
Perhaps a wild idea but have we considered hard coding all the units in a Python file (or multiple files) and including a pre-commit script that can ensure this auto generated file is up to date? This would make the stub issue moot and also likely speed up astropy.units imports. Obviously not perfect, but worth considering? |
be229f3 to
a2c6fc4
Compare
|
OK, I updated the PR, moving the functions that do the actual work from the script to a private module, and deleting the added documentation and what's-new. I left the changelog entry in place, as I think this is an important step worth mentioning, but am happy to remove that too if requested. |
|
p.s. I made a few light cleanups of the type-stub generating functions, but clearly more can be done. I suggest we leave that to follow-up, though. SImilarly, @eerovaher suggested the stubs themselves can be checked by |
|
Failures are unrelated. |
nstarman
left a comment
There was a problem hiding this comment.
LGTM. The existing tests haven't changed, only now stubs are always generated. This does increase the need for more thorough testing, but I agree this can be a followup.
a2c6fc4 to
29e709d
Compare
|
Last (force) push removed the mention of the script in |
| elif name == "enable": | ||
| variable_lines.append("enable() -> _UnitContext: ...") |
There was a problem hiding this comment.
Because of this addition astropy/units/imperial.pyi contains invalid syntax, so it is not even a valid stub file anymore. I don't know if you tested the stubs or not. If you did and the tests approved them despite invalid syntax then the tests are worthless. I suggest you don't try to make any changes to the contents of the generated stubs in this pull request.
There was a problem hiding this comment.
At least some files that stubs are generated for do include enable in their __all__, and my intent was to try to ensure the stub files were complete (since I think otherwise enable would just be untyped). To test this, I changed the code to raise RuntimeError below.
But if what I add here is wrong, then clearly my lack of familiarity with typing is biting me here, and I should just leave it for follow-up. What do you suggest? Just skipping only enable, or just removing the RuntimeError below.
Alternatively, if you can give the correct typing statement, I can do that too (or better still, just push to my branch).
p.s. Unfortunately, with teaching started, I do not have the time to start learning how to check typing -- as you know, typing is not something I particularly care about, I really just wanted to have a conclusion to the discussion about type stubs...
There was a problem hiding this comment.
my intent was to try to ensure the stub files were complete
If the output contains syntax errors then it's not a stub file at all.
To test this, I changed the code to raise
RuntimeErrorbelow.
Leave testing the stubs to stubtest.
What do you suggest? Just skipping only
enable
This pull request should be concerned with implementing the mechanism that generates the stubs when astropy is built and cleaning up the documentation. It should not be concerned with testing the stubs or with the contents of the generated stubs.
or just removing the
RuntimeErrorbelow.
It is very bad if a mistake in generating the stubs can cause astropy installation to fail, so having the script throw a RuntimeError not a good idea at all.
Alternatively, if you can give the correct typing statement, I can do that too (or better still, just push to my branch).
Let's just merge the mechanism for automatically generating the stubs. It's simpler to deal with the stubs themselves in a separate pull request.
There was a problem hiding this comment.
OK, I removed it; I'll leave it for typing afficionados to add proper checking (and then add enable).
There was a problem hiding this comment.
So developers know we're a step further and they can start helping out / using it. If others agree that this changelog entry is not necessary, I'm happy to remove it, but to me it seems obviously useful.
There was a problem hiding this comment.
Downstream packages cannot use astropy annotations for type checking their code unless we declare our code to be type-checkable with a py.typed marker file. So bringing up annotations in our change log before we do is just a source of confusion. And when it comes to e.g. IDE completions then any development environment worth using will be able to find the stubs automatically. So there is nothing to announce.
There was a problem hiding this comment.
As I wrote, my message was aimed at people who might think the time was now right to help improve the typing. But clearly it didn't get through to you that way, and I don't really care, so the easiest solution is to delete it.
There was a problem hiding this comment.
The change log is for the users. If you want to invite developers to work on type annotations then you should open an issue on GitHub, or perhaps write to the mailing list.
This moves the previous script to a new file in typing_utils and adjusts it so that it can be called from setup.py on installation (in regular or editable mode). Test are adjusted accordingly. Since typing is at present not directly useful, and IDEs will find these stubs automatically, no announcement of this is made in the changelog or documentation.
29e709d to
842c9ca
Compare
|
OK, I pushed a minimal version that is hopefully OK with everyone. Shrunk to two commits so there are no reversions internally. |
neutrinoceros
left a comment
There was a problem hiding this comment.
I finally did a full-on review, and while I did try catching up with the existing conversations, I may have overlooked some parts, so I apologise if any of my questions/suggestions were already resolved.
To reiterate on a related note, there has been continued interest in following numpy, scipy et al away from setuptools and to e.g. Meson. With Meson, etc a very similar build pattern is supported, so this PR should not make any future transition more challenging.
as a side note: @astrofrog and myself are currently working on an APE that may provide some help with any hypothetical transition to more modern build systems 🤞🏻
| # been completed, so we import units locally. | ||
| # We also avoid importing erfa up front since otherwise type stub generation | ||
| # in the build process can only run with erfa as a build requirement. | ||
| # TODO: solve both of the above by defining inside get_erfa_helpers? |
There was a problem hiding this comment.
To be clear, are you planning to implement these TODOs are part of this PR, or as a follow up ?
There was a problem hiding this comment.
Definitely for future follow-up (would want to do scipy similarly at the same time).
| typestubs_for_units.main() | ||
|
|
||
| def test_typestubs_installed(): | ||
| path = Path(u.__file__).parent |
There was a problem hiding this comment.
Since this looks like the only use of astropy.units in this test module, and in order to isolate it as much as possible from potential import side effects, I would recommend using importless-introspection instead, such as
from importlib.util import find_spec
if (units_modspec := find_spec("astropy.units")) is None:
# this is so typecheckers can confidently narrow the type of `units_modspec` in the following
raise AssertionError()
path = Path(units_modspec.origin)There was a problem hiding this comment.
I don't think in this context it matters much - these tests are run after installation and as part of the a pytest run, where surely other tests have already imported astropy.units.
Also, @eerovaher suggested that after this PR, the testing should be done using standard type testing, so this will likely be a temporary solution anyway.
There was a problem hiding this comment.
There is nothing to gain from updating this file any further.
| "numpy>=2.0.0, <3", | ||
| "extension-helpers>=1.4,<2"] | ||
| "extension-helpers>=1.4,<2", | ||
| "pyerfa>=2.0.1.1"] |
There was a problem hiding this comment.
while we're at it, can we format it in a way that we don't have to loose useful git blame info next time we want to add a line without actually changing this one ?
| "pyerfa>=2.0.1.1"] | |
| "pyerfa>=2.0.1.1", | |
| ] |
There was a problem hiding this comment.
I can do that if other changes need to be made.
There was a problem hiding this comment.
if you're worried about wasting CI resources, you can always include [skip ci] in the commit message
| from pathlib import Path | ||
|
|
||
| from setuptools import setup | ||
| from setuptools.command.editable_wheel import editable_wheel |
There was a problem hiding this comment.
I could hardly find any documentation for this class, so my question is this:
how did you learn about it and how do you know that we got every case covered ?
There was a problem hiding this comment.
Short answer: I don't. For the regular installation, there were quite a few clues on-line, but for the actual implementation I ended up looking through the setuptools source code.
Aside: from my experience with meson so far, the learning curve is no less steep for that. Anything unusual and quickly you need to look at source code. The meson.build files remain rather unreadable to me (at least for numpy, which of course is not the easiest package).
There was a problem hiding this comment.
p.s. Just a bit more positively, for regular installs I'm sure what I'm doing is OK. I am not sure that my way to cover editable installs covers every possibility to get an editable install, but think I'm OK too, since I'm on the path that the relevant PEP wants.
There was a problem hiding this comment.
Sorry I don't know what PEP you're hinting at.
There was a problem hiding this comment.
neutrinoceros
left a comment
There was a problem hiding this comment.
all my questions and suggestions were answered, so approving now !
|
Thanks! With two approvals, let's just get this in. |
There was a problem hiding this comment.
Didn't we fantasize about getting rid of setup.py altogether at one point? I guess it is not happening now...
There was a problem hiding this comment.
Indeed, not if we keep using setuptools. But at least setup.py now is fairly simple, with concrete things that it does.
There was a problem hiding this comment.
whatever build backend we migrate to if/when we get away from setuptools, we'll need to migrate this bit of code generation as well
to add build for Windows ARM64. Also backport the ignore added in astropy#18573 so devs do not accidentally check in pyi files when switching branches locally.
to add build for Windows ARM64. Also backport the ignore added in #18573 so devs do not accidentally check in pyi files when switching branches locally.
This PR ensures that type stubs files are generated as part of building wheels (or in-place for editable installs).
OLD TEXT
This is by no means a finished product, but I wanted to see whether with our current setup it is possible to ensure the unit stubs get added to the wheels, and thought I would share the result so far.
It turns out it is possible, though (as @eerovaher noted elsewhere) at present it means requiring
pyerfaas a build requirement (this problem is fairly easy to solve; EDIT: solved in bd9807e). A good bit is that it does properly make sure that the__pycache__, etc., files created while making the*.pyifiles are not included.Note that the scheme here does not work for editable installs; I've tried a bit to get that to work, and am sure it is possible, but didn't find an elegant solution for it.EDIT: the scheme now also works for editable installs (b6ad186). In this case, the*.pyifiles are stored directly in the project directory, and some__pycache__appears too - but this should be fine as one will create those anyway upon using the editable install.Anyway, mostly just to give everybody a sense of what's involved.
cc @eerovaher, @nstarman, @mikez