diff --git a/box/box.py b/box/box.py index 8252643..105be19 100644 --- a/box/box.py +++ b/box/box.py @@ -669,7 +669,8 @@ def __setitem__(self, key, value): if hasattr(self[first_item], "__setitem__"): return self[first_item].__setitem__(children, value) elif self._box_config["default_box"]: - if children[0] == "[": + # children may be "" for trailing dots (e.g. "a."); treat as Box + empty key. + if children[:1] == "[": super().__setitem__(first_item, box.BoxList(**self.__box_config(extra_namespace=first_item))) else: super().__setitem__( diff --git a/box/box_list.py b/box/box_list.py index 72609ff..adc4ed4 100644 --- a/box/box_list.py +++ b/box/box_list.py @@ -106,7 +106,8 @@ def __setitem__(self, key, value): return super().__setitem__(pos, value) children = key[len(list_pos.group()) :].lstrip(".") if self.box_options.get("default_box"): - if children[0] == "[": + # children may be "" after a trailing dot on a list path (e.g. "[0]."). + if children[:1] == "[": super().__setitem__(pos, box.BoxList(**self.box_options)) else: super().__setitem__(pos, self.box_options.get("box_class")(**self.box_options)) diff --git a/test/test_box.py b/test/test_box.py index e5c56e0..685aa74 100644 --- a/test/test_box.py +++ b/test/test_box.py @@ -344,6 +344,17 @@ def test_set_default_box_dots(self): assert t == Box(a=1, b=[1, 2]) assert t.setdefault("c", [{"d": 2}]) == BoxList([{"d": 2}]) + def test_box_dots_trailing_dot_default_box(self): + # Trailing dots leave an empty children segment; must set empty-string key, not IndexError. + b = Box(default_box=True, box_dots=True) + b["a."] = 1 + assert b["a"][""] == 1 + b2 = Box(default_box=True, box_dots=True) + b2["a[0]."] = 2 + assert b2.a[0][""] == 2 + b3 = Box(default_box=True, box_dots=True) + assert isinstance(b3[".."], Box) + def test_from_json_file(self): bx = Box.from_json(filename=data_json_file) assert isinstance(bx, Box)