Command Chain Alias

I have a tool with the commands: step1 , step2 and step3 .

I can link them by calling:

$ tool step1 step2 step3

I would like to have an alias named all to run all the steps by calling:

$ tool all

I found a solution that works, but this does not seem right for me because of calling cli() twice under the hood:

 @click.group(chain=True) def cli(): print('cli() has been called') ... @cli.command() def all(): cli(args=['step1', 'step2', 'step3']) 

How else could this be done without the side effect of calling cli() twice?

+5
source share
1 answer

One way to provide some aliases is to intercept the command and directly manipulate the args list. This can be done using a special class, for example:

Custom class

This class overrides the click.Group.__call__() method, which allows you to edit the args list before invoking the shell. He also overrides format_epilog to add reference documentation for aliases.

 class ExpandAliasesGroup(click.Group): def __init__(self, *args, **kwargs): self.aliases = kwargs.pop('aliases', {}) super(ExpandAliasesGroup, self).__init__(*args, **kwargs) def __call__(self, *args, **kwargs): if args[0][0] in self.aliases: alias = self.aliases[args[0][0]] args[0].pop(0) for command in reversed(alias): args[0].insert(0, command) return super(ExpandAliasesGroup, self).__call__(*args, **kwargs) @property def alias_help(self): return '\n'.join( '{}: {}'.format(alias, ' '.join(commands)) for alias, commands in sorted(self.aliases.items()) ) def format_epilog(self, ctx, formatter): """Inject our aliases into the help string""" if self.aliases: formatter.write_paragraph() formatter.write_text('Aliases:') with formatter.indentation(): formatter.write_text(self.alias_help) # call the original epilog super(ExpandAliasesGroup, self).format_epilog(ctx, formatter) 

Using a custom class

By click.group() cls parameter and an alias pointer to the click.group() decorator, the click.group() class can perform the alias extension.

 aliases = dict(all='command1 command2 command3'.split()) @click.group(chain=True, cls=ExpandAliasesGroup, aliases=aliases) def cli(): .... 

How it works?

This works because a click is a well-designed OO framework. The @click.group() decorator usually creates an instance of the click.Group object, but allows this behavior to exceed the cls parameter. Thus, it is relatively easy to inherit from click.Group in our own class and overcome the necessary methods.

__call__ method, we can intercept all command calls. Then, if the argument list starts with a known alias, we edit the args list by removing this aliased command and replacing it with aliases.

format_epilog method, we can add reference documentation for aliases.

Security Code:

 import click aliases = dict(all='command1 command2 command3'.split()) @click.group(cls=ExpandAliasesGroup, chain=True, aliases=aliases) def cli(): pass @cli.command() def command1(): click.echo('Command 1') @cli.command() def command2(): click.echo('Command 2') @cli.command() def command3(): click.echo('Command 3') if __name__ == "__main__": commands = ( 'command1', 'command3', 'command1 command2', 'all', '--help', ) for cmd in commands: try: print('-----------') print('> ' + cmd) cli(cmd.split()) except: pass 

Test results:

 ----------- > command1 Command 1 ----------- > command3 Command 3 ----------- > command1 command2 Command 1 Command 2 ----------- > all Command 1 Command 2 Command 3 ----------- > --help Usage: test.py [OPTIONS] COMMAND1 [ARGS]... [COMMAND2 [ARGS]...]... Options: --help Show this message and exit. Commands: command1 Command #1 comes first command2 Command #2 is after command #1 command3 Command #3 saves the best for last Aliases: all: command1 command2 command3 
+7
source

Source: https://habr.com/ru/post/1274335/


All Articles