New minor version because the change is externally observable.
Change:
* The lazy load proxy now stays in place, but delegates all attribute access (get/set) to the managed library. This gets rid of a corner-case that was related to variable binding.
Previously the libraries referenced had different behavior depending on how and when they were bound to a variable, while in the new version the binding does not matter; the behavior is identical.
Previously:
python
from lazyimport import Libs
torch = Libs.torch
assert torch is Libs.torch
torch.__version__
assert torch is not Libs.torch this is a problem
assert vars(torch) != vars(Libs.torch) this is an even bigger problem
This difference in binding behavior meant that when using a short variable name as an alias (as is the intended use case) that alias variable was directly referencing the proxy. However, the implementation was a bit too clever in that it also swapped the proxy for the real module (library) on the "Libs" (container) object. The intent was to make the container hold the native modules without any proxy involved after a module had been imported (loaded). Unfortunately since there is no way in python to change the binding of all variables referencing a given object, the above discrepancy in behavior resulted.
New behavior:
python
from lazyimport import Libs
torch = Libs.torch
assert torch is Libs.torch
torch.__version__
assert torch is Libs.torch fixed
assert vars(torch) == vars(Libs.torch) fixed
The new version does away with the "cleverness" and instead sticks to letting the proxy be in place regardless of whether the module has been imported or not. To ensure the proxy looks and behaves exactly like the actual module, all attribute access and setting is being forwarded to the underlying library, which means, aside from a type check, that the proxy looks and behaves exactly like the underlying module. It therefore also passes the `isinstance(proxy, ModuleType)` check.
To ensure having the proxy in place is not expensive, it only incurs two extra python calls (two attribute lookups). Since referencing top level modules is typically not on the critical path, that overhead should be negligible.