diff --git a/bison/__version__.py b/bison/__version__.py index a6c7ca7..4196ea1 100644 --- a/bison/__version__.py +++ b/bison/__version__.py @@ -3,7 +3,7 @@ __title__ = 'bison' __description__ = 'Python application configuration' __url__ = 'https://github.com/edaniszewski/bison' -__version__ = '0.0.2' +__version__ = '0.0.3' __author__ = 'Erick Daniszewski' __author_email__ = 'edaniszewski@gmail.com' __license__ = 'MIT' diff --git a/bison/utils.py b/bison/utils.py index 70db13e..f2065b9 100644 --- a/bison/utils.py +++ b/bison/utils.py @@ -62,8 +62,14 @@ class DotDict(dict): # otherwise, traverse the key components to set the value first, remainder = key.split('.', 1) if first in self: - v = super(DotDict, self).__getitem__(first) - v.__setitem__(remainder, value) + elem = super(DotDict, self).__getitem__(first) + if isinstance(elem, dict): + dd = DotDict(elem) + dd.__setitem__(remainder, value) + elem.update(dd) + else: + k, v = build_dot_value(key, value) + super(DotDict, self).__setitem__(k, v) else: k, v = build_dot_value(key, value) diff --git a/tests/test_bison.py b/tests/test_bison.py index a752a1f..377bff2 100644 --- a/tests/test_bison.py +++ b/tests/test_bison.py @@ -84,6 +84,33 @@ class TestBison: assert len(b._override) == 1 assert b.get(key) == value + def test_set_multiple_nested(self): + """Set overrides for multiple nested values""" + b = bison.Bison() + assert len(b._override) == 0 + assert len(b.config) == 0 + + b.set('foo.bar.a', 'test') + + assert b.config == { + 'foo': { + 'bar': { + 'a': 'test' + } + } + } + + b.set('foo.bar.b', 'test') + + assert b.config == { + 'foo': { + 'bar': { + 'a': 'test', + 'b': 'test' + } + } + } + @pytest.mark.parametrize( 'paths', [ (), diff --git a/tests/test_utils.py b/tests/test_utils.py index 4b4d5ee..fdd3fde 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -202,6 +202,168 @@ class TestDotDict: dd[key] = value assert dd.get(key) == value + def test_set_multiple_nested(self): + """Set multiple nested items""" + dd = utils.DotDict() + assert dd == {} + + dd['foo.bar.a'] = 'test' + assert dd == { + 'foo': { + 'bar': { + 'a': 'test' + } + } + } + + dd['foo.bar.b'] = 'test' + assert dd == { + 'foo': { + 'bar': { + 'a': 'test', + 'b': 'test' + } + } + } + + def test_set_multiple_neste2(self): + """Set multiple nested items""" + dd = utils.DotDict() + assert dd == {} + + dd['foo'] = 'test' + assert dd == { + 'foo': 'test' + } + + dd['bar.baz'] = 'test' + assert dd == { + 'foo': 'test', + 'bar': { + 'baz': 'test' + } + } + + dd['bar.ball'] = 'test' + assert dd == { + 'foo': 'test', + 'bar': { + 'baz': 'test', + 'ball': 'test' + } + } + + dd['bar.bits.a'] = 'test' + assert dd == { + 'foo': 'test', + 'bar': { + 'baz': 'test', + 'ball': 'test', + 'bits': { + 'a': 'test' + } + } + } + + dd['bar.bits.b'] = 'test' + assert dd == { + 'foo': 'test', + 'bar': { + 'baz': 'test', + 'ball': 'test', + 'bits': { + 'a': 'test', + 'b': 'test' + } + } + } + + dd['bar.new.a'] = 'test' + assert dd == { + 'foo': 'test', + 'bar': { + 'baz': 'test', + 'ball': 'test', + 'bits': { + 'a': 'test', + 'b': 'test' + }, + 'new': { + 'a': 'test' + } + } + } + + dd['bar.new.b.c.d'] = 'test' + assert dd == { + 'foo': 'test', + 'bar': { + 'baz': 'test', + 'ball': 'test', + 'bits': { + 'a': 'test', + 'b': 'test' + }, + 'new': { + 'a': 'test', + 'b': { + 'c': { + 'd': 'test' + } + } + } + } + } + + dd['bar.new.b'] = 'test' + assert dd == { + 'foo': 'test', + 'bar': { + 'baz': 'test', + 'ball': 'test', + 'bits': { + 'a': 'test', + 'b': 'test' + }, + 'new': { + 'a': 'test', + 'b': 'test' + } + } + } + + dd['bar'] = 'test' + assert dd == { + 'foo': 'test', + 'bar': 'test' + } + + dd['foo.bar.a'] = 'test' + assert dd == { + 'foo': { + 'bar': { + 'a': 'test' + } + }, + 'bar': 'test' + } + + dd['foo'] = ['a', 'b', 'c'] + assert dd == { + 'foo': ['a', 'b', 'c'], + 'bar': 'test' + } + + dd['foo.bar.b'] = ['a', 'b', 'c'] + assert dd == { + 'foo': { + 'bar': { + 'b': ['a', 'b', 'c'] + } + }, + 'bar': 'test' + } + @pytest.mark.parametrize( 'key,expected', [ ('a', True),