diff --git a/confuse/core.py b/confuse/core.py index e8d5e8b..0c794f0 100644 --- a/confuse/core.py +++ b/confuse/core.py @@ -156,7 +156,7 @@ def __setitem__(self, key: ConfigKey, value: Any) -> None: """Create an overlay source to assign a given key under this view. """ - self.set({key: value}) + self[key].set(value) def __contains__(self, key: ConfigKey) -> bool: return self[key].exists() @@ -486,6 +486,20 @@ def resolve(self) -> Iterator[tuple[dict[str, Any] | list[Any], ConfigSource]]: yield value, source def set(self, value: Any) -> None: + if isinstance(self.key, int): + # The parent is (or may be) a list. Overriding it with a + # dict of {index: value} would replace the whole sequence + # with an integer-keyed mapping, so rebuild the list + # instead, with only this index replaced. + for collection, _ in self.parent.resolve(): + if isinstance(collection, list) and -len(collection) <= self.key < len( + collection + ): + new_collection = list(collection) + new_collection[self.key] = value + self.parent.set(new_collection) + return + break self.parent.set({self.key: value}) def add(self, value: Any) -> None: diff --git a/docs/changelog.rst b/docs/changelog.rst index 47f3c4b..1bb30f4 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -4,6 +4,10 @@ Changelog Unreleased ---------- +- Fix setting a value at a list index, which replaced the containing list with + an integer-keyed mapping. + [#169](https://github.com/beetbox/confuse/issues/169) + v2.2.1 ------ diff --git a/test/test_views.py b/test/test_views.py index 2ead690..1a9f98e 100644 --- a/test/test_views.py +++ b/test/test_views.py @@ -276,3 +276,13 @@ def test_override_list_index(self): config = _root({"foo": ["a", "b", "c"]}) config["foo"][1] = "bar" assert config["foo"][1].get() == "bar" + + def test_override_list_index_keeps_list(self): + config = _root({"foo": ["a", "b", "c"]}) + config["foo"][1] = "bar" + assert config["foo"].get() == ["a", "bar", "c"] + + def test_override_nested_list_index_keeps_list(self): + config = _root({"foo": [{"name": "a"}, {"name": "b"}]}) + config["foo"][0]["name"] = "bar" + assert config["foo"].get() == [{"name": "bar"}, {"name": "b"}]