-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Clean up how pvlib imports optional dependencies #1283
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
Conversation
Sorry to be a downer, but I'd say yes and yes. I also understand that others may reasonably disagree.
This could be solved with comments near the external imports at the top of the file e.g. |
I agreed that it is quite clever/complex, but I like the fact that the 'cleverness' is contained in one function that most developers don't necessarily need to understand (like me). However, the current solution requires each function to have some slightly less clever method (e.g, as with As a newbie contributor to pvlib, I would have preferred to add What I also quite like about the solution is that all module imports are kept at the top (which is where I go to look for dependencies) instead of numerous times throughout the function as in the case of Just a few thoughts from this side of the pond :) |
Even tho I once thought it better for all imports at top, I agree with Will that the mocking of functions in
An alternate solution which might be slightly less clever would be a decorator & still commenting at module top: from pvlib.tools import import_optional_dependency
# has optional dependency import
@import_optional_dependency("foobar")
def myfun():
return
# in tools, untested psuedocode
import imp # or importlib whatever
def import_optional_dependency(imports):
def wrapper(f):
def wrapt_fcn(*args, **kwargs):
imp.import(imports)
return f(*args, **kwargs)
return wrapt_fcn
# end wrapper? A cool side effect is that if you see a module that imports this tool then you know already the are optional dependencies imported |
I'm going to inject a related question: can we move |
Some research on what other projects do:
So does I think this approach is also reasonable, while this one is overkill.
Developer considerations are important, but multiple maintainers do need to understand it and I just don't have it in me at the moment. |
Fine with me to KISS and just use
I was going to say that this is not a compelling point to me personally, but maybe that's because I already have a decent sense for what packages are used in each pvlib module. Or maybe I would just go to |
Sounds good to me. I was never super attached to the new strategy, but figured it was worth discussing. |
So for functions relying on a non-required package (e.g., xarray in
Of course for modules where it's usage depends on the input parameters it would be moved to one or more suitable places in the code. Also, is there a reason not to use the subclass |
It's not clear to me when
In the absence of any better information I'd say raise |
from zen of python:
and
which I interpret to mean, try to observe the most-accepted and widely used methods because:
|
Thanks all for the discussion, I think we can close this now. |
[ ] Closes #xxxxdocs/sphinx/source/api.rst
for API changes.docs/sphinx/source/whatsnew
for all changes. Includes link to the GitHub Issue with:issue:`num`
or this Pull Request with:pull:`num`
. Includes contributor name and/or GitHub username (link with:ghuser:`user`
).I'd have made an issue for this but I thought a PR would make it easier to see the idea in action.
pvlib has multiple ways of handling optional imports. One is to hide the imports inside the function that needs them, e.g. how
solarposition.py
handles theephem
dependency:pvlib-python/pvlib/solarposition.py
Lines 633 to 636 in f318c1c
Another way used in
ecmwf_macc.py
and #1264, is to keep the import at the top of the file for clarity, but defer an import error until the missing module is actually called on to do something, like this:pvlib-python/pvlib/iotools/ecmwf_macc.py
Lines 8 to 15 in f318c1c
So only when some bit of code actually tries to create a
netcdf4.Dataset
will theImportError
be raised.Summarizing some of @AdamRJensen's thoughts here: the first is simple but adds a bit of clutter to the functions (
solarposition.py
importsephem
four separate times) and doesn't make it easy to see what external packages each pvlib module uses. The second keeps imports at the top of the file but is a little opaque and gets clunky if more than one function is needed from a single import.This PR extends the second approach, but with improvements:
__getattr__
so there's no need to explicitly list out the module functions of interest. For example the ERA5 PR wouldn't have to handleopen_dataset
andopen_mfdataset
separately like thispvlib.tools
, so each module doesn't need to define a new class for each importWhat do people think? Too "clever"? Solving a problem that doesn't need solved? There might be other places to use this too, but I only looked at
ecwmf_macc.py
andsolarposition.py
for now to illustrate its usage.