""" Provides utilities for dynamically loading packages/modules. """ import importlib import importlib.util import logging import os import pkgutil import sys from pathlib import Path from typing import Generator, List, Union logger = logging.getLogger(__name__) def get_module_by_path( module_name: str, file_path: Union[str, Path] ) -> "module": """ Execute and return module from *file_path* """ # https://docs.python.org/3/library/importlib.html#importing-a-source-file-directly spec = importlib.util.spec_from_file_location(module_name, file_path) module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) return module def iter_modules( paths: List[Union[str, Path]] ) -> Generator["module", None, None]: """ Yields all modules from *paths* """ for module in pkgutil.iter_modules(paths): if not module.ispkg: path = Path(module.module_finder.path) / f"{module.name}.py" yield get_module_by_path(module.name, path) def load(name: str, missing_ok=False) -> None: """ Import *name* module, if *name* is a package import all submodules. If *name* module/package is not found: - raise ModuleNotFoundError if *missing_ok* is False - ignore it if *missing_ok* is True """ try: spec = importlib.util.find_spec(name) except ModuleNotFoundError: if not missing_ok: raise return # import *name* itself, for package it is __init__.py importlib.import_module(name) if spec.loader.is_package(spec.name): package = name for module in pkgutil.iter_modules(spec.submodule_search_locations): importlib.import_module(f"{package}.{module.name}") def load_packages(packages: tuple, missing_ok=False) -> None: for package in packages: load(package, missing_ok=missing_ok) def get(*, module, name, default): """ Return object with *name* from specific *module*. If object was not found return *default* """ try: m = importlib.import_module(module) except ModuleNotFoundError: return default return getattr(m, name, default) def exists(name): try: spec = importlib.util.find_spec(name) except ModuleNotFoundError: return False return spec is not None