https://github.com/liblaf/lazy-loader
https://github.com/liblaf/lazy-loader
Last synced: 3 months ago
JSON representation
- Host: GitHub
- URL: https://github.com/liblaf/lazy-loader
- Owner: liblaf
- License: mit
- Created: 2026-03-17T01:53:13.000Z (3 months ago)
- Default Branch: main
- Last Pushed: 2026-03-26T09:16:03.000Z (3 months ago)
- Last Synced: 2026-03-27T03:27:33.124Z (3 months ago)
- Language: Python
- Size: 232 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 4
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README

**[Explore the docs ยป](https://liblaf-lazy-loader.readthedocs.io/)**
[](https://codecov.io/gh/liblaf/lazy-loader)
[](https://github.com/liblaf/lazy-loader/actions/workflows/python-docs.yaml)
[](https://github.com/liblaf/lazy-loader/actions/workflows/shared-mega-linter.yaml)
[](https://github.com/liblaf/lazy-loader/actions/workflows/python-test.yaml)
[](https://results.pre-commit.ci/latest/github/liblaf/lazy-loader/main)
[](https://pypi.org/project/liblaf-lazy-loader)
[](https://pypi.org/project/liblaf-lazy-loader)
[](https://github.com/astral-sh/uv)
[Changelog](https://github.com/liblaf/lazy-loader/blob/main/CHANGELOG.md) ยท [Report Bug](https://github.com/liblaf/lazy-loader/issues) ยท [Request Feature](https://github.com/liblaf/lazy-loader/issues)

`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 `.pyi` file and turn its `import` and `from ... import ...` statements into on-demand attribute loaders.
- ๐ฆ **Module-friendly exports:** Preserve `__all__` and enrich `dir()` 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=1` to 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_stub` and `LazyLoader`.
- ๐ **Drop-in `attach_stub` call:** Support the familiar `attach_stub(__name__, __file__)` signature, with an optional trailing `__package__` override when needed.
## ๐ฆ Installation
> [!NOTE]
> `liblaf-lazy-loader` requires Python 3.12 or newer.
```bash
uv add liblaf-lazy-loader
```
## ๐ Quick Start
In `mypkg/__init__.py`, wire the package up once:
```python
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:
```python
__getattr__, __dir__, __all__ = attach_stub(__name__, __file__, __package__)
```
In the sibling `mypkg/__init__.pyi`, declare the exports you want to load lazily:
```python
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 rich`
- `import rich.console as rich_console`
- `from rich import get_console`
- `from . import cli`
- `from ._factory import make_settings`
- `from ._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_stub`](https://github.com/scientific-python/lazy-loader) parses stubs into its older `attach(...)` API. In its current implementation, the stub visitor only accepts within-package `from . import ...` and `from .foo import ...` forms and raises `ValueError` for other patterns, so it cannot express absolute entries like `import rich.console as rich_console` in the stub. See the [README](https://github.com/scientific-python/lazy-loader) and [source](https://raw.githubusercontent.com/scientific-python/lazy-loader/main/src/lazy_loader/__init__.py).
- [`etils.epy.lazy_api_imports`](https://etils.readthedocs.io/en/latest/api/epy/lazy_api_imports.html) records imports by temporarily wrapping `builtins.__import__`. Its underlying lazy import helper rejects relative imports with a `ValueError`, so it is not a drop-in fit for sibling package-relative exports. See the [API docs](https://etils.readthedocs.io/en/latest/api/epy/lazy_api_imports.html) and [source](https://cdn.jsdelivr.net/gh/google/etils@main/etils/epy/lazy_api_imports_utils.py).
## โจ๏ธ Local Development
Clone the repository, install all dependency groups with `uv`, and run the maintained `nox` test matrix:
```bash
gh repo clone liblaf/lazy-loader
cd lazy-loader
mise run install
nox
```
Build the documentation locally with:
```bash
mise run docs:build
```
## ๐ค Contributing
Issues and pull requests are welcome, especially around import edge cases, typing behavior, and documentation improvements.
[](https://github.com/liblaf/lazy-loader/pulls)
[](https://github.com/liblaf/lazy-loader/graphs/contributors)
---
#### ๐ License
Copyright ยฉ 2026 [liblaf](https://github.com/liblaf).
This project is [MIT](https://github.com/liblaf/lazy-loader/blob/main/LICENSE) licensed.