lazy-imports-lite

lazy-imports-lite changes the semantics of python imports and defers the import until it is used the first time like in PEP 690

Downloads
139
Stars
9
Committers
2

lazy-imports-lite changes the semantics of python imports and defers the import until it is used the first time like in PEP 690.

I liked the idea of lazy imports and wanted to use them for my projects without having to change my code. I hope this project allows more people to use lazy imports in their projects.

[!NOTE] It is important to note that this project is not affiliated in any way with the original PEP. I hope this project allows a wider adoption of lazy imports, but there is currently no plan to standardize it.

[!IMPORTANT] lazy-imports-lite is still in early development and may contain bugs. Make sure to test your code carefully before you use it.

Key Features

  • lazy imports almost like PEP 690
  • no code changes required
  • can be enabled per module (by keyword in the package)

How is it different to PEP 690?

  • It has not the same performance as the implementation from the pep. Every access to parts of imported modules is transformed to an attribute access x -> x._lazy_value.
  • Exceptions during deferred import are converted to LazyImportError.
  • modules which use exec or eval can not be transformed.

Usage

  • add lazy-imports-lite to your project dependencies.
  • add lazy-imports-lite-enabled to the keywords of your pyproject.toml.
      [project]
      keywords=["lazy-imports-lite-enabled"]
    

This enables lazy imports for all top-level imports in your modules in your project. One way to verify if it is enabled is to check which loader is used.

>>> import your_project
>>> print(type(your_project.__spec__.loader))
<class 'lazy_imports_lite._loader.LazyLoader'>

Implementation

lazy-imports-lite works by rewriting the AST at runtime before the code is compiled.

The following code:

from foo import bar


def f():
    print(bar())

is internally transformed to:

import lazy_imports_lite._hooks as __lazy_imports_lite__

globals = __lazy_imports_lite__.make_globals(lambda g=globals: g())
bar = __lazy_imports_lite__.ImportFrom(__package__, "foo", "bar")


def f():
    print(bar._lazy_value())

This transformation should be never visible to you (the source location is preserved) but it is good to know if something does not work as expected.

You can view a preview of this transformation with lazy-imports-lite preview <filename> if you want to know how your code would be changed.

TODO

  • mutable globals()
  • cache generated bytecode

Issues

If you encounter any problems, please report an issue along with a detailed description.

License

Distributed under the terms of the MIT license, "lazy-imports-lite" is free and open source software.