diff --git a/pkgs/clan-cli/clan_lib/machines/actions.py b/pkgs/clan-cli/clan_lib/machines/actions.py index d849d5de5..f5760513b 100644 --- a/pkgs/clan-cli/clan_lib/machines/actions.py +++ b/pkgs/clan-cli/clan_lib/machines/actions.py @@ -9,6 +9,7 @@ from clan_lib.nix_models.clan import ( ) from clan_lib.persist.inventory_store import InventoryStore from clan_lib.persist.util import ( + get_value_by_path, is_writeable_key, retrieve_typed_field_names, set_value_by_path, @@ -132,10 +133,8 @@ def get_machine_fields_schema(machine: Machine) -> dict[str, FieldSchema]: # TODO: handle this more generically. I.e via json schema persisted_data = inventory_store._get_persisted() # noqa: SLF001 inventory = inventory_store.read() # - all_tags = inventory.get("machines", {}).get(machine.name, {}).get("tags", []) - persisted_tags = ( - persisted_data.get("machines", {}).get(machine.name, {}).get("tags", []) - ) + all_tags = get_value_by_path(inventory, f"machines.{machine.name}.tags", []) + persisted_tags = get_value_by_path(persisted_data, f"machines.{machine.name}.tags", []) nix_tags = list_difference(all_tags, persisted_tags) return { diff --git a/pkgs/clan-cli/clan_lib/persist/util.py b/pkgs/clan-cli/clan_lib/persist/util.py index 3f748264d..9d90dd4f8 100644 --- a/pkgs/clan-cli/clan_lib/persist/util.py +++ b/pkgs/clan-cli/clan_lib/persist/util.py @@ -441,6 +441,27 @@ def delete_by_path(d: dict[str, Any], path: str) -> Any: type DictLike = dict[str, Any] | Any +def get_value_by_path(d: DictLike, path: str, fallback: Any = None) -> Any: + """ + Get the value at a specific dot-separated path in a nested dictionary. + + If the path does not exist, it returns fallback. + + :param d: The dictionary to get from. + :param path: The dot-separated path to the key (e.g., 'foo.bar'). + """ + keys = path.split(".") + current = d + for key in keys[:-1]: + current = current.setdefault(key, {}) + + if isinstance(current, dict): + return current.get(keys[-1], fallback) + + return fallback + + + def set_value_by_path(d: DictLike, path: str, content: Any) -> None: """ Update the value at a specific dot-separated path in a nested dictionary.