Repository URL to install this package:
|
Version:
1.1.33 ▾
|
#!/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