From e65fdba2e7e7377ee3d5489b4a5a142a4a2b94bb Mon Sep 17 00:00:00 2001 From: Zack Cerza Date: Wed, 24 Jun 2026 13:57:23 -0600 Subject: [PATCH 1/2] test_config: Use a fixture Signed-off-by: Zack Cerza --- tests/test_config.py | 40 +++++++++++++++------------------------- 1 file changed, 15 insertions(+), 25 deletions(-) diff --git a/tests/test_config.py b/tests/test_config.py index 8bdcd4ab..d4fc3000 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -1,8 +1,18 @@ +import pytest import tomlkit from ceph_devstack import config, Config +@pytest.fixture(scope="function") +def test_config(tmp_path) -> Config: + test_config = Config() + config_file = tmp_path / "test_config.toml" + config_file.write_text("") + test_config.load(config_file) + return test_config + + class TestConfigDump: def test_config_dump_returns_string(self): result = config.dump() @@ -54,43 +64,23 @@ def test_get_value_returns_string_for_int(self): class TestConfigSet: - def test_set_value_simple_key(self, tmp_path): - test_config = Config() - config_file = tmp_path / "test_config.toml" - config_file.write_text("") - test_config.load(config_file) + def test_set_value_simple_key(self, test_config): test_config.set_value("test_key", "test_value") assert test_config["test_key"] == "test_value" - def test_set_value_nested_key(self, tmp_path): - test_config = Config() - config_file = tmp_path / "test_config.toml" - config_file.write_text("") - test_config.load(config_file) + def test_set_value_nested_key(self, test_config): test_config.set_value("test_section.test_key", "test_value") assert test_config["test_section"]["test_key"] == "test_value" - def test_set_value_updates_user_obj(self, tmp_path): - test_config = Config() - config_file = tmp_path / "test_config.toml" - config_file.write_text("") - test_config.load(config_file) + def test_set_value_updates_user_obj(self, test_config): test_config.set_value("new_key", "new_value") assert "new_key" in test_config.user_obj - def test_set_value_creates_intermediate_sections(self, tmp_path): - test_config = Config() - config_file = tmp_path / "test_config.toml" - config_file.write_text("") - test_config.load(config_file) + def test_set_value_creates_intermediate_sections(self, test_config): test_config.set_value("deep.nested.key", "value") assert test_config.user_obj["deep"]["nested"]["key"] == "value" - def test_set_value_overrides_existing(self, tmp_path): - test_config = Config() - config_file = tmp_path / "test_config.toml" - config_file.write_text("") - test_config.load(config_file) + def test_set_value_overrides_existing(self, test_config): original_count = test_config["containers"]["testnode"]["count"] new_count = original_count + 2 test_config.set_value("containers.testnode.count", str(new_count)) From 71fb2af3a507d385ddb94d09909ff4e7f8605f6d Mon Sep 17 00:00:00 2001 From: Zack Cerza Date: Wed, 24 Jun 2026 13:25:50 -0600 Subject: [PATCH 2/2] Add Config.unset_value --- ceph_devstack/__init__.py | 24 ++++++++++++++++++++++++ ceph_devstack/cli.py | 1 + tests/test_config.py | 9 +++++++++ 3 files changed, 34 insertions(+) diff --git a/ceph_devstack/__init__.py b/ceph_devstack/__init__.py index 33c67cfb..94546468 100644 --- a/ceph_devstack/__init__.py +++ b/ceph_devstack/__init__.py @@ -50,6 +50,8 @@ def parse_args(args: List[str]) -> argparse.Namespace: parser_config_set = subparsers_config.add_parser("set") parser_config_set.add_argument("name") parser_config_set.add_argument("value") + parser_config_unset = subparsers_config.add_parser("unset") + parser_config_unset.add_argument("name") parser_doc = subparsers.add_parser( "doctor", help="Check that the system meets requirements" ) @@ -129,6 +131,8 @@ class Config(dict): __slots__ = ["user_obj", "user_path"] def load(self, config_path: Path | None = None): + args = self.get("args") + self.clear() parsed = tomlkit.parse((Path(__file__).parent / "config.toml").read_text()) self.update(parsed) if config_path: @@ -140,6 +144,8 @@ def load(self, config_path: Path | None = None): raise OSError(f"Config file at {self.user_path} not found!") else: self.user_obj = {} + if args: + self["args"] = args def dump(self): return tomlkit.dumps(self) @@ -182,6 +188,24 @@ def set_value(self, name: str, value: str) -> None: self.user_path.write_text(tomlkit.dumps(self.user_obj).strip()) i += 1 + def unset_value(self, name: str) -> None: + path = name.split(".") + obj = self.user_obj + i = 0 + last_index = len(path) - 1 + while i <= last_index: + if i < last_index: + if path[i] not in obj: + break + obj = obj[path[i]] + elif i == last_index: + obj.pop(path[i]) + self.update(self.user_obj) + self.user_path.parent.mkdir(exist_ok=True) + self.user_path.write_text(tomlkit.dumps(self.user_obj).strip()) + i += 1 + self.load(self.user_path) + config = Config() config.load() diff --git a/ceph_devstack/cli.py b/ceph_devstack/cli.py index 44410977..c0794170 100644 --- a/ceph_devstack/cli.py +++ b/ceph_devstack/cli.py @@ -12,6 +12,7 @@ "dump": lambda config, args: print(config.dump()), "get": lambda config, args: print(config.get_value(args.name)), "set": lambda config, args: print(config.set_value(args.name, args.value)), + "unset": lambda config, args: config.unset_value(args.name), } COMMAND_HANDLERS = { diff --git a/tests/test_config.py b/tests/test_config.py index d4fc3000..3abb9d52 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -88,6 +88,15 @@ def test_set_value_overrides_existing(self, test_config): assert test_config["containers"]["testnode"]["count"] == new_count +class TestConfigUnset: + def test_unset_value_simple_key(self, test_config): + test_config.set_value("test_key", "test_value") + assert "test_key" in test_config + assert test_config["test_key"] == "test_value" + test_config.unset_value("test_key") + assert "test_key" not in test_config + + class TestConfigDefaults: def test_config_defaults(self): assert config == {