loader.py (56 lines of code) (raw):
""" This module contains functions for loading classes from configuration file
variables. """
import logging
import warnings
from importlib import import_module
from typing import Type, Dict, Any, List
import yaml
LOG: logging.Logger = logging.getLogger(__name__)
def get_class(class_path: str) -> Type:
""" Method for loading a class from a absolute class path string.
Arguments:
class_path (str): The absolute import path to the class. For example:
pkg.module.ClassName
Returns:
The class object referred to in the supplied class path.
Raises:
ModuleNotFoundError: If the module part of the class path could not
be found.
AttributeError: If the module could be found but the specified class
name was not defined within it.
"""
LOG.info("Loading class: %s", class_path)
module_path, class_name = class_path.rsplit(".", 1)
try:
module = import_module(module_path)
except ModuleNotFoundError as mnf_err:
LOG.error("Module %s could not be found", module_path)
raise mnf_err
try:
found_class = module.__getattribute__(class_name)
except AttributeError as att_err:
LOG.error("Class %s is not part of module %s", class_name, module_path)
raise att_err
LOG.info("Successfully loaded class: %s from module: %s", class_name,
module_path)
return found_class
def load_config(file_path: str) -> Dict[str, Any]:
""" Converts the yaml file at the supplied path to a dictionary.
Arguments:
file_path (str): The path to the yaml formatted configuration file.
Returns:
A dictionary formed from the supplied yaml file.
"""
LOG.info("Loading yaml file at: %s", file_path)
with open(file_path, "r") as yaml_file:
yaml_dict: Dict[str, Any] = yaml.load(yaml_file)
return yaml_dict
def get_model_classes(config: Dict[str, Any], dsps_name: str,
model_type: str) -> List[Type]:
""" This method loads model classes from lists in the config dictionary and
checks for name and description properties.
Arguments:
config (dict): The main configuration dictionary containing the model
class paths under "{dsps_name}.{model_type}.models"
key.
dsps_name (str): The name of the streaming system whose models are
to be loaded.
model_type (str): The model type, traffic, topology etc.
Returns:
List[Type]: A list of Model Types.
Raises:
RuntimeError: If a model class does not have a name class property
set or if the name property of one model is the same
as another in the list.
UserWarning: If a model class does not have the description class
property set.
"""
model_classes: List[Type] = []
model_names: List[str] = []
for model in config[f"{dsps_name}.{model_type}.models"]:
model_class: Type = get_class(model)
if model_class.name == "base":
name_msg: str = (f"Model {str(model_class)} does not have a "
f"'name' class property defined. This is required"
f" for it to be correctly identified in the API.")
LOG.error(name_msg)
raise RuntimeError(name_msg)
if model_class.name in model_names:
other_model_index: int = model_names.index(model_class.name)
dup_msg: str = (f"The model {str(model_class)} has the same 'name'"
f" class property as "
f"{str(model_classes[other_model_index])}. The "
f"names of models should be unique.")
LOG.error(dup_msg)
raise RuntimeError(dup_msg)
if model_class.description == "base":
desc_msg: str = (f"Model {str(model_class)} does not have a "
f"'description' class property defined. This is "
f"recommended for use in the API.")
LOG.warning(desc_msg)
warnings.warn(desc_msg)
model_classes.append(model_class)
model_names.append(model_class.name)
return model_classes