From 37837afbc88cc14528ea6f9cb197c54689b448dd Mon Sep 17 00:00:00 2001 From: Sebastiaan Huber Date: Wed, 22 Mar 2023 10:14:25 +0100 Subject: [PATCH] Raise `click.BadParameter` if config contains unknown keys Before, any keys in the configuration file that did not match any parameters of the command would be silently ignored. This could lead to confusion with users if they accidentally mistyped a parameter in the configuration file. Here, the `configuration_callback` method is updated to cross-reference the keys in the configuration file with the names of the command's parameters. If any keys are not recognized a `click.BadParameter` is raised indicating the unrecognized keys. --- click_config_file.py | 13 ++++++++++ tests/test_configuration_option_explicit.py | 28 ++++++++++++++++++--- 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/click_config_file.py b/click_config_file.py index 6236801..aa4287e 100755 --- a/click_config_file.py +++ b/click_config_file.py @@ -90,6 +90,19 @@ def configuration_callback(cmd_name, option_name, config_file_name, except Exception as e: raise click.BadOptionUsage(option_name, "Error reading configuration file: {}".format(e), ctx) + + valid_params = [ + p.name for p in ctx.command.params if p.name != option_name + ] + specified_params = list(config.keys()) + unknown_params = set(specified_params).difference(set(valid_params)) + + if unknown_params: + raise click.BadParameter( + f'Invalid configuration file, the following keys are not ' + f'supported: {unknown_params}', ctx, param + ) + ctx.default_map.update(config) return saved_callback(ctx, param, value) if saved_callback else value diff --git a/tests/test_configuration_option_explicit.py b/tests/test_configuration_option_explicit.py index d453d9b..dd7d27f 100644 --- a/tests/test_configuration_option_explicit.py +++ b/tests/test_configuration_option_explicit.py @@ -31,13 +31,14 @@ def cli(config): def test_custom_default_value(runner, cfgfile): @click.command() + @click.option('--who') @configuration_option(implicit=False, default=str(cfgfile), expose_value=True) - def cli(config): + def cli(who, config): assert config == str(cfgfile) click.echo(config) result = runner.invoke(cli) - assert not result.exception + assert not result.exception, result.output assert result.output == ''.join(( str(cfgfile.realpath()), '\n', @@ -60,9 +61,10 @@ def cli(who): def test_config_value_no_replacement(runner, cfgfile): """Test that config does not replace variable of other name.""" @click.command() + @click.option('--who') @click.option('--whom', default='World', envvar='CLICK_TEST_WHO') @configuration_option(implicit=False) - def cli(whom): + def cli(who, whom): assert whom == "World" click.echo("Hello {}".format(whom)) @@ -73,6 +75,7 @@ def cli(whom): assert not result.exception assert result.output == 'Hello World\n' + def test_broken_config(runner, tmpdir): @click.command() @configuration_option(implicit=False) @@ -92,6 +95,25 @@ def cli(): assert result.exit_code != 0 +def test_config_unknown_parameters(runner, cfgfile): + """Test that the command fails if the configuration file contains unknown parameters.""" + @click.command() + @configuration_option(implicit=False) + def cli(): + pass + + result = runner.invoke(cli, ( + '--config', + str(cfgfile), + )) + assert re.search( + r"Invalid configuration file, the following keys are not supported: {'who'}", + result.output + ) is not None + assert result.exception + assert result.exit_code != 0 + + def test_config_precedence_override_default(runner, cfgfile): @click.command()