-
Notifications
You must be signed in to change notification settings - Fork 944
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
lazy_loading matplotlib submodules can cause errors #1721
Comments
@bmcfee Why was this marked off topic? AFAIK this is not an upstream problem, lazy_loader is working as expected (note I've corrected a reference to importlib which was wrong). |
@agucova sorry for that, you responded before I was able to finish typing the response below. I'm flagging this as I do agree that it's a bigger problem. So far, it's mostly been users of In the specific example you provide, you could resolve the issue by importing librosa.display last instead of first. (Confirmed on my dev machine that this works.) This is probably good style anyway, as lower-level imports usually should come first, even if it's not supposed to really matter. We could add some language in the documentation to advise this use pattern to avoid problems if folks think that would be helpful, but I expect that users would probably not easily find this.
Not knowing much of the details here, I think this might be reasonable. Relying on import side effects seems like an antipattern, though I'm sure they have good reasons for doing it. |
@bmcfee sorry! I got ahead of myself.
As much as I agree with this, the example provided is only implemented in one module for simplicity of replication. The original bug that prompted me to notify the maintainers here (even though I'm not a librosa user), involved another library (matplotlib-inline) importing matplotlib themselves after the user had imported I'm not sure what librosa users could do to work around that particular bug unless matplotlib chooses to fix this (or matplotlib-inline implements a workaround only for librosa users, which seems unlikely). AFAIK this affects all Jupyter Notebook users that import Anyways, I'm in a crusade to warn users of weird errors like this to save them some time (as I lost ~2 hours when debugging the same bug in a package I was maintaining). I've filed issues with both importlib and lazy_loader so they can document this flaw. |
Ah, fair enough. Thanks for clarifying. At any rate, I think it's unreasonable to expect end-users to have a full working knowledge of the dependency tree for every package, so it's probably not a practicable solution to have them import in a specific order anyway.
I guess this would also come up with every other package that uses lazy-loader and matplotlib (eg, scikit-image, networkx, etc)? That seems like a growing list, so an mpl-side fix wouldn't necessarily be specific to us. Maybe the better question is: is there something we could do to fix it, short of reverting the use of lazy-loader entirely? |
Looking at this a bit more closely, it's possible that I was conflating this specific behavior with the more general issue that's been popping up with pyinstaller. The latter has to do with lazy loading full stop, while it seems like maybe this one could be isolated only to lazy-loading across package boundaries. Would that make sense? If that's the case, I think we could potentially roll back the lazy loading of matplotlib modules: Lines 53 to 69 in cc01904
and just import them directly. I think in practice this wouldn't change anything for our users; we can still lazy-load The only wriggle here is that if we're going to forbid lazy loading across package boundaries, we should probably do it across the board, right? We have a few other optional dependencies that are lazy-loaded (resampling libraries, mainly). However, we lazy-load the entire package there, so maybe the cross-package submodule issue would not be a problem? I'm curious for @stefanv's input on this. |
I responded on scientific-python/lazy-loader#55 (comment) I don't yet understand whether it is a flaw in the lazy loader, I only see the behavior for matplotlib, but I'd like to distill which assumption is at play. |
Thanks @stefanv ! I mainly wanted to make sure this wasn't just me misunderstanding how this is supposed to work, ie whether lazyloading submodules from other packages is specifically discouraged. But having read through the spec again, I don't think we're really doing anything wrong here. That said, I'll still look into the solution proposed above just for the case of matplotlib as a stopgap solution until the dust settles. |
Took the liberty of updating the title here, since it does (for us) appear to be much more specifically related to matplotlib and not lazy-loading in general. The PR #1722 fixes this while retaining lazy-loading benefits in general from our side, so I'll slot this into the next minor release (0.10.1). |
Just got bitten by this as well. Looks like it can manifest itself in other ways as well. For instance, this import librosa.display
import matplotlib.pyplot as plt
fig, axes = plt.subplots(2, 1) errors with
Please note that this isn't really a sustainable solution. When working in larger teams, CI enforced standardization of imports via Note that since
Thanks for fixing this! |
We are all well aware of this. I am trying to see if I can implement a solution in |
For anyone having this exact problem (as we did in our project), you can at least tell import matplotlib.pyplot as plt # isort:skip |
Librosa uses lazy_loader to lazily load some libraries. This implementation has the known flaw (see here, here and here) of not triggering certain import side effects, effectively breaking libraries that rely on this, like matplotlib. This can cause weird and hard to replicate bugs, like this one, which was filed initially against matplotlib and then to matplotlib-inline.
My best guess is that bugs related to this are probably more widespread than would be apparent, as users would report these bugs to the wrong projects most of the time.
It's not clear to me there is a good solution to this. As far as I know, the only reliable solution would be to patch matplotlib itself to support lazy loading, like numpy did.
To Reproduce
In the following case, one should be able to access pyplot (or run one of its methods): 1
Instead, one gets:
Just removing the
librosa.display
import makes it work again.Edit: Removed reference to importlib's implementation, as I thought Librosa was using it (and not lazy_loader).
Footnotes
Yes, both imports are necessary to replicate because the python import system is weird. ↩
The text was updated successfully, but these errors were encountered: