Lazy import
One of the obstacles to using Toga with Invent is their feeling that Toga is too big and slow. Part of the reason for this is that we import every module on startup whether the app needs it or not.
This could be improved by adding a lazy import mechanism using a module-level __getattr__ function. This applies both to:
- toga-core (in
toga/__init__.py) - Each backend (e.g. in
toga_cocoa/factory.py)
It could work something like this:
_lazy_imports = {}
__all__ = []
def lazy_import(mod_name, *names):
for name in names:
_lazy_imports[name] = mod_name
__all__.append(name)
def __getattr__(name):
# Look up name in _lazy_imports
# Import the module
# Copy the name into this module
# Return the value
lazy_import(".widgets.activityindicator", "ActivityIndicator")
...
Agreed that startup time is something worth optimising; lazy imports like you've detailed here looks like they are likely a good approach.
There's also a class of related bugs - for example, even if your android app doesn't use DetailedList, it still needs to include the swiperefreshlayout component because DetailedList is imported. This specific case can be fixed in other ways (see #2454), but it highlights an import that shouldn't be happening at all - we'll have similar "try to import OSMDroid and fail" passes on every app because MapView is being imported, even if the app doesn't use it.
A related area that might be worth optimizing is the use of Rubicon classes on the macOS and iOS backends. The first time you import cocoa, it wraps all the classes that could possibly be used, even if they're not actually used by an app. I'm not sure if the same lazy import approach will work here, but it's worth exploring.