Skip to content

ENH: avoid importing optional dependencies just to know if they are installed#15771

Merged
mhvk merged 3 commits intoastropy:mainfrom
neutrinoceros:rfc/lazier_optional_deps
Dec 19, 2023
Merged

ENH: avoid importing optional dependencies just to know if they are installed#15771
mhvk merged 3 commits intoastropy:mainfrom
neutrinoceros:rfc/lazier_optional_deps

Conversation

@neutrinoceros
Copy link
Copy Markdown
Contributor

@neutrinoceros neutrinoceros commented Dec 18, 2023

Description

Inspired by #13821 (comment)
I noticed that we could define HAS_<optional_dep> constant more lazily without breaking tests, though maybe this isn't desired in the overall context of #13821 ?

I bet @mhvk has opinions here.

  • By checking this box, the PR author has requested that maintainers do NOT use the "Squash and Merge" button. Maintainers should respect this when possible; however, the final decision is at the discretion of the maintainer that merges the PR.

@github-actions
Copy link
Copy Markdown
Contributor

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.

  • Do the proposed changes actually accomplish desired goals?
  • Do the proposed changes follow the Astropy coding guidelines?
  • Are tests added/updated as required? If so, do they follow the Astropy testing guidelines?
  • Are docs added/updated as required? If so, do they follow the Astropy documentation guidelines?
  • Is rebase and/or squash necessary? If so, please provide the author with appropriate instructions. Also see instructions for rebase and squash.
  • Did the CI pass? If no, are the failures related? If you need to run daily and weekly cron jobs as part of the PR, please apply the "Extra CI" label. Codestyle issues can be fixed by the bot.
  • Is a change log needed? If yes, did the change log check pass? If no, add the "no-changelog-entry-needed" label. If this is a manual backport, use the "skip-changelog-checks" label unless special changelog handling is necessary.
  • Is this a big PR that makes a "What's new?" entry worthwhile and if so, is (1) a "what's new" entry included in this PR and (2) the "whatsnew-needed" label applied?
  • Is a milestone set? Milestone must be set but we cannot check for it on Actions; do not let the green checkmark fool you.
  • At the time of adding the milestone, if the milestone set requires a backport to release branch(es), apply the appropriate "backport-X.Y.x" label(s) before merge.

Copy link
Copy Markdown
Contributor

@mhvk mhvk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That seems like a clear improvement! (but note small mistake) From a quick test, it looks like find_spec is indeed substantially faster.

p.s. I also wondered about doing

present = globals()[name] = findspec(_deps[name[4:]]) is not None
return present

so that the result is remembered for the next call. But am not sure that saves much time after this!

except (ImportError, ModuleNotFoundError):
return False
return True
return find_spec(name.removeprefix("HAS_").lower()) is not None
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be find_spec(_deps[name[4:]])

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

at the risk of bike shedding, isn't .removeprefix("HAS_") clearer than [4:] (albeit ever so slightly slower) ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(though getting the name for _deps is clearly required, so I applied that fix)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I agree, more obvious what it does.

@neutrinoceros neutrinoceros force-pushed the rfc/lazier_optional_deps branch from f426128 to 37b74e8 Compare December 18, 2023 22:43
Copy link
Copy Markdown
Contributor

@mhvk mhvk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice improvement, now all good!

@mhvk
Copy link
Copy Markdown
Contributor

mhvk commented Dec 18, 2023

Well, I approved too quickly: quite a few failures!

@neutrinoceros
Copy link
Copy Markdown
Contributor Author

Indeed ! I just learned that find_spec may raise ModuleNotFoundError when called with a subpackage name (e.g. matplotlib.pyplot). So, the easiest fix from here is

diff --git a/astropy/utils/compat/optional_deps.py b/astropy/utils/compat/optional_deps.py
index b079a82e99..08140b4439 100644
--- a/astropy/utils/compat/optional_deps.py
+++ b/astropy/utils/compat/optional_deps.py
@@ -37,7 +37,7 @@ _optional_deps = [
 _deps = {k.upper(): k for k in _optional_deps}
 
 # Any subpackages that have different import behavior:
-_deps["PLT"] = "matplotlib.pyplot"
+_deps["PLT"] = "matplotlib"
 
 __all__ = [f"HAS_{pkg}" for pkg in _deps]

which seems reasonable to me, since I highly doubt that matplotlib will ever not ship matplotlib.pyplot

@neutrinoceros
Copy link
Copy Markdown
Contributor Author

pushing the fix now to prove my point. I may be away until Wednesday, so feel free to push/merge/close as needed !

@mhvk
Copy link
Copy Markdown
Contributor

mhvk commented Dec 19, 2023

pre-commit.ci autofix

@mhvk
Copy link
Copy Markdown
Contributor

mhvk commented Dec 19, 2023

OK, now all tests pass and this looks like a very obvious improvement, so merging!

@mhvk mhvk merged commit ae438b4 into astropy:main Dec 19, 2023
@mhvk mhvk added utils and removed units labels Dec 19, 2023
@neutrinoceros neutrinoceros deleted the rfc/lazier_optional_deps branch December 19, 2023 06:40
@mhvk mhvk mentioned this pull request Dec 20, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants