Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get params sent to a subcommand of a click.group()

If I have a click.group() with multiple sub-commands, is there a way that I can get the command line arguments passed to those sub-commands within the group itself?

I know you can go from the group downwards via the context, and I know that I can use a callback function that will execute before the command, but I didn't know if there was a better way to do this than use a callback.

An example:

@click.group()
def cli():
    pass

@cli.command()
@click.argument('task')
@click.argument('task_id')
def sync(task, task_id):
    click.echo('Synching: {}'.format(task))

In this example, is there any way to get task or task_id in the group method?

like image 432
Wesley Bowman Avatar asked May 18 '17 15:05

Wesley Bowman


People also ask

How to invoke a group or multi command without a subcommand?

By default, a group or multi command is not invoked unless a subcommand is passed. In fact, not providing a command automatically passes --help by default. This behavior can be changed by passing invoke_without_command=True to a group.

What debug arguments does the basic command group accept?

As you can see from the earlier example, the basic command group accepts a debug argument which is passed to its callback, but not to the sync command itself. The sync command only accepts its own arguments.

How does the basic command group work with the sync command?

As you can see from the earlier example, the basic command group accepts a debug argument which is passed to its callback, but not to the sync command itself. The sync command only accepts its own arguments. This allows tools to act completely independent of each other, but how does one command talk to a nested one?

How many subcommands can an app have in a group?

An app can have up to 25 subcommands within a subcommand group Let's write a settings command that can change 3 fields in our bot. Now that we have our command made, we need to handle the multiple options with this command.


Video Answer


1 Answers

This can be done by over riding the click.Group.invoke() method like:

Custom Class:

class MyGroup(click.Group):
    def invoke(self, ctx):
        ctx.obj = tuple(ctx.args)
        super(MyGroup, self).invoke(ctx)

Using Custom Class:

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

@click.group(cls=MyGroup)
@click.pass_context
def cli(ctx):
    args = ctx.obj
    ....

How does this work?

This works because click is a well designed OO framework. The @click.group() decorator usually instantiates a click.Group object but allows this behavior to be over ridden with the cls parameter. So it is a relatively easy matter to inherit from click.Group in our own class and over ride desired methods.

In this case we over ride click.Group.invoke() and grab the arguments and put them into the ctx.obj field. They are then accessible in the cli() function.

Test Code:

import click

class MyGroup(click.Group):
    def invoke(self, ctx):
        ctx.obj = tuple(ctx.args)
        super(MyGroup, self).invoke(ctx)

@click.group(cls=MyGroup)
@click.pass_context
def cli(ctx):
    args = ctx.obj
    click.echo('cli: {} {}'.format(ctx.invoked_subcommand, ' '.join(args)))

@cli.command()
@click.argument('task')
@click.argument('task_id')
def sync(task, task_id):
    click.echo('Synching: {}'.format(task))

cli('sync task taskid'.split())

Results:

cli: sync task taskid
Synching: task
like image 152
Stephen Rauch Avatar answered Oct 29 '22 15:10

Stephen Rauch