README
liblaf-lazy-loader lets Python packages expose stub-driven lazy exports with both absolute imports and package-relative imports.
โจ Features¶
- ๐ค Stub-driven lazy imports: Parse a sibling
.pyifile and turn itsimportandfrom ... import ...statements into on-demand attribute loaders. - ๐ฆ Module-friendly exports: Preserve
__all__and enrichdir()so interactive use and star exports stay aligned with the stub definition. - ๐ Absolute and relative import support: Handle both local package imports and external modules, including aliased imports.
- โก Optional eager mode: Set
EAGER_IMPORT=1to resolve every declared export at import time when startup indirection is not wanted. - ๐งญ Typed, tiny surface area: Ship as a typed package with no runtime dependencies and a small public API centered on
attach_stubandLazyLoader. - ๐ Drop-in
attach_stubcall: Support the familiarattach_stub(__name__, __file__)signature, with an optional trailing__package__override when needed.
๐ฆ Installation¶
[!NOTE]
liblaf-lazy-loaderrequires Python 3.12 or newer.
๐ Quick Start¶
In mypkg/__init__.py, wire the package up once:
from liblaf.lazy_loader import attach_stub
__getattr__, __dir__, __all__ = attach_stub(__name__, __file__)
If you need to pass an explicit package anchor, use the optional third argument:
In the sibling mypkg/__init__.pyi, declare the exports you want to load lazily:
from . import cli
from ._config import Settings
from ._factory import make_settings
from rich import get_console
import rich.console as rich_console
__all__ = ["Settings", "cli", "get_console", "make_settings", "rich_console"]
With that wiring in place, Settings, cli, make_settings, get_console, and rich_console are imported only when first accessed. When the third argument is omitted entirely, attach_stub uses __name__ as the package anchor, which makes the two-argument form work as a drop-in replacement for lazy_loader.attach_stub in package __init__.py files. Passing None explicitly preserves None. The sibling .pyi file is part of the runtime configuration here, not only a type-checking aid.
๐งฉ Supported Stub Forms¶
The stub parser understands the explicit import forms that the test suite covers:
import richimport rich.console as rich_consolefrom rich import get_consolefrom . import clifrom ._factory import make_settingsfrom ._factory import make_settings as build_settings
__all__ stays aligned with the stub definition, and dir() includes both declared exports and any names already materialized on the module.
โก Eager Import Mode¶
Lazy loading defers import errors until the first attribute access. During development or tests, set EAGER_IMPORT=1 before importing the package to resolve every declared export immediately.
The current test suite also covers EAGER_IMPORT=0 for normal lazy behavior and raises a ValueError when EAGER_IMPORT is set to an invalid boolean string.
๐ง Limitations and Errors¶
- Accessing a name that is not declared in the stub raises
AttributeError. - Import failures surface when the lazy attribute is accessed, or earlier if eager mode is enabled.
- The package expects explicit import statements in the sibling stub file and uses that stub file at runtime.
๐ Compared With Alternatives¶
This project parses the sibling stub AST directly, so the runtime behavior is defined by the same file that type checkers read.
scientific-python/lazy-loader.attach_stubparses stubs into its olderattach(...)API. In its current implementation, the stub visitor only accepts within-packagefrom . import ...andfrom .foo import ...forms and raisesValueErrorfor other patterns, so it cannot express absolute entries likeimport rich.console as rich_consolein the stub. See the README and source.etils.epy.lazy_api_importsrecords imports by temporarily wrappingbuiltins.__import__. Its underlying lazy import helper rejects relative imports with aValueError, so it is not a drop-in fit for sibling package-relative exports. See the API docs and source.
โจ๏ธ Local Development¶
Clone the repository, install all dependency groups with uv, and run the maintained nox test matrix:
Build the documentation locally with:
๐ค Contributing¶
Issues and pull requests are welcome, especially around import edge cases, typing behavior, and documentation improvements.
