{"id":51216624,"url":"https://github.com/knitli/lateimport","last_synced_at":"2026-06-28T04:04:09.642Z","repository":{"id":340058448,"uuid":"1164346877","full_name":"knitli/lateimport","owner":"knitli","description":"Exceptionally lazy imports. Type-checker support. No dependencies.","archived":false,"fork":false,"pushed_at":"2026-04-14T02:30:31.000Z","size":88,"stargazers_count":2,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-05T18:05:02.526Z","etag":null,"topics":["lazy","lazy-evaluation","lazy-load","lazyload","lazyloading","python","type-checking"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/knitli.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE-Apache-2.0","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":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-02-23T01:10:04.000Z","updated_at":"2026-05-11T16:53:07.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/knitli/lateimport","commit_stats":null,"previous_names":["knitli/lateimport"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/knitli/lateimport","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/knitli%2Flateimport","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/knitli%2Flateimport/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/knitli%2Flateimport/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/knitli%2Flateimport/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/knitli","download_url":"https://codeload.github.com/knitli/lateimport/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/knitli%2Flateimport/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34876289,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-28T02:00:05.809Z","response_time":54,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["lazy","lazy-evaluation","lazy-load","lazyload","lazyloading","python","type-checking"],"created_at":"2026-06-28T04:04:09.574Z","updated_at":"2026-06-28T04:04:09.630Z","avatar_url":"https://github.com/knitli.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003c!--\nSPDX-FileCopyrightText: 2026 Knitli Inc.\n\nSPDX-License-Identifier: MIT OR Apache-2.0\n--\u003e\n\n# lateimport\n\nLazy import utilities for Python — defer module loading until an imported object is actually used.\n\n[![PyPI](https://img.shields.io/pypi/v/lateimport)](https://pypi.org/project/lateimport/)\n[![Python](https://img.shields.io/pypi/pyversions/lateimport)](https://pypi.org/project/lateimport/)\n[![License](https://img.shields.io/badge/license-MIT%20OR%20Apache--2.0-blue)](LICENSE-MIT)\n[![CI](https://github.com/knitli/lateimport/actions/workflows/ci.yml/badge.svg)](https://github.com/knitli/lateimport/actions/workflows/ci.yml)\n[![codecov](https://codecov.io/gh/knitli/lateimport/branch/main/graph/badge.svg?token=BQ416F8SRZ)](https://codecov.io/gh/knitli/lateimport)\n\n**Status:** Stable. Feature complete. Open to suggestions for API improvements and will fix bugs.\n\n## Overview\n\n`lateimport` provides two complementary tools:\n\n- **`lateimport`** — an explicit proxy for deferring imports in application code\n- **`create_late_getattr`** — a factory for the `__getattr__` hook in package `__init__.py` files, for use with dispatch-table-based lazy loading (e.g. as generated by [Exportify](https://github.com/knitli/exportify))\n\nWe developed lateimport for [CodeWeaver](https://github.com/knitli/codeweaver), and we liked it so much we decided to ship it as its own package for other projects. It has been rigorously tested and has complete test coverage.\n\n## Dependency Free. Lightweight.\n\n- lateimport is purely stdlib -- **zero outside dependencies**.\n- **Small package** -- the whole implementation is ~280 lines of code. For that you get modern typing and type checker support, and *truly lazy* imports.\n\n## Why?\n\nThere are a lot of late/lazy/delayed import solutions out there. Lateimport's main differences:\n\n- Complete type checking support\n- **Actually lazy**. Most lazy patterns delay until the lazy object is in scope. **lateimport delays imports until they are actually directly accessed** (i.e. you access an attribute or call it).\n- **Get linting and automation with [Exportify](https://github.com/knitli/exportify)**. Exportify is a CLI development tool that lets you declaratively set rules for import and export patterns, and validate all lazy imports from lateimport. You can say goodbye to broken lazy imports and updating `__all__`.\n\n## Requirements\n\nPython 3.12+\n\n## Installation\n\n```bash\npip install lateimport\n```\n\n## Usage\n\n### Explicit lazy imports\n\nUse `lateimport` when you want to defer a heavy import in application code. The module is not imported until the returned proxy is actually *used* — called, subscripted, passed to `isinstance`, etc.\n\n```python\nfrom lateimport import lateimport\n\n# No import happens here\nnumpy = lateimport(\"numpy\")\npandas = lateimport(\"pandas\", \"DataFrame\")  # defers pandas.DataFrame\n\n# Import triggered on first use\nresult = numpy.array([1, 2, 3])\ndf = pandas({\"a\": [1, 2]})\n```\n\nProxies are **thread-safe** and cache the resolved object after first access.\n\n```python\nproxy = lateimport(\"heavy.module\", \"ExpensiveClass\")\n\nproxy.is_resolved()   # False\ninstance = proxy()    # heavy.module imported, ExpensiveClass resolved\nproxy.is_resolved()   # True\n```\n\n### Type annotations\n\n`LateImport[T]` is a generic class. Annotating a variable with the resolved type gives you full type-checking and autocompletion downstream — the type checker knows that calling or accessing the proxy produces a `T`.\n\n```python\nfrom __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nfrom lateimport import LateImport, lateimport\n\nif TYPE_CHECKING:\n    import numpy as np\n    from pandas import DataFrame\n\n# Type checker knows ndarray() returns np.ndarray\nndarray: LateImport[np.ndarray] = lateimport(\"numpy\", \"ndarray\")\narr = ndarray([1, 2, 3])  # arr: np.ndarray\n\n# type[DataFrame] means calling the proxy produces a DataFrame instance\nDataFrame: LateImport[type[DataFrame]] = lateimport(\"pandas\", \"DataFrame\")\ndf = DataFrame({\"col\": [1, 2, 3]})  # df: DataFrame\n```\n\n\u003e [!IMPORTANT]\n\u003e The `TYPE_CHECKING` guard keeps the imports from executing at runtime — you get static type information without the import cost, which is the whole point.\n\nFor types that are always available no guard is needed:\n\n```python\nfrom pathlib import Path\nfrom lateimport import LateImport, lateimport\n\nMkPath: LateImport[type[Path]] = lateimport(\"pathlib\", \"Path\")\np = MkPath(\"/tmp/out\")  # p: Path\n```\n\n### Package-level lazy `__getattr__`\n\nFor packages using a dispatch-table pattern (e.g. generated by [Exportify](knitli/exportify), `create_late_getattr` creates a `__getattr__` hook that imports attributes on demand:\n\n```python\n# mypackage/__init__.py\nfrom types import MappingProxyType\nfrom lateimport import create_late_getattr\n# NOTE: for IDE support, import any dynamic imports in a TYPE_CHECKING block:\nfrom typing import TYPE_CHECKING:\n    from mypackage.core.models import MyClass\n    from mypackage.utils.helpers import my_function\n    from mypackage import SubModule\n\n_dynamic_imports = MappingProxyType({\n    \"MyClass\":      (\"mypackage.core\",   \"models\"),\n    \"my_function\":  (\"mypackage.utils\",  \"helpers\"),\n    # for the current package you can just use __spec__.parent:\n    \"SubModule\":    (__spec__.parent,        \"__module__\"),\n})\n\n__getattr__ = create_late_getattr(_dynamic_imports, globals(), __name__)\n\n__all__ = (\"MyClass\", \"my_function\", \"SubModule\")\n\n# Make sure dir calls use __all__ and not globals:\n__dir__ = lambda: list(__all__)\n```\n\nThe dispatch tuple is `(package, submodule)`:\n- **Normal attributes**: imports `package.submodule` and returns `getattr(submodule, attr_name)`\n- **`\"__module__\"`**: imports the submodule itself as the attribute (`import_module(f\".{attr_name}\", package=package)`)\n\nResolved attributes are cached in `globals()` so subsequent accesses are direct.\n\n## API\n\n### `lateimport(module_name, *attrs) -\u003e LateImport[T]`\n\nCreate a lazy proxy for `module_name`. Optional `attrs` are an attribute chain traversed after import. The function is generic — annotate the variable as `LateImport[T]` to propagate the resolved type to callers.\n\n```python\nlateimport(\"os\")                    # proxy for the os module\nlateimport(\"os.path\", \"join\")       # proxy for os.path.join\nlateimport(\"mypackage\", \"A\", \"B\")   # proxy for mypackage.A.B\n```\n\n### `class LateImport[T]`\n\nThe generic proxy class returned by `lateimport`. Annotate the variable with `LateImport[T]` to tell the type checker what type the proxy resolves to — `__call__` and `_resolve()` are typed to return `T`. Supports call, attribute access, `dir()`, and `repr()`. Thread-safe.\n\n| Method | Description |\n|--------|-------------|\n| `is_resolved()` | `True` if the import has been triggered |\n| `_resolve()` | Force immediate resolution and return the object (typed as `T`) |\n\n### `create_late_getattr(dynamic_imports, module_globals, module_name)`\n\nCreate a `__getattr__` function for package-level lazy loading.\n\n| Parameter | Type | Description |\n|-----------|------|-------------|\n| `dynamic_imports` | `MappingProxyType[str, tuple[str, str]]` | Dispatch table |\n| `module_globals` | `dict[str, object]` | `globals()` of the calling module |\n| `module_name` | `str` | `__name__` of the calling module |\n\n### `INTROSPECTION_ATTRIBUTES`\n\n`frozenset` of dunder attribute names that are resolved immediately rather than proxied (e.g. `__doc__`, `__name__`, `__module__`). Available for reference when implementing custom proxy patterns.\n\n## License\n\n`MIT OR Apache-2.0` — see [LICENSE-MIT](LICENSES/MIT.txt) and [LICENSE-Apache-2.0](LICENSES/Apache-2.0.txt).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fknitli%2Flateimport","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fknitli%2Flateimport","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fknitli%2Flateimport/lists"}