From b1e3b5a1709132e9cae42fccd6f60a2143425b4c Mon Sep 17 00:00:00 2001 From: Sebastiaan Huber Date: Fri, 27 Oct 2023 13:44:38 +0200 Subject: [PATCH] CLI: Fix `verdi config set` when setting list option `verdi config set` would except when setting a single value for an option that is of list type, such as `caching.enable_for`. This only started happening after the recent move to `pydantic` for the configuration options. Now the `Option.validate` will correctly raise when trying to set a string value for a list type. The `verdi config set` implementation is updated to check when it is setting a value for an option with a list type, and in that case, the value is wrapped in a list, unless the `--append` or `--remove` flags are specified. --- aiida/cmdline/commands/cmd_config.py | 4 ++++ aiida/manage/configuration/config.py | 2 +- tests/cmdline/commands/test_config.py | 16 ++++++++++------ 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/aiida/cmdline/commands/cmd_config.py b/aiida/cmdline/commands/cmd_config.py index 6ac6ef9dd7..95e0533d7c 100644 --- a/aiida/cmdline/commands/cmd_config.py +++ b/aiida/cmdline/commands/cmd_config.py @@ -119,6 +119,8 @@ def verdi_config_set(ctx, option, value, globally, append, remove): List values are split by whitespace, e.g. "a b" becomes ["a", "b"]. """ + import typing + from aiida.common.exceptions import ConfigurationError from aiida.manage.configuration import Config, Profile @@ -146,6 +148,8 @@ def verdi_config_set(ctx, option, value, globally, append, remove): value = list(set(current + [value])) else: value = [item for item in current if item != value] + elif option.valid_type == typing.List[str]: + value = [value] # Set the specified option try: diff --git a/aiida/manage/configuration/config.py b/aiida/manage/configuration/config.py index dd6811a510..d88f81f30c 100644 --- a/aiida/manage/configuration/config.py +++ b/aiida/manage/configuration/config.py @@ -130,7 +130,7 @@ def validate_caching_identifier_pattern(cls, value: List[str]) -> List[str]: return value -class GlobalOptionsSchema(ProfileOptionsSchema): +class GlobalOptionsSchema(ProfileOptionsSchema, defer_build=True): """Schema for the global options of an AiiDA instance.""" autofill__user__email: Optional[str] = Field( None, description='Default user email to use when creating new profiles.' diff --git a/tests/cmdline/commands/test_config.py b/tests/cmdline/commands/test_config.py index eac3d10e2b..95f2c67e93 100644 --- a/tests/cmdline/commands/test_config.py +++ b/tests/cmdline/commands/test_config.py @@ -32,17 +32,21 @@ def test_config_set_option_no_profile(run_cli_command, empty_config): assert str(config.get_option(option_name, scope=None)) == option_value -def test_config_set_option(run_cli_command, config_with_profile_factory): +@pytest.mark.parametrize('option_name, is_list', ( + ('storage.sandbox', False), + ('caching.enabled_for', True), +)) +def test_config_set_option(run_cli_command, config_with_profile_factory, option_name, is_list): """Test the `verdi config set` command when setting an option.""" config = config_with_profile_factory() - option_name = 'daemon.timeout' - option_values = [str(10), str(20)] - - for option_value in option_values: + for option_value in ['value0', 'value1']: options = ['config', 'set', option_name, option_value] run_cli_command(cmd_verdi.verdi, options, use_subprocess=False) - assert str(config.get_option(option_name, scope=get_profile().name)) == option_value + if is_list: + assert config.get_option(option_name, scope=get_profile().name) == [option_value] + else: + assert str(config.get_option(option_name, scope=get_profile().name)) == option_value def test_config_append_option(run_cli_command, config_with_profile_factory):