Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Setting Nested Value Via Environment Variable Removing All Nested Values #152

Open
FancyGecko opened this issue Nov 1, 2022 · 2 comments

Comments

@FancyGecko
Copy link

Build: 2.0.0

When I use set_env() and only want to change one nested value via environment variables it is removing all the nested values.

config.yaml

parent_value:
  nested_value1: foo
  nested_value2: bar

code

import confuse

config = confuse.Configuration('app', __name__)
config.set_file("config.yaml")
config.set_env()

print(config['parent_value'].get())

Output: Not setting an environment variable

OrderedDict([('nested_value1', 'foo'), ('nested_value2', 'bar')])

Output: Setting environment variable export APP_PARENT_VALUE__NESTED_VALUE1=newValue

{'nested_value1': 'newValue'}

Expected Outcome:

{'nested_value1': 'newValue', 'nested_value2': 'bar'}
@sampsyo
Copy link
Member

sampsyo commented Nov 2, 2022

Hello! Unfortunately, this is a symptom of a deeper issue having to do with the way that overlays and get() work, which are covered in #143 as well.

Here's a script demonstrating the issue without environment variables:

import confuse

# Setup.
s = confuse.ConfigSource({'a': {'b': 'c', 'd': 'e'}})
r = confuse.RootView([s])
print(r['a'].get())

# Overriding seems to affect the entire `a` dict.
r['a']['b'].set('c1')
print(r['a']['b'].get())
print(r['a'].get())

# However, `d` is still there.
print(r['a']['d'].get())

The output is:

{'b': 'c', 'd': 'e'}
c1
{'b': 'c1'}
e

The upshot is that the other keys in the a dict are still there, but you can't see them by doing r['a'].get(). Code that instead iterates over a, as in for view in r['a'], will still see all the keys.

I totally agree that this behavior is not intuitive, especially for environment variable overrides. It would be great to do something better with this, perhaps with an alternative to get with different "merging" behavior. But that's a tricky conceptual problem…

@arroyoj
Copy link
Contributor

arroyoj commented Nov 3, 2022

In addition to iterating over the keys in the view like @sampsyo suggested, you could also use a Template with get() to make sure none of the keys are skipped. I'm not sure if this fits your use case, but for example, you could do the following, building on your sample code:

import confuse

config = confuse.Configuration('app', __name__)
config.set_file("config.yaml")
config.set_env()
parent_template = {'nested_value1': str, 'nested_value2': str}

print(config['parent_value'].get())
print(config['parent_value'].get(parent_template))

When a dict is passed to get() as above, it is converted to a MappingTemplate. Using the template also gives you validation. In the example above, the template indicates that the values for those keys must be strings.

Without setting environment variables, the output is

OrderedDict([('nested_value1', 'foo'), ('nested_value2', 'bar')])
{'nested_value1': 'foo', 'nested_value2': 'bar'}

With setting the env var (export APP_PARENT_VALUE__NESTED_VALUE1=newValue), the output when using the template matches what you expected:

{'nested_value1': 'newValue'}
{'nested_value1': 'newValue', 'nested_value2': 'bar'}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants