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
MNT: add astropy.system_info
report helper function
#16335
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.
|
I like this idea in general, but I am wondering if there is any benefit to including the list of packages here versus just asking users to give the output of pip freeze? |
This might also make more sense to put in https://github.com/astropy/astropy-tools - and it doesn't have to be core package specific, but could be used for coordinated packages too. |
I can think of one: not everybody uses pip (conda and uv come to mind) |
I agree. However, I'm not sure how much additional complexity would be required to make it re-usable (the list of "relevant" packages would vary from one repo to the other) |
Another reason why not to make this a shared resource is that I would like to add information about the build environment, eventually, but that's so project specific that I don't think such a section could be made portable, could it ? |
ce8e889
to
42c6f24
Compare
|
Ah, thanks for pointing this out. I don't know off-hand how to change it but I definitely don't want to alienate such a common platform. |
Thinking about this a bit more, this seems to overlap somewhat with pytest-astropy-header - certainly what we are printing here is very similar to what is there. Can we avoid duplication somehow? We could for instance ask users to install pytest and do:
which has output that includes:
anyway not saying that is the solution, but there certainly is a lot of overlap, and various packages have already put in the work to determine what is relevant to show in this list. |
Ideally there should be as little friction as possible, and requiring pytest is a bit much in my opinion: users with broken envs or not able to install python packages (for example if they use a server where they don't have install rights) might turn away the minute we ask them to do something that doesn't work out of the box. The invoke you're proposing also runs a large part of the test suite, which is a lot more than what my script intends to do. All that said, it might indeed be a good idea to avoid duplication, but I'm thinking it should be the other way around (pytest header would rely on this script), if possible. |
I think there was a very old conversation about such thing but no one could agree on what is the one best way to pull it off. Every problem is a little different. |
In our bug report template we ask the users to list the versions of some of the key packages, but the less code they need to run for that the better. Relying on any external code (whether it be from Having a single command that could be copy-pasted and executed in a shell would be good, but in practice it doesn't work on all platforms. Possible problems using |
That's indeed how I intend it to be used long term, when it is safe to assume that every user runs a sufficiently recent version of astropy that it has the function, but it can't be how we advertise it from the get go. What if w didn't try to use it in the bug report template for now (or maybe just suggest it in passing) ? |
There's no LTS version anymore, so we no longer provide bugfixes for |
Ok let's go with this plan then. |
42c6f24
to
1805b68
Compare
In order to avoid making it more confusing than it needs to, I streamlined the instructions into a |
print("Matplotlib", matplotlib.__version__) | ||
except ImportError: | ||
print("Matplotlib not installed") | ||
from astropy.utils.report_info import main; main() |
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.
Still not 100% sold on this. This import won't work until astropy 7.0 is released. Until then, we might just get confused users reporting that our bug report template is broken.
I agree with @astrofrog that https://github.com/astropy/astropy-tools is probably a better place for such scripts, but then the same problem about it not being readily installable.
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 import won't work until astropy 7.0 is released.
except for reporters running the main branch, which I suspect wouldn't be a negligible fraction ?
Until then, we might just get confused users reporting that our bug report template is broken.
Now I'm confused: why would anyone report this as broken if there's no visible exception being raised ? (this isn't a rhetorical question, I'm genuinely curious)
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 a lot of users report from stable release. Not everyone installs from main
even when requested for various reasons (no time, no desire, stuck in old release, etc). Therefore for those users, from astropy.utils.report_info import main
will not work until we release v7. Hope this clarifies the matter.
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.
But this is in a try-except
block.
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.
Oh, oops... Must have missed it in the diff. Let me ponder again then when I get the chance. Thanks for your patience.
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.
Would it be more explicit if we now do an astropy
version check instead of a blind try-except?
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.
More explicit yes, but maybe also more fragile, and I'm afraid it would come back biting us with users reporting the script is broken...
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.
Well, we would need to test this script in CI anyway. And even then if it is still broken, won't we want to know? Under what condition would the script not break in CI but broken when user runs 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.
Since the attribute astropy.__version__
is generated via setuptools_scm
, I don't feel confortable taking any bets on exactly what could go wrong, but I don't trust that CI exercises all the ways it can be broken, which should all be weird edge cases that no one ever reported yet.
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.
Additionally, I note that
- the main function in the script has a doctest
- the exact way it's used in the report template is not exercised yet (as far as I know). That's definitely doable if you think it's a good idea.
1805b68
to
8326880
Compare
astropy/utils/report_info.py
Outdated
pyerfa ... | ||
""" | ||
report_lines = chain( | ||
header("platform"), |
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.
Why don't you call header()
in the report_*
functions?
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.
separation of concerns, I guess, but I don't hold a strong opinion on this.
astropy/utils/report_info.py
Outdated
This can be run in two fashions either by downloading the latest version of this | ||
script and running it on the fly | ||
``` | ||
curl --silent https://raw.githubusercontent.com/astropy/astropy/main/astropy/utils/report_info.py | python | ||
``` |
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'd remove this entirely. It is not possible to provide a simple shell one-liner that will work on all the supported platforms.
astropy/utils/report_info.py
Outdated
def header(name): | ||
return [name, "-" * len(name)] |
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.
Adding type annotations is not mandatory, but I recommend adding them nonetheless:
def header(name): | |
return [name, "-" * len(name)] | |
def header(name: str) -> tuple[str, str]: | |
return name, len(name) * "-" |
or perhaps
def header(name): | |
return [name, "-" * len(name)] | |
def header(name: str) -> str: | |
return f"{name}\n{len(name) * '-'}" |
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'd like to keep the return type a list for two reasons:
- in tuples, position is significant (borrowing the "Cultural Difference between lists and tuples" from https://nedbatchelder.com/blog/201608/lists_vs_tuples.html)
- your second suggestion requires more changes
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'd prefer to keep the output immutable unless there is good reason to make it mutable, but these functions should never be called outside this module, so it doesn't really matter.
astropy/utils/report_info.py
Outdated
pandas ... | ||
pyerfa ... | ||
""" | ||
report_lines = chain( |
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 not convinced importing itertools
just to use chain()
here once is worth 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.
I'm confused, what does it cost ? Last I checked itertools
wasn't an expensive import, and it seems to be imported so often by other parts of the standard library that trying to avoid the overhead seemed futile.
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.
chain()
is not an overly expensive function, but in this case it is very simple to avoid importing itertools
entirely:
report_lines = chain( | |
report_lines = "\n".join([*header("platform"), *report_platform(), "", *header("packages"), *report_packages()]) |
or if you move the header()
calls inside the report_*
functions it would be as simple as
report_lines = chain( | |
report_lines = "\n".join([*report_platform(), "", *report_packages()]) |
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 dropped itertools using unpacking. Thanks !
astropy/utils/report_info.py
Outdated
|
||
This can be run as | ||
``` | ||
python -c "from astropy.utils.report_info import main; main()" |
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 might not work on Debian-based systems (including Ubuntu) because on those Python is by default installed as python3
, not python
.
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.
So, what are you suggesting ? should I mention it in text or should I just present the 2-line python script as such ?
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.
in passing, I'm tempted to go with python -m astropy.utils.report_info
instead (already implemented)
I'd prefer if the function that prints the installed versions was named like either |
Thinking about this more, I am curious to see how other packages are rolling such things out. I have reached out to Scientific Python and will get back here after I have gathered some external data. Thank you for your continued patience! |
My conclusion from various feedback is that the packages rolled their own reporting because each has different needs. So the script is probably okay in this repo, but maybe ditch the CLI and rename scipy
https://github.com/scipy/scipy/blob/1464ae12df417a8c8b6750d86fca4437d1c732c0/scipy/__init__.py#L51 numpy
Others
|
|
astropy/utils/report_info.py
Outdated
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.
Add an __all__
in this module as well?
I'm not sure what you mean here. Are you refering to the Thanks for reminding me of @eerovaher's suggestion to change the name of the function, I think I missed it entirely ! |
astropy/utils/report_info.py
helper scriptastropy.system_info
report helper function
I've renamed the module and its main function, added an |
astropy/utils/system_info.py
Outdated
|
||
This can be run as | ||
``` | ||
python -m astropy.utils.report_info |
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.
python -m astropy.utils.report_info | |
python -m astropy.utils.system_info |
This script should not require anything else than the standard library. | ||
""" | ||
|
||
__all__ = ["system_info"] |
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.
Defining __all__
makes this module look public, so there should be an explicit warning in the module docstring that the contents here are subject to change without notice.
astropy/utils/system_info.py
Outdated
""" | ||
Examples | ||
-------- | ||
>>> main() # doctest: +ELLIPSIS |
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.
>>> main() # doctest: +ELLIPSIS | |
>>> import astropy | |
>>> astropy.system_info() |
I have a vague memory that we don't need +ELLIPSIS
because it is turned on globally somewhere in our configuration, but do check if this is true.
astropy/utils/system_info.py
Outdated
from importlib.util import find_spec | ||
|
||
|
||
def header(name: str) -> list[str]: |
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'd prefer if all the functions except system_info()
would be explicitly marked as private:
def header(name: str) -> list[str]: | |
def _header(name: str) -> list[str]: |
astropy/__init__.py
Outdated
@@ -234,3 +234,6 @@ def online_help(query): | |||
del locals()[varname] | |||
|
|||
del varname, __module_type__ | |||
|
|||
|
|||
from astropy.utils.system_info import system_info |
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.
Does this also need to be updated?
Line 199 in e7a556f
__dir_inc__ = [ |
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.
you tell me. I have never seen this variable name before, I don't know what it does.
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.
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, module.__dir__
has a special meaning since Python 3.7: https://docs.python.org/3.12/reference/datamodel.html#customizing-module-attribute-access
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.
Oh, I see. I think my new function does belong in __dir_inc__
, which lists everything that should not be dropped from the namespace, but since I added it at the end of the module, it's only included after namespace cleanup happens, so it dodges that. I think the cleanup ought to be the last thing to happen in the module, so I'll move my import and update this list, thanks !
astropy/utils/system_info.py
Outdated
return 0 | ||
|
||
|
||
if __name__ == "__main__": |
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 still don't see why we need this part.
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.
Enabling running the module as a script (python -m astropy.utils.system_info
) makes basically everything in the module (other than its name) an implementation detail, which seemed fitting.
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.
@eerovaher , what do you think?
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 wouldn't have implemented this. The ability to run
>>> import astropy
>>> astropy.system_info()
in Python should be good enough.
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.
Fair enough I'll drop it
Just one more thing we have to maintain but not advertised to be used in the template anyway. |
Well, following @eerovaher's suggestion I added a note that the module is subject to change without notice, and it's just one line, so it can be thrown away the minute it causes any hurdle. That said, it's also not a hill I'll die on so if you're still unconvinced, I'll drop it. |
astropy/utils/system_info.py
Outdated
return lines | ||
|
||
|
||
def system_info() -> 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.
There is no need to return 0
anymore.
def system_info() -> int: | |
def system_info() -> 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.
done
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.
Turns out its API doc is currently not rendered, so I think you have to add this module under "utility" in https://github.com/astropy/astropy/blob/main/docs/utils/ref_api.rst .
@stefanv
over at Scientific Python suggested we could also parse pyproject.toml
to gather the list of dependencies. Maybe something we can consider in the future if hardcoding is not enough.
Thanks for your patience with all the comments!
Co-authored-by: P. L. Lim <2090236+pllim@users.noreply.github.com>
5d2c635
to
550b3e9
Compare
I rebased and squashed all but the last two (new) commits, which correspond to applying latest suggestions from @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.
Looks like pre-commit isn't happy about something.
I think this counts as a new feature under utils, so need a change log.
Not sure why the files diff has codecov complains. When I go into codecov page, says patch coverage is 100%. 🤷♀️
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.
LGTM. Thanks!
Description
This has been proposed a couple times in passing, so here's an implementation of this idea that's immediately useful (not just when every user can be assumed to have a sufficiently recent version of astropy that includes the new function).
this outputs a report looking like
How to test this: