I have a tool with commands: step1, step2 and step3.
I can chain 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 have found a solution that works but it doesn't 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?
An alias lets you create a shortcut name for a command, file name, or any shell text. By using aliases, you save a lot of time when doing tasks you do frequently.
Aliasing multiple commandsYou can combine multiple commands in an alias by separating them with a semicolon, or by using &&, which will run the next command only if the previous command succeeds. This will cd to the top-level git directory, check out the main branch, and run gitpull.
An alias replaces a string that invokes a command in the Linux shell with another user-defined string. Aliases are mostly used to replace long commands, improving efficiency and avoiding potential spelling errors. Aliases can also replace commands with additional options, making them easier to use.
One way to provide some aliases is to intercept the command and directly manipulate the args list. That can be done with a custom class like:
This class overrides the click.Group.__call__() method to allow editing the args list before calling the command processor. In addition it overrides format_epilog to add help documentation for the 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 and args[0] and 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)
By passing the cls parameter, and a dict of aliases to the click.group() decorator, the ExpandAliasesGroup class can do alias expansion.
aliases = dict(all='command1 command2 command3'.split())
@click.group(chain=True, cls=ExpandAliasesGroup, aliases=aliases)
def cli():
....
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 the desired methods.
By overriding the __call__ method we can intercept all command calls. Then if the list of args starts with a known alias, we edit the args list by removing that aliased command and replacing it with the aliases.
By overriding the format_epilog method we can add help documentation for the aliases.
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
-----------
> 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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With