tr11 / python-configuration

A Python library to load configuration parameters

Home Page:https://tr11.github.io/python-configuration/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Bug: update() broken for mapping-like objects

dargueta opened this issue · comments

To reproduce:

import collections

conf = Configuration({})
data = collections.ChainMap({"abc": {"def": "ghi"}})
conf.update(data)

print(conf["abc.def"])

If data is a plain dict or a Configuration object, conf["abc.def"] will give "ghi". However, if it's a mapping-like object, it crashes:

In [60]: c['foo.abc']
Traceback (most recent call last):
  File "/Users/diegoargueta/.pyenv/versions/aircraft/lib/python3.7/site-packages/IPython/core/interactiveshell.py", line 3524, in run_code
    exec(code_obj, self.user_global_ns, self.user_ns)
  File "<ipython-input-60-e7c3c391a86e>", line 1, in <module>
    c['abc.def']
  File "/Users/diegoargueta/.pyenv/versions/3.7.8/envs/aircraft/lib/python3.7/site-packages/config/configuration.py", line 155, in __getitem__
    raise KeyError(item)
KeyError: 'abc.def'

Your problem is in _flatten_dict(); you need to change

nested = {k for k, v in d.items() if isinstance(v, (dict, Configuration))}

to

nested = {k for k, v in d.items() if isinstance(v, Mapping)}

I don't have time to test it but I believe you'll run into this problem with as_attrdict() which also compares against dict. Use Mapping or MutableMapping as needed instead, and this will work as expected.

This KeyError behavior is reproduced when unittest.mock.patch.dict is used to modify a ConfigurationSet object as part of unit testing. Subsequent unit tests will find that the ConfigurationSet object has been corrupted as patch.dict() is unable to restore the original dictionary after the test that performed the patching.

Example

The following decorator-style use of @patch.dict will corrupt the CONFIG object:
@patch.dict('app.rest.permissions.CONFIG', {'config_key': 'config_value'})

A workaround is to use @patch instead like this:
@patch('app.rest.permissions.CONFIG', Configuration({'config_key': 'config_value'}))

@alexholtz, do you have an example? Does the suggestion from @dargueta fix it for you?