diff --git a/AUTHORS.rst b/AUTHORS.rst index 6b44851..74c01ce 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -37,6 +37,7 @@ Code contributions: - Jesper Schlegel (jesperschlegel) - J vanBemmel (jbemmel) - m-janicki +- AryanRG (AryanRG) Suggestions and bug reporting: diff --git a/CHANGES.rst b/CHANGES.rst index c4a347d..4b04e9e 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,6 +1,11 @@ Changelog ========= +Unreleased +---------- + +* Fix ``Box.update`` rejecting a positional mapping or iterable when keyword updates are also supplied. + Version 7.4.1 ------------- diff --git a/box/box.py b/box/box.py index 8252643..7e260b4 100644 --- a/box/box.py +++ b/box/box.py @@ -820,8 +820,8 @@ def to_dict(self) -> dict: def update(self, *args, **kwargs): if self._box_config["frozen_box"]: raise BoxError("Box is frozen") - if (len(args) + int(bool(kwargs))) > 1: - raise BoxTypeError(f"update expected at most 1 argument, got {len(args) + int(bool(kwargs))}") + if len(args) > 1: + raise BoxTypeError(f"update expected at most 1 argument, got {len(args)}") single_arg = next(iter(args), None) if single_arg: if hasattr(single_arg, "keys"): diff --git a/test/test_box.py b/test/test_box.py index e5c56e0..defbfef 100644 --- a/test/test_box.py +++ b/test/test_box.py @@ -268,6 +268,47 @@ def test_update(self): assert a.key1.gg == 4 assert "new" not in a.key1.keys() + @pytest.mark.parametrize("source_type", [dict, list, iter]) + def test_update_source_and_keywords(self, source_type): + pairs = [("mode", "source"), ("service", {"timeout": 10})] + overrides = {"mode": "override", "workers": [{"id": 1}]} + expected = {"keep": True} + expected.update(source_type(pairs), **overrides) + bx = Box(keep=True) + + assert bx.update(source_type(pairs), **overrides) is None + + assert bx.to_dict() == expected + assert isinstance(bx.service, Box) + assert bx.service.timeout == 10 + assert isinstance(bx.workers, BoxList) + assert isinstance(bx.workers[0], Box) + assert bx.workers[0].id == 1 + + def test_update_empty_source_and_keywords(self): + bx = Box(keep=True) + + assert bx.update({}, mode="override") is None + + assert bx.to_dict() == {"keep": True, "mode": "override"} + + @pytest.mark.parametrize("kwargs", [{}, {"mode": "override"}]) + def test_update_rejects_multiple_sources(self, kwargs): + bx = Box(keep=True) + + with pytest.raises(TypeError, match="update expected at most 1 argument"): + bx.update({"first": 1}, {"second": 2}, **kwargs) + + assert bx.to_dict() == {"keep": True} + + def test_update_source_and_keywords_frozen(self): + bx = Box(keep=True, frozen_box=True) + + with pytest.raises(BoxError, match="Box is frozen"): + bx.update({"first": 1}, mode="override") + + assert bx.to_dict() == {"keep": True} + def test_merge_update(self): a = Box(test_dict) a.grand = 1000