Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Debian packages RPM packages NuGet packages

Repository URL to install this package:

Details    
Size: Mime:
#!/usr/bin/env python3
import os
import yaml
from everett.manager import ConfigOSEnv, ConfigManager, ConfigDictEnv
from configurator import loader
import inspect
from six import with_metaclass

class SingletonArgs(type):
    """
    Singleton that keep single instance for single set of arguments. E.g.:
    assert SingletonArgs('spam') is not SingletonArgs('eggs')
    assert SingletonArgs('spam') is SingletonArgs('spam')
    """
    _instances = {}
    _init = {}

    def __init__(cls, name, bases, dct):
        cls._init[cls] = dct.get('__init__', None)

    def __call__(cls, *args, **kwargs):
        init = cls._init[cls]
        if init is not None:
            key = (cls, frozenset(
                    inspect.getcallargs(init, None, *args, **kwargs).items()))
        else:
            key = cls

        if key not in cls._instances:
            cls._instances[key] = super(SingletonArgs, cls).__call__(*args, **kwargs)
        return cls._instances[key]

class YamlConfigManager(with_metaclass(SingletonArgs, object)):
    """
    YAML configurations manager that can load YAML files with '!include' and '!join' tags
    The YamlConfigManager configuration can be accessed by a dictionary or by everett's ConfigManager object
    """

    def __init__(self, yaml_path):
        """
        :param self: YamlConfig instance
        :param yaml_path: a path to a YAML file
        """
        assert os.path.isfile(yaml_path), \
            'Configuration file path: \'{}\' not found '.format(yaml_path)
        #convert the parsed dict to one dimensional dict
        #where each pair of key and nested dict is replaced recursively
        #with each key in the nested dict prefixed by the parent key and an underscore
        self.pyYAML_dict = loader.load(yaml_path)  
        #get Everett ConfigManager object with a configuration environemt in following order: os evnvironment, everett_config_dict 
        self.everett_manager = self._get_everett_manager()
        self._req_overwrite_pyYaml_dict()

    def _get_everett_manager(self):
        """
        :param self: YamlConfig instance
        """ 
        everett_config_dict = self._generate_everett_config() 
        return ConfigManager(
            # Specify one or more configuration environments in the order they should be checked
            environments=[
                # Look in OS process environment first
               ConfigOSEnv(), ConfigDictEnv(everett_config_dict)
            ],
            # Provide users a link to documentation for when they hit configuration errors
            doc='Check https://example.com/configuration for docs.'
        )

    def _add_namespace_prefix(self, namespace, key):
        """
        :param self: YamlConfig instance
        :param namespace: the parent's key of nested dictionary of the given key. i.e: {<namespace>: {<key>: some_value}}
        :param key: a key in the given namespace 
        :return: concatenetion of the given namespace and key seperated by an underscore. i.e: '<namespace>_<key>'
        """
        assert isinstance(namespace, str) or namespace == None, \
            'parameter \'namespace\' should be of type string, not {}'.format(type(namespace))
        namespace_prefixed_key = '_'.join([namespace, key]) if namespace else key
        return namespace_prefixed_key

    def _req_gen_namespace_dict(self, everett_config_dict, pyYaml_dict_object, namespace=None):
        """
        :param self: YamlConfig instance
        :param everett_config_dict: destenation dictionary for the namespace dictionary
        :param pyYaml_dict_object: source dictionary parsed by py-yaml
        :param namespace: a namespace that pyYaml_dict_object belongs to.
            all the keys in the destenation will be prefixed with - '<namspace>_' (optional, default - None)
        This function is a reqursive function that converts standart dictionary
        that possibly represents namespaces as nested dictionaries
        and updates everett_config_dict so it will be 1 dimmensional dictionary
        where each namespace is represented by the prefix - '<namspace>_'
        For example:
            >>> pyYaml_dict_object = {KEY: {KEY: {KEY: value}}, MORE_KEY: more_value}
            >>> everett_config_dict = dict()
            >>> YamlConfigManager_instance._req_namespace_dict(everett_config_dict, pyYaml_dict_object, namespace='ALL')
            >>> everett_config_dict
            {ALL_KEY_KEY_KEY: value, ALL_MORE_KEY: mpre value}
        """
        assert isinstance(everett_config_dict, dict), \
            'parameter \'everett_config_dict\' should be of type dict, not {}'.format(type(everett_config_dict))
        assert isinstance(pyYaml_dict_object, dict), \
            'parameter \'pyYAML_dict\' should be of type dict, not {}'.format(type(pyYaml_dict_object))
        for node, value in pyYaml_dict_object.items():
            namespace_prefixed_key = self._add_namespace_prefix(namespace, node)
            if isinstance(value, dict):                           
                self._req_gen_namespace_dict(everett_config_dict, value, namespace_prefixed_key)
            else:
                everett_config_dict[namespace_prefixed_key] = value

    def _generate_everett_config(self):
        """
        :param self: YamlConfig instance
        This function use reqursive function _req_namespace_dict that converts standart dictionary
        that possibly represents namespaces as nested dictionaries
        :return: one dimmensional dictionary 
        where each namespace is represented by the prefix - '<namspace>_'
        Example on YamlConfigManager._req_update_dict documentation
        """
        #parse the YAML into a dict with '!include' and '!join' constructors
        
        everett_config_dict = dict()
        self._req_gen_namespace_dict(everett_config_dict, self.pyYAML_dict)  
        return everett_config_dict

    def _req_overwrite_pyYaml_dict(self, pyYaml_dict_object=None, namespace=None):
        """
        :param self: YamlConfig instance
        :param pyYaml_dict_object: source dictionary parsed by py-yaml
        :param namespace: a namespace that pyYaml_dict_object belongs to.
            all the keys in the destenation will be prefixed with - '<namspace>_' (optional, default - None)
        The function overwites the self.pyYaml_dict_object values with the final everett manager values
        It recursivly calls for each nested dictionary in self.pyYaml_dict_object,
        and uses each key of a nested dictionary as a namespace in self.everett_manager.
        When a key that its value is not more nested dictionary it evaluates the namespaced key,
        and assign it in self.pyYaml_dict_object.
        """
        #
        if pyYaml_dict_object == None:
            pyYaml_dict_object = self.pyYAML_dict
        for key in pyYaml_dict_object:
            val = pyYaml_dict_object[key]    
            if isinstance(val, dict):
                nested_namespace = self._add_namespace_prefix(namespace, key)
                self._req_overwrite_pyYaml_dict(val, nested_namespace)
            else:
                everett_manager_val = self.everett_manager.with_namespace(namespace)(key)
                try: 
                    everett_manager_val = eval(everett_manager_val)
                except:
                    pass
                pyYaml_dict_object[key] = everett_manager_val