From f4d77b65968ad43466259a69c2b16227c236afec Mon Sep 17 00:00:00 2001 From: Jeffrey Martin Date: Tue, 18 Jun 2024 09:45:53 -0500 Subject: [PATCH] support merged dictionary in `Configurable` Signed-off-by: Jeffrey Martin --- garak/configurable.py | 5 +++++ tests/test_configurable.py | 19 ++++++++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/garak/configurable.py b/garak/configurable.py index 7ad768a76..efb9f5668 100644 --- a/garak/configurable.py +++ b/garak/configurable.py @@ -88,6 +88,8 @@ def _apply_config(self, config): ) ): continue + if isinstance(v, dict): # if value is an existing dictionary merge + v = getattr(self, k) | v setattr(self, k, v) # This will set attribute to the full dictionary value def _apply_missing_instance_defaults(self): @@ -96,6 +98,9 @@ def _apply_missing_instance_defaults(self): for k, v in self.DEFAULT_PARAMS.items(): if not hasattr(self, k): setattr(self, k, v) + elif isinstance(v, dict): + v = v | getattr(self, k) + setattr(self, k, v) def _validate_env_var(self): if hasattr(self, "key_env_var"): diff --git a/tests/test_configurable.py b/tests/test_configurable.py index 4979beb16..7847f65b7 100644 --- a/tests/test_configurable.py +++ b/tests/test_configurable.py @@ -24,7 +24,13 @@ class mockConfigurable(Configurable): # Configurable is coupled to hierarchy of plugin types __module__ = "garak.generators.mock" - DEFAULT_PARAMS = {"class_var": "from_class"} + DEFAULT_PARAMS = { + "class_var": "from_class", + "class_dict_var": { + "dict_a": "dict_val", + "dict_b": "dict_val", + }, + } def __init__( self, @@ -63,6 +69,17 @@ def test_param_provided(generator_sub_config): def test_class_vars_propagate_to_instance(generator_sub_config): m = mockConfigurable(config_root=generator_sub_config) assert m.class_var == m.DEFAULT_PARAMS["class_var"] + assert m.class_dict_var == m.DEFAULT_PARAMS["class_dict_var"] + + +# when a default parameter dictionary is provided merge on the resulting object +def test_class_dict_merge_to_instance(generator_sub_config): + config_dict_var = {"dict_a": "test_val", "dict_c": "test_val"} + generator_sub_config.generators["mock"]["class_dict_var"] = config_dict_var + m = mockConfigurable(config_root=generator_sub_config) + assert m.class_dict_var == m.DEFAULT_PARAMS["class_dict_var"] | config_dict_var + assert m.class_dict_var["dict_a"] == config_dict_var["dict_a"] + assert m.class_dict_var["dict_c"] == config_dict_var["dict_c"] # when a default parameter is provided and not config_root set on the resulting object