Source code for mllaunchpad.config

"""This module contains functionality for reading and validating the
   configuration.
"""

# Stdlib imports
import logging
import os
from typing import AnyStr, Dict, TextIO, Union

# Third-party imports
import yaml  # https://camel.readthedocs.io/en/latest/yamlref.html

# Project imports
from mllaunchpad.yaml_loader import SafeIncludeLoader


logger = logging.getLogger(__name__)

CONFIG_DEFAULT = "./LAUNCHPAD_CFG.yml"
CONFIG_ENV = os.environ.get("LAUNCHPAD_CFG", CONFIG_DEFAULT)
required_config: Dict[str, Dict] = {
    # datasources and datasinks are optional
    "model_store": {"location": {}},
    "model": {
        "name": {},
        "version": {},
        "module": {},
        # custom model keys are optional
    },
    # api: is optional because using mllaunchpad's WSGI/Flask API is optional
    # (e.g. when using mllaunchpad's convenience functions in creating Azure functions).
    # You are free to add more custom settings in the config file. For an example, see
    # https://github.com/schuderer/mllaunchpad-template
}


[docs]def validate_config(config_dict, required, path=""): for item in required: path_start = (path + ":") if path else "" if item not in config_dict: raise ValueError( "Missing key in config file: {}".format(path_start + item) ) validate_config(config_dict[item], required[item], path_start + item)
[docs]def check_semantics(config_dict): if "api" in config_dict and "version" in config_dict["api"]: raise ValueError( "'api:version:' is not allowed in the config, " "only 'model:version:'." )
[docs]def get_validated_config(filename: str = CONFIG_ENV) -> dict: """Read the configuration from file and return it as a dict object. :param filename: Path to configuration file :type filename: optional str, default: environment variable LAUNCHPAD_CFG or file ./LAUNCHPAD_CFG.yml :return: dict with configuration :rtype: dict """ if filename == CONFIG_DEFAULT: logger.warning( "Config filename environment variable LAUNCHPAD_CFG not set, " "using default file: %s", repr(CONFIG_DEFAULT), ) logger.info("Loading configuration file %s...", filename) with open(filename, encoding="utf-8") as f: return get_validated_config_str(f)
[docs]def get_validated_config_str(io: Union[AnyStr, TextIO]) -> dict: """Read the configuration from a string or open file and return it as a dict object. This function exists mainly for making debugging and unit testing your model's code easier. :param io: Configuration as unicode string or b"byte string" or a open text file to read from :type io: str or open text file handle :return: configuration :rtype: dict """ # Normally, one should use safe_load(), but our Loader # is a subclass of yaml.SafeLoader y = yaml.load(io, SafeIncludeLoader) # nosec validate_config(y, required_config) check_semantics(y) logger.debug("Configuration loaded and validated: %s", y) return y