Skip to content

liblaf.lazy_loader

Stub-driven lazy exports for package modules.

Use attach_stub in a package __init__.py and declare the exported imports in the adjacent .pyi file. The runtime loader supports both package-relative imports and absolute imports while keeping the public surface area small.

Classes:

  • Getter

    Define how one exported name is resolved from a stub entry.

  • GetterContext

    Carry module metadata needed to resolve a lazy export.

  • GetterImport

    Resolve an import ... statement from the stub file.

  • GetterImportFrom

    Resolve a from ... import ... statement from the stub file.

  • LazyLoader

    Resolve stub-declared exports on first attribute access.

  • StubVisitor

    Parse a stub AST into getters and optional export names.

Functions:

  • attach_stub

    Create module hooks from a sibling stub file.

  • env_bool

    Parse an environment variable as a boolean.

Attributes:

__commit_id__ module-attribute

__commit_id__: str | None = None

__version__ module-attribute

__version__: str = '0.2.1.dev6+g4d05211bf'

__version_tuple__ module-attribute

__version_tuple__: tuple[int | str, ...] = (
    0,
    2,
    1,
    "dev6",
    "g4d05211bf",
)

Getter dataclass

Getter()

Bases: ABC


              flowchart TD
              liblaf.lazy_loader.Getter[Getter]

              

              click liblaf.lazy_loader.Getter href "" "liblaf.lazy_loader.Getter"
            

Define how one exported name is resolved from a stub entry.

Methods:

  • get

    Resolve the exported value for the given module context.

Attributes:

  • attr_name (str) –

    Return the attribute name exposed on the target module.

attr_name abstractmethod property

attr_name: str

Return the attribute name exposed on the target module.

get abstractmethod

get(ctx: GetterContext) -> Any

Resolve the exported value for the given module context.

Source code in src/liblaf/lazy_loader/_getter.py
@abc.abstractmethod
def get(self, ctx: GetterContext) -> Any:
    """Resolve the exported value for the given module context."""

GetterContext dataclass

GetterContext(module: str, package: str | None)

Carry module metadata needed to resolve a lazy export.

Parameters:

  • module

    (str) –

    Package anchor used for relative imports.

  • package

    (str | None) –

Attributes:

module instance-attribute

module: str

Package anchor used for relative imports.

package instance-attribute

package: str | None

GetterImport dataclass

GetterImport(name: str, asname: str | None)

Bases: Getter


              flowchart TD
              liblaf.lazy_loader.GetterImport[GetterImport]
              liblaf.lazy_loader._getter.Getter[Getter]

                              liblaf.lazy_loader._getter.Getter --> liblaf.lazy_loader.GetterImport
                


              click liblaf.lazy_loader.GetterImport href "" "liblaf.lazy_loader.GetterImport"
              click liblaf.lazy_loader._getter.Getter href "" "liblaf.lazy_loader._getter.Getter"
            

Resolve an import ... statement from the stub file.

An aliased import exposes the alias directly. Without an alias, the getter follows Python import semantics and exposes the top-level module name.

Parameters:

  • name

    (str) –

    Optional alias exposed on the lazily loaded module.

  • asname

    (str | None) –

Methods:

  • get

    Resolve the exported value for the given module context.

Attributes:

  • asname (str | None) –
  • attr_name (str) –

    Return the attribute name exposed on the target module.

  • name (str) –

    Optional alias exposed on the lazily loaded module.

asname instance-attribute

asname: str | None

attr_name property

attr_name: str

Return the attribute name exposed on the target module.

name instance-attribute

name: str

Optional alias exposed on the lazily loaded module.

get

get(ctx: GetterContext) -> Any

Resolve the exported value for the given module context.

Source code in src/liblaf/lazy_loader/_getter.py
@override
def get(self, ctx: GetterContext) -> Any:
    if self.asname:
        return importlib.import_module(self.name, package=ctx.package)
    importlib.import_module(self.name, package=ctx.package)
    asname: str = self.name.split(".", 1)[0]
    return importlib.import_module(asname, package=ctx.package)

GetterImportFrom dataclass

GetterImportFrom(
    module: str | None,
    name: str,
    asname: str | None,
    level: int,
)

Bases: Getter


              flowchart TD
              liblaf.lazy_loader.GetterImportFrom[GetterImportFrom]
              liblaf.lazy_loader._getter.Getter[Getter]

                              liblaf.lazy_loader._getter.Getter --> liblaf.lazy_loader.GetterImportFrom
                


              click liblaf.lazy_loader.GetterImportFrom href "" "liblaf.lazy_loader.GetterImportFrom"
              click liblaf.lazy_loader._getter.Getter href "" "liblaf.lazy_loader._getter.Getter"
            

Resolve a from ... import ... statement from the stub file.

This getter supports both absolute imports and relative imports. When a direct attribute lookup would recurse back into the lazily loaded module, it falls back to importing the submodule itself.

Parameters:

  • module

    (str | None) –

    Imported attribute or submodule name.

  • name

    (str) –

    Optional alias exposed on the lazily loaded module.

  • asname

    (str | None) –

    Relative import depth encoded by the stub AST node.

  • level

    (int) –

Methods:

  • get

    Resolve the exported value for the given module context.

Attributes:

  • asname (str | None) –

    Relative import depth encoded by the stub AST node.

  • attr_name (str) –

    Return the attribute name exposed on the target module.

  • level (int) –
  • module (str | None) –

    Imported attribute or submodule name.

  • name (str) –

    Optional alias exposed on the lazily loaded module.

asname instance-attribute

asname: str | None

Relative import depth encoded by the stub AST node.

attr_name property

attr_name: str

Return the attribute name exposed on the target module.

level instance-attribute

level: int

module instance-attribute

module: str | None

Imported attribute or submodule name.

name instance-attribute

name: str

Optional alias exposed on the lazily loaded module.

get

get(ctx: GetterContext) -> Any

Resolve the exported value for the given module context.

Source code in src/liblaf/lazy_loader/_getter.py
@override
def get(self, ctx: GetterContext) -> Any:
    module_name: str = "." * self.level
    if self.module:
        module_name += self.module
    module: types.ModuleType = importlib.import_module(
        module_name, package=ctx.package
    )
    if module.__name__ != ctx.module:  # avoid recursion
        value: Any = getattr(module, self.name, MISSING)
        if value is not MISSING:
            return value
    module_name += f".{self.name}" if self.module else self.name
    return importlib.import_module(module_name, package=ctx.package)

LazyLoader dataclass

LazyLoader(
    name: str,
    package: str | None,
    exports: list[str] | None,
    getters: dict[str, Getter],
)

Resolve stub-declared exports on first attribute access.

The loader backs the tuple returned by attach_stub. It keeps the parsed getter objects, exposes a module-friendly __dir__ and __all__, and caches each resolved value onto the target module after the first lookup.

Parameters:

  • name

    (str) –

    Package anchor used for relative imports.

  • package

    (str | None) –

    Export names declared by __all__ in the stub, if present.

  • exports

    (list[str] | None) –

    Mapping from exported attribute names to getter objects.

  • getters

    (dict[str, Getter]) –

Methods:

  • __dir__

    List declared exports together with materialized module attributes.

  • __getattr__

    Import and cache one declared export.

Attributes:

__all__ cached property

__all__: list[str]

Return exported names for module __all__.

context cached property

context: GetterContext

Return import context shared by all getters.

exports instance-attribute

exports: list[str] | None

Mapping from exported attribute names to getter objects.

getters instance-attribute

getters: dict[str, Getter]

module cached property

module: ModuleType

Return the target module object from sys.modules.

name instance-attribute

name: str

Package anchor used for relative imports.

package instance-attribute

package: str | None

Export names declared by __all__ in the stub, if present.

__dir__

__dir__() -> list[str]

List declared exports together with materialized module attributes.

Source code in src/liblaf/lazy_loader/_loader.py
def __dir__(self) -> list[str]:
    """List declared exports together with materialized module attributes."""
    return sorted(set().union(vars(self.module), self.__all__))

__getattr__

__getattr__(name: str) -> Any

Import and cache one declared export.

Parameters:

  • name

    (str) –

    Exported attribute name requested from the module.

Returns:

  • Any

    The resolved module, function, class, or other imported object.

Raises:

Source code in src/liblaf/lazy_loader/_loader.py
def __getattr__(self, name: str) -> Any:
    """Import and cache one declared export.

    Args:
        name: Exported attribute name requested from the module.

    Returns:
        The resolved module, function, class, or other imported object.

    Raises:
        AttributeError: If `name` is not declared by the stub.
        ImportError: If resolving the declared import fails.
    """
    if name not in self.getters:
        msg: str = f"module '{self.name}' has no attribute '{name}'"
        raise AttributeError(msg, name=name, obj=self.module)
    getter: Getter = self.getters[name]
    value: Any = getter.get(self.context)
    setattr(self.module, name, value)
    return value

StubVisitor dataclass

StubVisitor(
    exports: list[str] | None = None,
    getters: dict[str, Getter] = dict(),
)

Bases: NodeVisitor


              flowchart TD
              liblaf.lazy_loader.StubVisitor[StubVisitor]

              

              click liblaf.lazy_loader.StubVisitor href "" "liblaf.lazy_loader.StubVisitor"
            

Parse a stub AST into getters and optional export names.

Parameters:

  • exports

    (list[str] | None, default: None ) –

    Mapping populated from explicit import statements in the stub.

  • getters

    (dict[str, Getter], default: <class 'dict'> ) –

    dict() -> new empty dictionary dict(mapping) -> new dictionary initialized from a mapping object's (key, value) pairs dict(iterable) -> new dictionary initialized as if via: d = {} for k, v in iterable: d[k] = v dict(**kwargs) -> new dictionary initialized with the name=value pairs in the keyword argument list. For example: dict(one=1, two=2)

Methods:

  • finish

    Build a loader from the imports collected so far.

  • visit_AnnAssign

    Capture annotated __all__ assignments from the stub.

  • visit_Assign

    Capture __all__ = [...] assignments from the stub.

  • visit_Import

    Convert import ... statements into getter entries.

  • visit_ImportFrom

    Convert from ... import ... statements into getter entries.

Attributes:

exports class-attribute instance-attribute

exports: list[str] | None = None

Mapping populated from explicit import statements in the stub.

getters class-attribute instance-attribute

getters: dict[str, Getter] = field(default_factory=dict)

finish

finish(name: str, package: str | None) -> LazyLoader

Build a loader from the imports collected so far.

Source code in src/liblaf/lazy_loader/_visitor.py
def finish(self, name: str, package: str | None) -> LazyLoader:
    """Build a loader from the imports collected so far."""
    getters: dict[str, Getter] = self.getters
    if self.exports is not None:
        exports: set[str] = set(self.exports)
        getters: dict[str, Getter] = {
            attr_name: getter
            for attr_name, getter in getters.items()
            if attr_name in exports
        }
    return LazyLoader(
        name=name, package=package, exports=self.exports, getters=getters
    )

visit_AnnAssign

visit_AnnAssign(node: AnnAssign) -> None

Capture annotated __all__ assignments from the stub.

Source code in src/liblaf/lazy_loader/_visitor.py
def visit_AnnAssign(self, node: ast.AnnAssign) -> None:
    """Capture annotated `__all__` assignments from the stub."""
    target: ast.expr = node.target
    if not isinstance(target, ast.Name):
        return
    if target.id != "__all__":
        return
    if node.value is None:
        return
    self.exports = list(ast.literal_eval(node.value))

visit_Assign

visit_Assign(node: Assign) -> None

Capture __all__ = [...] assignments from the stub.

Source code in src/liblaf/lazy_loader/_visitor.py
def visit_Assign(self, node: ast.Assign) -> None:
    """Capture `__all__ = [...]` assignments from the stub."""
    if len(node.targets) != 1:
        return
    target: ast.expr = node.targets[0]
    if not isinstance(target, ast.Name):
        return
    if target.id != "__all__":
        return
    self.exports = list(ast.literal_eval(node.value))

visit_Import

visit_Import(node: Import) -> None

Convert import ... statements into getter entries.

Source code in src/liblaf/lazy_loader/_visitor.py
def visit_Import(self, node: ast.Import) -> None:
    """Convert `import ...` statements into getter entries."""
    for alias in node.names:
        getter: GetterImport = GetterImport(name=alias.name, asname=alias.asname)
        self.getters[getter.attr_name] = getter

visit_ImportFrom

visit_ImportFrom(node: ImportFrom) -> None

Convert from ... import ... statements into getter entries.

Raises:

  • ValueError

    If the stub contains a wildcard import.

Source code in src/liblaf/lazy_loader/_visitor.py
def visit_ImportFrom(self, node: ast.ImportFrom) -> None:
    """Convert `from ... import ...` statements into getter entries.

    Raises:
        ValueError: If the stub contains a wildcard import.
    """
    for alias in node.names:
        if alias.name == "*":
            msg: str = f"liblaf.lazy_loader does not support wild card form of import: `{ast.unparse(node)}`"
            raise ValueError(msg)
        getter: GetterImportFrom = GetterImportFrom(
            module=node.module,
            name=alias.name,
            asname=alias.asname,
            level=node.level,
        )
        self.getters[getter.attr_name] = getter

attach_stub

attach_stub(
    name: str, file: str
) -> tuple[GetAttr, Dir, All]
attach_stub(
    name: str, file: str, package: str | None
) -> tuple[GetAttr, Dir, All]

Create module hooks from a sibling stub file.

Call this from a package __init__.py and assign the returned values to __getattr__, __dir__, and __all__. The function reads the adjacent .pyi file, parses its explicit import statements into a LazyLoader, and optionally resolves every export at import time when EAGER_IMPORT is enabled.

Parameters:

  • name

    (str) –

    Module name, typically __name__.

  • file

    (str) –

    Path to the module file whose sibling .pyi file declares the lazy exports, typically __file__.

  • package

    (str | None | MissingType, default: MISSING ) –

    Optional package name used to resolve relative imports, typically __package__. When the argument is omitted entirely, name is reused so attach_stub(__name__, __file__) works as a drop-in call for package __init__.py modules. Passing None explicitly preserves None.

Returns:

  • GetAttr

    A tuple containing __getattr__, __dir__, and __all__ in that

  • Dir

    order.

Raises:

  • FileNotFoundError

    If the sibling .pyi file does not exist.

  • SyntaxError

    If the sibling stub cannot be parsed as Python.

  • ValueError

    If EAGER_IMPORT is not a valid boolean string or the stub uses an unsupported construct such as a wildcard import.

  • ImportError

    If eager loading is enabled and one of the declared imports cannot be resolved.

Source code in src/liblaf/lazy_loader/_attach_stub.py
def attach_stub(
    name: str, file: str, package: str | None | MissingType = MISSING
) -> tuple[GetAttr, Dir, All]:
    """Create module hooks from a sibling stub file.

    Call this from a package `__init__.py` and assign the returned values to
    `__getattr__`, `__dir__`, and `__all__`. The function reads the adjacent
    `.pyi` file, parses its explicit import statements into a [LazyLoader]
    [liblaf.lazy_loader.LazyLoader], and optionally resolves every export at
    import time when `EAGER_IMPORT` is enabled.

    Args:
        name: Module name, typically `__name__`.
        file: Path to the module file whose sibling `.pyi` file declares the
            lazy exports, typically `__file__`.
        package: Optional package name used to resolve relative imports,
            typically `__package__`. When the argument is omitted entirely,
            `name` is reused so `attach_stub(__name__, __file__)` works as a
            drop-in call for package `__init__.py` modules. Passing `None`
            explicitly preserves `None`.

    Returns:
        A tuple containing `__getattr__`, `__dir__`, and `__all__` in that
        order.

    Raises:
        FileNotFoundError: If the sibling `.pyi` file does not exist.
        SyntaxError: If the sibling stub cannot be parsed as Python.
        ValueError: If `EAGER_IMPORT` is not a valid boolean string or the stub
            uses an unsupported construct such as a wildcard import.
        ImportError: If eager loading is enabled and one of the declared
            imports cannot be resolved.
    """
    if package is MISSING:
        package = name
    file: Path = Path(file)
    stub_file: Path = file.with_suffix(".pyi")
    node: ast.Module = ast.parse(stub_file.read_text())
    visitor: StubVisitor = StubVisitor()
    visitor.visit(node)
    loader: LazyLoader = visitor.finish(name=name, package=package)
    if env_bool("EAGER_IMPORT", False):  # noqa: FBT003
        for attr_name in loader.getters:
            loader.__getattr__(attr_name)
    return loader.__getattr__, loader.__dir__, loader.__all__

env_bool

env_bool(name: str, default: bool = False) -> bool

Parse an environment variable as a boolean.

Parameters:

  • name

    (str) –

    Environment variable name to read.

  • default

    (bool, default: False ) –

    Value to return when the variable is unset.

Returns:

  • bool

    The parsed boolean value.

Raises:

  • ValueError

    If the environment variable is set but does not match one of the accepted truthy or falsy strings.

Source code in src/liblaf/lazy_loader/_utils.py
def env_bool(name: str, default: bool = False) -> bool:  # noqa: FBT001, FBT002
    """Parse an environment variable as a boolean.

    Args:
        name: Environment variable name to read.
        default: Value to return when the variable is unset.

    Returns:
        The parsed boolean value.

    Raises:
        ValueError: If the environment variable is set but does not match one
            of the accepted truthy or falsy strings.
    """
    value: str | None = os.getenv(name)
    if value is None:
        return default
    if value in _ENV_BOOL_TRUTHY:
        return True
    if value in _ENV_BOOL_FALSY:
        return False
    msg: str = f"Environment variable {name!r} invalid: Not a valid boolean."
    raise ValueError(msg)