ManderaGeneral / generalimport

Handle all your optional dependencies with a single call!
https://pypi.org/project/generalimport/
Apache License 2.0
16 stars 1 forks source link

Support for lazy imports #43

Open mauvilsa opened 1 year ago

mauvilsa commented 1 year ago

A topic closely related to optional imports are lazy imports. When a package is optional, it commonly happens that even though it is installed, it is not used for some process execution. In these cases, always importing all packages is a waste of resources. Implementing lazy imports can provide "startup time improvements up to 70% and memory-use reductions up to 40% on real-world Python CLIs".

Eventually lazy imports could become a native feature of python, but for the moment there are only long discussions and a rejected PEP 690. Thus, a package that provides it would certainly be welcome.

From my understanding adding lazy imports to generalimport would be relatively easy given what it already does. So I propose to add this as a feature.

Possible implementations

  1. Add a new function lazy_imports() to explicitly make imports lazy.
  2. Modify the existing generalimport() so that imports are always lazy.

Independent from this, from generalimport import generalimport does not look very nice or readable. Might be nicer if it were from generalimport import optional_import.

mauvilsa commented 9 months ago

@Mandera any comment regarding this?

Mandera commented 9 months ago

Lazy imports would be very interesting if it could be implemented cleanly!

I'm a little offended by your distaste of from generalimport import generalimport 😅 But I'm afraid you might have a point. My reasoning was that for "flagship" methods it makes sense to use the same name, i.e. from pprint import pprint. It's easier to remember

mauvilsa commented 9 months ago

Good to hear that this would be a welcome feature!

What would be your choice for a user facing design? I mentioned two. One could be a totally new function, e.g. lazy_imports, which would mean that people need to use this new function or change their code if they would benefit. The other option would be that the existing generalimport would become lazy. My main question regarding both options are if there are use cases in which someone would want an optional dependency but not be lazy. In my case all the packages that I have developed that have optional dependencies makes sense for them to be lazy.

If lazy is added to generalimport it could also be with a transition period. When first released could be off by default and have a way to enable it. If it makes sense, in some other release make it enabled by default.

Regarding the actual implementation, I haven't really looked at how generalimport works. From what I understood, which a package is marked as optional via generalimport, an error of not being installed is delayed up to the moment that the package is attempted to be used. So I guess, currently if the package is installed, then the import statement imports it immediately. The required changes would be mainly two:

Is my understanding correct?

Mandera commented 9 months ago

I like this one:

If lazy is added to generalimport it could also be with a transition period. When first released could be off by default and have a way to enable it. If it makes sense, in some other release make it enabled by default.

I'm afraid I have my doubts about a clean implementation for lazy imports though. The current way the "nice error messages" are produced is by trying to catch all dunder methods. I'd argue it's impossible to have 100% coverage for all use-cases, but this is currently fine because the consequence of a use-case slipping by is simply a generic error message.

I'm afraid we'd want 100% coverage if functional code were to go through this system

Is my understanding correct?

I think so yeah!

Mandera commented 9 months ago

Right, there's also one remaining issue for a use-case we can't handle: