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