Module core.utils.module_loader
Utilities to help standardize how the framework dynamically imports code
Expand source code
"""
Utilities to help standardize how the framework dynamically imports code
"""
from dataclasses import dataclass, field
import sys
import importlib
from types import ModuleType
from typing import TextIO, List
import inspect
from core.utils.exceptions import cdev_core_error
###############################
##### Exceptions
###############################
@dataclass
class ImportModuleError(cdev_core_error):
help_message: str = """
This is a helper function used by Cdev. This error should have been wrapped by the high level components,
and they should have provided a more informative help message.
"""
help_resources: List[str] = field(default_factory=lambda: [])
@dataclass
class ImportClassError(ImportModuleError):
help_message: str = """
This is a helper function used by Cdev. This error should have been wrapped by the high level components,
and they should have provided a more informative help message.
"""
help_resources: List[str] = field(default_factory=lambda: [])
###############################
##### API
###############################
def import_module(
module_name: str, denote_output: bool = False, override_stdout: bool = True
) -> ModuleType:
"""Helper function for dynamically loading python modules.
This function should be used whenever a python module needs to be dynamically loaded within the framework.
For more information about python modules see https://docs.python.org/3/tutorial/modules.html. Any changes
to the PYTHONPATH needed to find the module should be done before this function is called.
Args:
module_name (str): The python module to be loaded. Note that this is not a file path. It is the name of a module that can be loaded.
Raises:
ImportModuleError
"""
if denote_output:
print(f"--------- OUTPUT FROM MODULE {module_name} ----------")
# Sometimes the module is already loaded so just reload it to capture any changes
# Importing the initialization file should cause it to modify the state of the Workspace however is needed
if override_stdout:
sys.stdout = override_sys_out
try:
if sys.modules.get(module_name):
rv = importlib.reload(sys.modules.get(module_name))
else:
rv = importlib.import_module(module_name)
except Exception as e:
raise ImportModuleError(error_message=f"Could Not Import {module_name} -> {e}")
finally:
if override_stdout:
sys.stdout = sys.__stdout__
if denote_output:
print(f"---------------------------------------------------")
return rv
def import_class(module_name: str, class_name: str) -> type:
"""Helper function for loading a specific class from a module
Import the given module name and then search through all the objects in the module
for a class with a matching name. Return the `class` if found.
Args:
module_name (str): module name
class_name (str): class name
Returns:
type: the class
Raises:
ImportModuleError
ImportClassError
"""
mod = import_module(module_name)
for item_name in dir(mod):
if not item_name == class_name:
continue
potential_obj = getattr(mod, item_name)
if inspect.isclass(potential_obj):
return potential_obj
raise ImportClassError(
error_message=f"Could not find class '{class_name}' in the loaded module '{module_name}'"
)
class override_sys_out(TextIO):
encoding = sys.__stdout__.encoding
def write(s: str) -> None:
if len(s) > 0 and not s == "\n":
sys.__stdout__.write(f"> {s}\n")
Functions
def import_class(module_name: str, class_name: str) ‑> type
-
Helper function for loading a specific class from a module
Import the given module name and then search through all the objects in the module for a class with a matching name. Return the
class
if found.Args
module_name
:str
- module name
class_name
:str
- class name
Returns
type
- the class
Raises
ImportModuleError ImportClassError
Expand source code
def import_class(module_name: str, class_name: str) -> type: """Helper function for loading a specific class from a module Import the given module name and then search through all the objects in the module for a class with a matching name. Return the `class` if found. Args: module_name (str): module name class_name (str): class name Returns: type: the class Raises: ImportModuleError ImportClassError """ mod = import_module(module_name) for item_name in dir(mod): if not item_name == class_name: continue potential_obj = getattr(mod, item_name) if inspect.isclass(potential_obj): return potential_obj raise ImportClassError( error_message=f"Could not find class '{class_name}' in the loaded module '{module_name}'" )
def import_module(module_name: str, denote_output: bool = False, override_stdout: bool = True) ‑> module
-
Helper function for dynamically loading python modules.
This function should be used whenever a python module needs to be dynamically loaded within the framework.
For more information about python modules see https://docs.python.org/3/tutorial/modules.html. Any changes to the PYTHONPATH needed to find the module should be done before this function is called.
Args
module_name
:str
- The python module to be loaded. Note that this is not a file path. It is the name of a module that can be loaded.
Raises
ImportModuleError
Expand source code
def import_module( module_name: str, denote_output: bool = False, override_stdout: bool = True ) -> ModuleType: """Helper function for dynamically loading python modules. This function should be used whenever a python module needs to be dynamically loaded within the framework. For more information about python modules see https://docs.python.org/3/tutorial/modules.html. Any changes to the PYTHONPATH needed to find the module should be done before this function is called. Args: module_name (str): The python module to be loaded. Note that this is not a file path. It is the name of a module that can be loaded. Raises: ImportModuleError """ if denote_output: print(f"--------- OUTPUT FROM MODULE {module_name} ----------") # Sometimes the module is already loaded so just reload it to capture any changes # Importing the initialization file should cause it to modify the state of the Workspace however is needed if override_stdout: sys.stdout = override_sys_out try: if sys.modules.get(module_name): rv = importlib.reload(sys.modules.get(module_name)) else: rv = importlib.import_module(module_name) except Exception as e: raise ImportModuleError(error_message=f"Could Not Import {module_name} -> {e}") finally: if override_stdout: sys.stdout = sys.__stdout__ if denote_output: print(f"---------------------------------------------------") return rv
Classes
class ImportClassError (error_message: str, help_message: str = '\n This is a helper function used by Cdev. This error should have been wrapped by the high level components,\n and they should have provided a more informative help message.\n ', help_resources: List[str] = <factory>)
-
ImportClassError(error_message: str, help_message: str = '\n This is a helper function used by Cdev. This error should have been wrapped by the high level components,\n and they should have provided a more informative help message.\n ', help_resources: List[str] =
) Expand source code
class ImportClassError(ImportModuleError): help_message: str = """ This is a helper function used by Cdev. This error should have been wrapped by the high level components, and they should have provided a more informative help message. """ help_resources: List[str] = field(default_factory=lambda: [])
Ancestors
- ImportModuleError
- cdev_core_error
- builtins.Exception
- builtins.BaseException
Class variables
var help_message : str
var help_resources : List[str]
class ImportModuleError (error_message: str, help_message: str = '\n This is a helper function used by Cdev. This error should have been wrapped by the high level components,\n and they should have provided a more informative help message.\n ', help_resources: List[str] = <factory>)
-
ImportModuleError(error_message: str, help_message: str = '\n This is a helper function used by Cdev. This error should have been wrapped by the high level components,\n and they should have provided a more informative help message.\n ', help_resources: List[str] =
) Expand source code
class ImportModuleError(cdev_core_error): help_message: str = """ This is a helper function used by Cdev. This error should have been wrapped by the high level components, and they should have provided a more informative help message. """ help_resources: List[str] = field(default_factory=lambda: [])
Ancestors
- cdev_core_error
- builtins.Exception
- builtins.BaseException
Subclasses
Class variables
var help_message : str
var help_resources : List[str]
class override_sys_out (*args, **kwds)
-
Typed version of the return of open() in text mode.
Expand source code
class override_sys_out(TextIO): encoding = sys.__stdout__.encoding def write(s: str) -> None: if len(s) > 0 and not s == "\n": sys.__stdout__.write(f"> {s}\n")
Ancestors
- typing.TextIO
- typing.IO
- typing.Generic
Class variables
var encoding
Methods
def write(s: str) ‑> None
-
Expand source code
def write(s: str) -> None: if len(s) > 0 and not s == "\n": sys.__stdout__.write(f"> {s}\n")