1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
|
"""General utility functions."""
import sys
from importlib import import_module
from importlib.util import find_spec
from pathlib import Path
from typing import TYPE_CHECKING, Any, Optional
if TYPE_CHECKING:
from types import ModuleType
__all__ = (
"import_string",
"module_to_os_path",
)
def module_to_os_path(dotted_path: str = "app") -> Path:
"""Find Module to OS Path.
Return a path to the base directory of the project or the module
specified by `dotted_path`.
Args:
dotted_path: The path to the module. Defaults to "app".
Raises:
TypeError: The module could not be found.
Returns:
Path: The path to the module.
"""
try:
if (src := find_spec(dotted_path)) is None: # pragma: no cover
msg = f"Couldn't find the path for {dotted_path}"
raise TypeError(msg)
except ModuleNotFoundError as e:
msg = f"Couldn't find the path for {dotted_path}"
raise TypeError(msg) from e
path = Path(str(src.origin))
return path.parent if path.is_file() else path
def import_string(dotted_path: str) -> Any:
"""Dotted Path Import.
Import a dotted module path and return the attribute/class designated by the
last name in the path. Raise ImportError if the import failed.
Args:
dotted_path: The path of the module to import.
Raises:
ImportError: Could not import the module.
Returns:
object: The imported object.
"""
def _is_loaded(module: "Optional[ModuleType]") -> bool:
spec = getattr(module, "__spec__", None)
initializing = getattr(spec, "_initializing", False)
return bool(module and spec and not initializing)
def _cached_import(module_path: str, class_name: str) -> Any:
"""Import and cache a class from a module.
Args:
module_path: dotted path to module.
class_name: Class or function name.
Returns:
object: The imported class or function
"""
# Check whether module is loaded and fully initialized.
module = sys.modules.get(module_path)
if not _is_loaded(module):
module = import_module(module_path)
return getattr(module, class_name)
try:
module_path, class_name = dotted_path.rsplit(".", 1)
except ValueError as e:
msg = "%s doesn't look like a module path"
raise ImportError(msg, dotted_path) from e
try:
return _cached_import(module_path, class_name)
except AttributeError as e:
msg = "Module '%s' does not define a '%s' attribute/class"
raise ImportError(msg, module_path, class_name) from e
|