pythonpython-click

Why is the click add_command not working?


Running the following code as python script.py does nothing. I expect at least the click.echo statement to get printed. It seems like the call to first_command doesn't work. Any idea why?

    import click
    
    def multiple_input_option(**option_kwargs):
        def decorator(func):
            def _callback(ctx, param, value):
                ctx.obj['multiple_options'] = value
    
            help_text = 'some options to get from user'
    
            keyword_arguments = {
                'help': help_text,
                'callback': _callback,
                'expose_value': False,
                'multiple': True,
            }
            keyword_arguments.update(**option_kwargs)
    
            return click.option('--multiple-options', '-e', **keyword_arguments)(func)
    
        return decorator
    
    @click.command()
    @multiple_input_option()
    def first_command(ctx):
        click.echo('> hello...first_command')
        command_param_data = ctx.obj.keys()
    
        click.echo(json.dumps(command_param_data, indent=2))
    
    @click.group()
    def hello_world(ctx):
        """
        Your plugin description here
        """
        ctx.ensure_object(dict)
     
    # This is how we add more sub-commands
    hello_world.add_command(first_command)

Solution

  • I came up with the following solution.

    import click
    #(1) import json, other wise json.dump will not work
    import json
    
    def multiple_input_option(**option_kwargs):
        def decorator(func):
            def _callback(ctx, param, value):
              ctx.obj['multiple_options'] = value
    
            help_text = 'some options to get from user'
    
            keyword_arguments = {
                'help': help_text,
                'callback': _callback,
                'expose_value': False,
                'multiple': True,
            }
            keyword_arguments.update(**option_kwargs)
    
            return click.option('--multiple-options', '-e', **keyword_arguments)(func)
    
        return decorator
    
    @click.command()
    @multiple_input_option()
    #(2) pass context here
    @click.pass_context
    def first_command(ctx):
        click.echo('> hello...first_command')
        command_param_data = ctx.obj.keys()   
        click.echo(json.dumps(command_param_data, indent=2))
    
    @click.group()
    #(3) pass context here
    @click.pass_context
    def hello_world(ctx):
        """
        Your plugin description here
        """
        ctx.ensure_object(dict)
    
    # This is how we add more sub-commands
    hello_world.add_command(first_command)
    
    #(4) make a call in the main function
    if __name__ == '__main__':
      hello_world()
    

    The output is as follows: Output calling the script with and without commands

    I had to do the following to make it work:

    (1) Import the json module, so you could use json.dump

    import json
    

    (2)/(3) Since hello_world and first_command expect the context, you have to pass it via

    click.pass_context
    

    According to the Click documentation here,

    [w]henever a Click command is executed, a Context object is created which holds state for this particular invocation. It remembers parsed parameters, what command created it, which resources need to be cleaned up at the end of the function, and so forth. It can also optionally hold an application-defined object.

    A Context is, as stated above, an object of the class "Context" (see the Click API here for more information). The parameter "ctx" you use in hello_world and first_command is such an object. In hello_world you call the function "ensure_object" which is a function of the class "Context". Since you are using the context object, you have to make sure, that it is passed to your command, which is done by

    click.pass_context
    

    (4) At the end I added a main function, to call the command

    if __name__ == '__main__':
        hello_world()
    

    One last word to json.dump. I assume, you are using python 2.x, in that case the call is alright. For python 3.x, the call should look like that:

    click.echo(json.dumps(list(command_param_data), indent=2))