From 3a3fb64db49c54772f0def13ed545278b6f59c40 Mon Sep 17 00:00:00 2001 From: Jesper Schlegel Date: Fri, 3 Jan 2025 15:21:39 +0100 Subject: [PATCH 1/3] fix: create BoxLists if default_box is set and a dotted key with non existent index is provided --- AUTHORS.rst | 1 + box/box.py | 11 ++++++++--- box/box_list.py | 10 +++++++++- test/test_box_list.py | 15 +++++++++++++++ 4 files changed, 33 insertions(+), 4 deletions(-) diff --git a/AUTHORS.rst b/AUTHORS.rst index c0b068d..9072429 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -34,6 +34,7 @@ Code contributions: - Muspi Merol (CNSeniorious000) - YISH (mokeyish) - Bit0r +- Jesper Schlegel (jesperschlegel) Suggestions and bug reporting: diff --git a/box/box.py b/box/box.py index 6729616..56f4a24 100644 --- a/box/box.py +++ b/box/box.py @@ -657,9 +657,14 @@ def __setitem__(self, key, value): if hasattr(self[first_item], "__setitem__"): return self[first_item].__setitem__(children, value) elif self._box_config["default_box"]: - super().__setitem__( - first_item, self._box_config["box_class"](**self.__box_config(extra_namespace=first_item)) - ) + if children[0] == "[": + super().__setitem__( + first_item, box.BoxList(**self.__box_config(extra_namespace=first_item)) + ) + else: + super().__setitem__( + first_item, self._box_config["box_class"](**self.__box_config(extra_namespace=first_item)) + ) return self[first_item].__setitem__(children, value) else: raise BoxKeyError(f"'{self.__class__}' object has no attribute {first_item}") diff --git a/box/box_list.py b/box/box_list.py index 3535f22..f6aa808 100644 --- a/box/box_list.py +++ b/box/box_list.py @@ -94,9 +94,17 @@ def __setitem__(self, key, value): if self.box_options.get("box_dots") and isinstance(key, str) and key.startswith("["): list_pos = _list_pos_re.search(key) pos = int(list_pos.groups()[0]) + if pos >= len(self): + self.extend([None] * (pos - len(self) + 1)) if len(list_pos.group()) == len(key): return super().__setitem__(pos, value) - return super().__getitem__(pos).__setitem__(key[len(list_pos.group()) :].lstrip("."), value) + children = key[len(list_pos.group()):].lstrip(".") + if self.box_options.get("default_box"): + if children[0] == "[": + super().__setitem__(pos, box.BoxList(**self.box_options)) + else: + super().__setitem__(pos, self.box_options.get("box_class")(**self.box_options)) + return super().__getitem__(pos).__setitem__(children, value) super().__setitem__(key, value) def _is_intact_type(self, obj): diff --git a/test/test_box_list.py b/test/test_box_list.py index e22c3a5..f96f6cf 100644 --- a/test/test_box_list.py +++ b/test/test_box_list.py @@ -204,6 +204,21 @@ def test_box_list_dots(self): for key in keys: db[key] + def test_box_list_default_dots(self): + box_1 = Box(default_box=True, box_dots=True) + box_1["a[0]"] = 42 + assert box_1.a[0] == 42 + + box_1["b[0].c[0].d"] = 42 + assert box_1.b[0].c[0].d == 42 + + box_1["c[0][0][0]"] = 42 + assert box_1.c[0][0][0] == 42 + + box_2 = Box(default_box=True, box_dots=True) + box_2["a[4]"] = 42 + assert box_2.a.to_list() == [None, None, None, None, 42] + def test_box_config_propagate(self): structure = Box(a=[Box(default_box=False)], default_box=True, box_inherent_settings=True) assert structure._box_config["default_box"] is True From a492e8cabe924d767a3874e37e1a8918b68699b1 Mon Sep 17 00:00:00 2001 From: Jesper Schlegel Date: Fri, 3 Jan 2025 15:26:39 +0100 Subject: [PATCH 2/3] fix: inflate BoxList only if default_box is set --- box/box_list.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/box/box_list.py b/box/box_list.py index f6aa808..fb00864 100644 --- a/box/box_list.py +++ b/box/box_list.py @@ -94,7 +94,7 @@ def __setitem__(self, key, value): if self.box_options.get("box_dots") and isinstance(key, str) and key.startswith("["): list_pos = _list_pos_re.search(key) pos = int(list_pos.groups()[0]) - if pos >= len(self): + if pos >= len(self) and self.box_options.get("default_box"): self.extend([None] * (pos - len(self) + 1)) if len(list_pos.group()) == len(key): return super().__setitem__(pos, value) From 2a29a286c059c743f074c105f6d1768f574e4d0c Mon Sep 17 00:00:00 2001 From: Jesper Schlegel Date: Mon, 6 Jan 2025 11:45:13 +0100 Subject: [PATCH 3/3] fix: if a key already exists should not be a criteria when parsing dot notation strings --- box/box.py | 3 +-- test/test_box_list.py | 4 ++++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/box/box.py b/box/box.py index 56f4a24..937ba0e 100644 --- a/box/box.py +++ b/box/box.py @@ -101,8 +101,7 @@ def _parse_box_dots(bx, item, setting=False): if char == "[": return item[:idx], item[idx:] elif char == ".": - if item[:idx] in bx: - return item[:idx], item[idx + 1 :] + return item[:idx], item[idx + 1 :] if setting and "." in item: return item.split(".", 1) raise BoxError("Could not split box dots properly") diff --git a/test/test_box_list.py b/test/test_box_list.py index f96f6cf..99c65d7 100644 --- a/test/test_box_list.py +++ b/test/test_box_list.py @@ -219,6 +219,10 @@ def test_box_list_default_dots(self): box_2["a[4]"] = 42 assert box_2.a.to_list() == [None, None, None, None, 42] + box_3 = Box(default_box=True, box_dots=True) + box_3["a.b[0]"] = 42 + assert box_3.a.b[0] == 42 + def test_box_config_propagate(self): structure = Box(a=[Box(default_box=False)], default_box=True, box_inherent_settings=True) assert structure._box_config["default_box"] is True