diff --git a/README.md b/README.md index 0917499..e1f580a 100644 --- a/README.md +++ b/README.md @@ -154,9 +154,26 @@ test1: ``` will raise a `ValueError` because `data1: !TEST ${ENV_TAG2}` there is no default value for `ENV_TAG2` in this line. ---- +--- +#### Raise an exception on missing environment variable without default + +Often, a proper default value for an environment variable doesn't exist and +an exception should be thrown when the environment variable is not set. + +If you want config parsing to fail if the environment variable is missing +and no *default value* is specified, you can set the `raise_if_missing` +flag to `True`. + +For example: +```yaml +test1: + data1: !TEST ${ENV_TAG2} +``` +will raise a `ValueError` when `ENV_TAG2` is missing because no default is set +explicitly. +--- #### Using a different loader: The default yaml loader is `yaml.SafeLoader`. If you need to work with serialized Python objects, diff --git a/src/pyaml_env/parse_config.py b/src/pyaml_env/parse_config.py index e3706cd..1fd4877 100644 --- a/src/pyaml_env/parse_config.py +++ b/src/pyaml_env/parse_config.py @@ -11,7 +11,8 @@ def parse_config( default_value='N/A', raise_if_na=False, loader=yaml.SafeLoader, - encoding='utf-8' + encoding='utf-8', + raise_if_missing=False ): """ Load yaml configuration from path or from the contents of a file (data) @@ -39,6 +40,8 @@ def parse_config( yaml.SafeLoader :param str encoding: the encoding of the data if a path is specified, defaults to utf-8 + :param bool raise_if_missing: raise a ValueError when an env variable + is not set and no default value is given :return: the dict configuration :rtype: dict[str, T] """ @@ -86,10 +89,15 @@ def constructor_env_variables(loader, node): _, curr_default_value = each.split(default_sep, 1) found = True break - if not found and raise_if_na: - raise ValueError( - f'Could not find default value for {env_var_name}' - ) + if not found: + if raise_if_na: + raise ValueError(f'Could not find default value ' + f'for {env_var_name}') + if raise_if_missing and env_var_name not in os.environ: + raise ValueError(f'Could not find env variable ' + f'{env_var_name} and no default ' + f'is set') + full_value = full_value.replace( f'${{{env_var_name_with_default}}}', os.environ.get(env_var_name, curr_default_value) diff --git a/tests/pyaml_env_tests/test_parse_config.py b/tests/pyaml_env_tests/test_parse_config.py index 725c333..ff3fb4c 100644 --- a/tests/pyaml_env_tests/test_parse_config.py +++ b/tests/pyaml_env_tests/test_parse_config.py @@ -582,6 +582,18 @@ def test_parse_config_default_sep_blank_value_error(self): expected_config ) + def test_parse_config_raise_if_missing_raised(self): + test_data = 'data0: !TAG ${ENV_TAG1}' + self.assertRaises(ValueError, parse_config, data=test_data, tag='!TAG', + raise_if_missing=True) + + def test_parse_config_raise_if_missing_not_raised(self): + default_value = 'default_value' + test_data = f'data0: !TAG ${{ENV_TAG1:{default_value}}}' + config = parse_config(data=test_data, tag='!TAG', + raise_if_missing=True) + self.assertEqual(config['data0'], default_value) + def test_default_loader(self): os.environ[self.env_var1] = 'it works!' os.environ[self.env_var2] = 'this works too!'