diff --git a/benedict/dicts/keylist/keylist_util.py b/benedict/dicts/keylist/keylist_util.py index cfa773c1..9d8b9aa1 100644 --- a/benedict/dicts/keylist/keylist_util.py +++ b/benedict/dicts/keylist/keylist_util.py @@ -28,7 +28,11 @@ def _get_item_key_and_value(item: Any, key: Any) -> tuple[Any, Any]: def _get_or_new_item_value(item: Any, key: Any, subkey: Any) -> Any: try: _, value = _get_item_key_and_value(item, key) - if not type_util.is_dict_or_list_or_tuple(value): + # a list can only hold an integer-index subkey; anything else needs a dict + keeps_subkey = type_util.is_dict(value) or ( + type_util.is_list_or_tuple(value) and _get_index(subkey) is not None + ) + if not keeps_subkey: raise TypeError except (IndexError, KeyError, TypeError): value = _new_item_value(subkey) diff --git a/tests/dicts/keypath/test_keypath_dict.py b/tests/dicts/keypath/test_keypath_dict.py index 639cd1eb..9c6a703e 100644 --- a/tests/dicts/keypath/test_keypath_dict.py +++ b/tests/dicts/keypath/test_keypath_dict.py @@ -499,6 +499,23 @@ def test_setitem_override_existing_item(self) -> None: r = {"a": {"b": {"c": {"d": 3}}}} self.assertEqual(b, r) + def test_setitem_string_subkey_overrides_list_value(self) -> None: + # a string subkey cannot index a list, so it overrides the list like a scalar + b = KeypathDict({"a": [1, 2]}) + b["a.x"] = 1 + self.assertEqual(b, {"a": {"x": 1}}) + b = KeypathDict({"a": [1, 2]}) + b["a.x.y"] = 1 + self.assertEqual(b, {"a": {"x": {"y": 1}}}) + # a list-of-scalar element is likewise overridden by a string subkey + b = KeypathDict({"a": [5]}) + b["a[0].x"] = 3 + self.assertEqual(b, {"a": [{"x": 3}]}) + # a valid integer index keeps the list + b = KeypathDict({"a": [1, 2]}) + b["a[0]"] = 9 + self.assertEqual(b, {"a": [9, 2]}) + def test_setitem_with_keys_list(self) -> None: d: dict[str, Any] = { "a": {