Source code for diplomat.processing.containers

"""
Provides the config container implementation, used for storing configuration parameters.
"""

from collections import UserDict
from typing import TypeVar, Dict, Tuple, Any, Mapping
from diplomat.processing.type_casters import TypeCaster

T = TypeVar("T")
ConfigSpec = Dict[str, Tuple[T, TypeCaster[T], str]]


[docs] class Config(UserDict): """ Represents a configuration of settings. Is a dictionary with support for attribute style access of keys, and also allows for a backing dictionary, which can control what values are allowed to be stored in this configuration. """
[docs] def __init__(self, *args, **kwargs): """ Create a new configuration dictionary: :param args: Only accepts 2 non-keyword arguments, the data, and a backing dictionary. See set_backing for more info. :param kwargs: Optional, additional keys and values to add to the dictionary. """ args = list(args) self._back_dict = None if len(args) > 2: raise TypeError( "Only accepts up to 2 non-keyword arguments, dict and back_dict" ) if len(args) == 2: self._back_dict = args.pop() super().__init__(*args, **kwargs)
[docs] def set_backing(self, back_dict: ConfigSpec): """ Set the backing dictionary for this config object. :param back_dict: A dictionary of strings (key names) to length 3 tuples. The tuples contain the following values in order: - E: The default value for this setting. - TypeCaster[E]: A casting function, to convert or check passed values are of the right type. - str: The description of this setting, not used here... """ self._back_dict = back_dict self.update(self.data)
def __missing__(self, key: str) -> Any: # Return default setting value if it is missing from the dictionary. if self._back_dict is None: raise KeyError(key) else: def_val, conv_meth, desc = self._back_dict[key] return conv_meth(def_val) def extract(self, data: Mapping): if self._back_dict is None: self.update(data) else: for key in self._back_dict: if key in data: self[key] = data[key] def __setitem__(self, key: str, value: Any): # We only allow for settings in the backing dict to be set, and only # if they type cast correctly. if self._back_dict is None: super().__setitem__(key, value) else: def_val, conv_meth, desc = self._back_dict.get(key, (None, None, None)) if conv_meth is None: raise KeyError( f"Attempted to set non-existent setting: '{key}'.\n" f"Supported settings are: {self._back_dict.keys()}" ) super().__setitem__(key, conv_meth(value)) def __getattr__(self, key: str) -> Any: # Allow for access to dictionary values using the '.' operator... if key.startswith("_") or (key == "data"): return super().__getattribute__(key) return self.__getitem__(key) def __setattr__(self, key: str, value: Any): # Allow for setting dictionary values using the '.' operator... if key.startswith("_") or (key == "data"): return super().__setattr__(key, value) return self.__setitem__(key, value)