Search code examples
pythonpython-click

Creating a Click.Option with prompt that shows only if default value is empty


Given the following option:

@cli.command()
@click.option('--param', default=lambda: get_value(), prompt="Enter Param")

Normal behavior is for click to show a prompt to enter a value for param and display the default value (and you can just ENTER through it to preserve that).

Instead I'd like the param prompt to only show if get_value() returns None or some pre-defined "show" value, but for any Truthy/Other value for get_value() click will not show the prompt for this option and run the command or move to the next prompt.


Solution

  • This can be done by over riding the click.Option.get_default() and the click.Option.prompt_for_value() methods like:

    Custom Class:

    import click
    
    class OptionPromptNull(click.Option):
        _value_key = '_default_val'
    
        def get_default(self, ctx):
            if not hasattr(self, self._value_key):
                default = super(OptionPromptNull, self).get_default(ctx)
                setattr(self, self._value_key, default)
            return getattr(self, self._value_key)
    
        def prompt_for_value(self, ctx):        
            default = self.get_default(ctx)
    
            # only prompt if the default value is None
            if default is None:
                return super(OptionPromptNull, self).prompt_for_value(ctx)
    
            return default
    

    Using Custom Class:

    Then to use the custom class, pass it as the cls argument to the option decorator like:

    @click.command()
    @click.option('--param', cls=OptionPromptNull,
                  default=lambda: get_value(), prompt="Enter Param")
    def cli(param):
        click.echo("param: '{}'".format(param))
    

    Test Code:

    @click.command()
    @click.option('--param1', cls=OptionPromptNull,
                  default=lambda: get_value_none(), prompt="Enter Param1")
    @click.option('--param2', cls=OptionPromptNull,
                  default=lambda: get_value_one(), prompt="Enter Param2")
    def cli(param1, param2):
        click.echo("param1: '{}'".format(param1))
        click.echo("param2: '{}'".format(param2))
    
    
    def get_value_none():
        return None
    
    def get_value_one():
        return 1
    
    cli([])
    

    Result:

    Enter Param1: 23
    param1: '23'
    param2: '1'