Source code for diplomat.utils.lazy_import

import builtins
from importlib import import_module
from importlib.util import find_spec
from types import ModuleType
from typing import Callable, Optional
import warnings
import os


ImportFunction = Callable[[str, Optional[str]], ModuleType]
""" Represents a import function. Must be able to import an object from it's dot notated access path. """


def _simple_import(name: str, pkg: Optional[str] = None) -> ModuleType:
    path = name.split(".")

    if(len(path) == 1):
        return import_module(name, pkg)
    else:
        try:
            mod = import_module(".".join(path[:-1]), pkg)
        except ImportError:
            mod = _simple_import(".".join(path[:-1]), pkg)
        return getattr(mod, path[-1])


def _dummy_print(*args, **kwargs):
    pass


def _silent_import(name: str, pkg: Optional[str] = None) -> ModuleType:
    with warnings.catch_warnings():
        # Keep deeplabcut from flooding diplomat with warning messages and print statements...
        debug_mode = os.environ.get("DIPLOMAT_DEBUG", False)

        if(not debug_mode):
            os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3"
            import tensorflow as tf
            tf.get_logger().setLevel('ERROR')

            warnings.filterwarnings("ignore")
            true_print = builtins.print
            builtins.print = _dummy_print
        try:
            return _simple_import(name, pkg)
        finally:
            if(not debug_mode):
                builtins.print = true_print
                del os.environ["TF_CPP_MIN_LOG_LEVEL"]


[docs] class ImportFunctions: """ A set of import functions that can be used by the lazy importer object. Includes the following import functions: - SIMPLE: A basic implementation of an import function. - SILENT: Makes imported package silent by disabling printing and log outputs before importing modules. """ SIMPLE = _simple_import SILENT = _silent_import
[docs] def verify_existence_of(name: str): """ Verifies a package exists without importing it, otherwise throws an ImportError. :param name: The name of the package to test for existence. :raises: ImportError if the provided module can't be found. """ if(len(name.split(".")) > 1): raise ValueError("Can only check top-level modules without attempting to import them.") try: spec = find_spec(name) if(spec is None): raise ImportError(f"Unable to find package '{name}'.") except Exception as e: raise ImportError(str(e))
[docs] class LazyImporter: """ Represents a Lazily Imported Object. It waits until the user requests functionality before actually importing a module. """ NOTHING = object()
[docs] def __init__(self, name: str, pkg: Optional[str] = None, import_function: ImportFunction = ImportFunctions.SILENT): """ Create a new lazily import module, object, or function. :param name: The name or path of the package or module to import. :param pkg: The relative path information needed for relative import. Defaults to None. :param import_function: The function to use for importing modules. See ImportFunctions for a list of pre-provided importers. """ self._name = name self._pkg = pkg self._mod = self.NOTHING self._imp = import_function
def __getattr__(self, item: str) -> "LazyImporter": """ For lazy importers, getting an attribute simply returns another lazy importer. """ return type(self)(f"{self._name}.{item}") def __call__(self, *args, **kwargs): """ Calling an attribute of a LazyImporter triggers import of the module. """ if(self._mod is self.NOTHING): self._mod = self._imp(self._name, self._pkg) return self._mod(*args, **kwargs) def __str__(self) -> str: return repr(self) def __repr__(self) -> str: return self._name