blob: 1e08f3bbc203803a9e101a3a475799675d6606c4 [file] [log] [blame] [edit]
# Copyright 2023 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Basic lazy module loader."""
import functools
import importlib
import sys
from typing import Any, Callable, Tuple
class ForFunctions:
"""Load modules only when they're needed.
This helps speed things up when only some of the modules are used.
This only works for modules that export functions. Other types (e.g.
classes, properties, etc...) are not supported.
"""
def __init__(self, name: str):
"""Initialize.
Args:
name: The module name to import.
"""
self._mod = None
self._modname = name
def _get_mod(self):
"""Load the module and return it."""
if self._mod is None:
self._mod = importlib.import_module(self._modname)
return self._mod
def _wrapped_func(self, name: str, *args, **kwargs) -> Any:
"""Load the module and then call |name|."""
mod = self._get_mod()
return getattr(mod, name)(*args, **kwargs)
def __getattr__(self, name: str) -> Callable:
"""Return a callable to the module function."""
return functools.partial(self._wrapped_func, name)
def __str__(self) -> str:
return f"lazy_loader.ForFunctions({self._modname})"
def __eq__(self, other: Any) -> bool:
"""Determine if objects are equal."""
return (
isinstance(other, ForFunctions) and self._modname == other._modname
)
def __ne__(self, other: Any) -> bool:
"""Determine if objects are not equal."""
return not self == other
def __getstate__(self) -> Tuple[Any]:
"""Return pickleable state for this Manifest."""
return (self._modname,)
def __setstate__(self, state: Tuple[Any]) -> None:
"""Set the state from pickle for this Manifest."""
# We don't need to pickle the module directly as it'll be cached in
# sys.modules. Try to pull it back out to avoid importlib call.
(self._modname,) = state
self._mod = sys.modules.get(self._modname)