Last active
May 14, 2024 16:33
-
-
Save mkaranasou/ba83e25c835a8f7629e34dd7ede01931 to your computer and use it in GitHub Desktop.
Python Load a yaml configuration file and resolve any environment variables
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
import os | |
import re | |
import yaml | |
def parse_config(path=None, data=None, tag='!ENV'): | |
""" | |
Load a yaml configuration file and resolve any environment variables | |
The environment variables must have !ENV before them and be in this format | |
to be parsed: ${VAR_NAME}. | |
E.g.: | |
database: | |
host: !ENV ${HOST} | |
port: !ENV ${PORT} | |
app: | |
log_path: !ENV '/var/${LOG_PATH}' | |
something_else: !ENV '${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 for global vars: look for ${word} | |
pattern = re.compile('.*?\${(\w+)}.*?') | |
loader = yaml.SafeLoader | |
# the tag will be used to mark where to start searching for the pattern | |
# e.g. somekey: !ENV somestring${MYENVVAR}blah blah blah | |
loader.add_implicit_resolver(tag, pattern, None) | |
def constructor_env_variables(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) | |
match = pattern.findall(value) # to find all env variables in line | |
if match: | |
full_value = value | |
for g in match: | |
full_value = full_value.replace( | |
f'${{{g}}}', os.environ.get(g, g) | |
) | |
return full_value | |
return value | |
loader.add_constructor(tag, constructor_env_variables) | |
if path: | |
with open(path) as conf_data: | |
return yaml.load(conf_data, Loader=loader) | |
elif data: | |
return yaml.load(data, Loader=loader) | |
else: | |
raise ValueError('Either a path or data should be defined as input') |
Thanks! Fixed it
Thanks for sharing this code. It works perfectly for my scenario also where the value is fetched from config file. Btw, can I use this code in my project?
Thanks for sharing this code. It works perfectly for my scenario also where the value is fetched from config file. Btw, can I use this code in my project?
@nandhinik glad to know you found this helpful! Of course you can use it. Let me know if I can be of any help.
An improved version of this is in the very small pyaml-env
library : https://pypi.org/project/pyaml-env/
(mostly for convenience)
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This code has an error because not import
re
...before the 1st line will fix it.