Created
November 16, 2019 23:21
-
-
Save juftin/82f8680a6bb55cbbc41b31f3a7b0d1e2 to your computer and use it in GitHub Desktop.
A safe way to read yml files with environmental variable integration
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
from os import environ | |
from re import compile | |
from yaml import SafeLoader, load | |
def yml(path=None, data=None, tag=None): | |
""" | |
Load a yaml configuration file (path) or data object(data) | |
and resolve any environment variables. The environment | |
variables must be in this format to be parsed: ${VAR_NAME}. | |
E.g.: | |
database: | |
host: ${HOST} | |
port: ${PORT} | |
${KEY}: ${VALUE} | |
app: | |
log_path: '/var/${LOG_PATH}' | |
something_else: '${AWESOME_ENV_VAR}/var/${A_SECOND_AWESOME_VAR}' | |
:param str path: the path to the yaml file | |
:param str data: the yaml data itself as a stream | |
:param str tag: the tag to look for | |
:return: the dict configuration | |
:rtype: dict[str, T] | |
""" | |
pattern = compile('.*?\${(\w+)}.*?') | |
loader = SafeLoader | |
loader.add_implicit_resolver(tag=tag, regexp=pattern, first=None) | |
def env_var_constructor(loader, node): | |
""" | |
Extracts the environment variable from the node's value | |
:param yaml.Loader loader: the yaml loader | |
:param node: the current node in the yaml | |
:return: the parsed string that contains the value of the environment | |
variable | |
""" | |
value = loader.construct_scalar(node=node) | |
match = pattern.findall(string=value) | |
if match: | |
full_value = value | |
for g in match: | |
full_value = full_value.replace( | |
f'${{{g}}}', environ.get(key=g, default=g)) | |
return full_value | |
return value | |
loader.add_constructor(tag=tag, constructor=env_var_constructor) | |
if path: | |
with open(path) as conf_data: | |
return load(stream=conf_data, Loader=loader) | |
elif data: | |
return load(stream=data, Loader=loader) | |
else: | |
raise ValueError('Either a path or data should be defined as input') | |
yaml = yml("testing/yml.yml") | |
print(yaml) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment