{"id":48042220,"url":"https://github.com/liblaf/lazy-loader","last_synced_at":"2026-04-04T14:11:34.676Z","repository":{"id":344978686,"uuid":"1183833581","full_name":"liblaf/lazy-loader","owner":"liblaf","description":null,"archived":false,"fork":false,"pushed_at":"2026-03-26T09:16:03.000Z","size":238,"stargazers_count":0,"open_issues_count":4,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-27T03:27:33.124Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/liblaf.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"custom":["https://afdian.com/a/liblaf"]}},"created_at":"2026-03-17T01:53:13.000Z","updated_at":"2026-03-26T09:15:25.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/liblaf/lazy-loader","commit_stats":null,"previous_names":["liblaf/lazy-loader"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/liblaf/lazy-loader","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/liblaf%2Flazy-loader","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/liblaf%2Flazy-loader/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/liblaf%2Flazy-loader/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/liblaf%2Flazy-loader/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/liblaf","download_url":"https://codeload.github.com/liblaf/lazy-loader/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/liblaf%2Flazy-loader/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31402278,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-04T10:20:44.708Z","status":"ssl_error","status_checked_at":"2026-04-04T10:20:06.846Z","response_time":60,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-04-04T14:11:34.044Z","updated_at":"2026-04-04T14:11:34.627Z","avatar_url":"https://github.com/liblaf.png","language":"Python","funding_links":["https://afdian.com/a/liblaf"],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\" markdown\u003e\n\n![lazy-loader](https://socialify.git.ci/liblaf/lazy-loader/image?description=1\u0026forks=1\u0026issues=1\u0026language=1\u0026name=1\u0026owner=1\u0026pattern=Transparent\u0026pulls=1\u0026stargazers=1\u0026theme=Auto)\n\n**[Explore the docs »](https://liblaf-lazy-loader.readthedocs.io/)**\n\n[![codecov](https://codecov.io/gh/liblaf/lazy-loader/graph/badge.svg)](https://codecov.io/gh/liblaf/lazy-loader)\n[![Docs](https://github.com/liblaf/lazy-loader/actions/workflows/python-docs.yaml/badge.svg)](https://github.com/liblaf/lazy-loader/actions/workflows/python-docs.yaml)\n[![MegaLinter](https://github.com/liblaf/lazy-loader/actions/workflows/shared-mega-linter.yaml/badge.svg)](https://github.com/liblaf/lazy-loader/actions/workflows/shared-mega-linter.yaml)\n[![Tests](https://github.com/liblaf/lazy-loader/actions/workflows/python-test.yaml/badge.svg)](https://github.com/liblaf/lazy-loader/actions/workflows/python-test.yaml)\n[![pre-commit.ci status](https://results.pre-commit.ci/badge/github/liblaf/lazy-loader/main.svg)](https://results.pre-commit.ci/latest/github/liblaf/lazy-loader/main)\n[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/liblaf-lazy-loader?logo=Python\u0026label=Python)](https://pypi.org/project/liblaf-lazy-loader)\n[![PyPI - Version](https://img.shields.io/pypi/v/liblaf-lazy-loader?logo=PyPI\u0026label=PyPI)](https://pypi.org/project/liblaf-lazy-loader)\n[![uv](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/uv/main/assets/badge/v0.json)](https://github.com/astral-sh/uv)\n\n[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)\n\n![Rule](https://cdn.jsdelivr.net/gh/andreasbm/readme/assets/lines/rainbow.png)\n\n\u003c/div\u003e\n\n`liblaf-lazy-loader` lets Python packages expose stub-driven lazy exports with both absolute imports and package-relative imports.\n\n## ✨ Features\n\n- 💤 **Stub-driven lazy imports:** Parse a sibling `.pyi` file and turn its `import` and `from ... import ...` statements into on-demand attribute loaders.\n- 📦 **Module-friendly exports:** Preserve `__all__` and enrich `dir()` so interactive use and star exports stay aligned with the stub definition.\n- 🔁 **Absolute and relative import support:** Handle both local package imports and external modules, including aliased imports.\n- ⚡ **Optional eager mode:** Set `EAGER_IMPORT=1` to resolve every declared export at import time when startup indirection is not wanted.\n- 🧭 **Typed, tiny surface area:** Ship as a typed package with no runtime dependencies and a small public API centered on `attach_stub` and `LazyLoader`.\n- 🔄 **Drop-in `attach_stub` call:** Support the familiar `attach_stub(__name__, __file__)` signature, with an optional trailing `__package__` override when needed.\n\n## 📦 Installation\n\n\u003e [!NOTE]\n\u003e `liblaf-lazy-loader` requires Python 3.12 or newer.\n\n```bash\nuv add liblaf-lazy-loader\n```\n\n## 🚀 Quick Start\n\nIn `mypkg/__init__.py`, wire the package up once:\n\n```python\nfrom liblaf.lazy_loader import attach_stub\n\n__getattr__, __dir__, __all__ = attach_stub(__name__, __file__)\n```\n\nIf you need to pass an explicit package anchor, use the optional third argument:\n\n```python\n__getattr__, __dir__, __all__ = attach_stub(__name__, __file__, __package__)\n```\n\nIn the sibling `mypkg/__init__.pyi`, declare the exports you want to load lazily:\n\n```python\nfrom . import cli\nfrom ._config import Settings\nfrom ._factory import make_settings\nfrom rich import get_console\nimport rich.console as rich_console\n\n__all__ = [\"Settings\", \"cli\", \"get_console\", \"make_settings\", \"rich_console\"]\n```\n\nWith 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.\n\n## 🧩 Supported Stub Forms\n\nThe stub parser understands the explicit import forms that the test suite covers:\n\n- `import rich`\n- `import rich.console as rich_console`\n- `from rich import get_console`\n- `from . import cli`\n- `from ._factory import make_settings`\n- `from ._factory import make_settings as build_settings`\n\n`__all__` stays aligned with the stub definition, and `dir()` includes both declared exports and any names already materialized on the module.\n\n## ⚡ Eager Import Mode\n\nLazy 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.\n\nThe 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.\n\n## 🚧 Limitations and Errors\n\n- Accessing a name that is not declared in the stub raises `AttributeError`.\n- Import failures surface when the lazy attribute is accessed, or earlier if eager mode is enabled.\n- The package expects explicit import statements in the sibling stub file and uses that stub file at runtime.\n\n## 🔍 Compared With Alternatives\n\nThis project parses the sibling stub AST directly, so the runtime behavior is defined by the same file that type checkers read.\n\n- [`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).\n- [`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).\n\n## ⌨️ Local Development\n\nClone the repository, install all dependency groups with `uv`, and run the maintained `nox` test matrix:\n\n```bash\ngh repo clone liblaf/lazy-loader\ncd lazy-loader\nmise run install\nnox\n```\n\nBuild the documentation locally with:\n\n```bash\nmise run docs:build\n```\n\n## 🤝 Contributing\n\nIssues and pull requests are welcome, especially around import edge cases, typing behavior, and documentation improvements.\n\n[![PR Welcome](https://img.shields.io/badge/%F0%9F%A4%AF%20PR%20WELCOME-%E2%86%92-ffcb47?labelColor=black\u0026style=for-the-badge)](https://github.com/liblaf/lazy-loader/pulls)\n\n[![Contributors](https://gh-contributors-gamma.vercel.app/api?repo=liblaf/lazy-loader)](https://github.com/liblaf/lazy-loader/graphs/contributors)\n\n---\n\n#### 📝 License\n\nCopyright © 2026 [liblaf](https://github.com/liblaf). \u003cbr /\u003e\nThis project is [MIT](https://github.com/liblaf/lazy-loader/blob/main/LICENSE) licensed.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fliblaf%2Flazy-loader","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fliblaf%2Flazy-loader","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fliblaf%2Flazy-loader/lists"}