I am using python click options that are shared by multiple commands, as described at https://stackoverflow.com/a/77732441.
Is there a simple way to customize the help= text of the list option in the example below such for cmd1 it will be "List the apples in the basket" and for cmd2 it will be "List the oranges in the container".
My goal is to avoid having a complete --list option definition for each possible help text.
EDIT: I am looking for a solution where per-command help text is specified near the @list_option of that command, without modifying any shared definition. This is because in my application, each of the commands and shared option definitions are in their own .py files.
import sys
import click
# Shared option definition.
list_option = click.option(
"-l",
"--list",
is_flag=True,
help="List items."
)
@click.group()
@click.pass_context
def cli(ctx):
pass
@cli.command()
@click.pass_context
@list_option
def cmd1(ctx, **kwargs):
print(f'Running cmd1: {kwargs = }')
@cli.command()
@click.pass_context
@list_option
def cmd2(ctx, **kwargs):
print(f'Running cmd2: {kwargs = }')
if __name__ == '__main__':
sys.exit(cli())
Explanation for added things:
Use a CustomHelpOption Class
which overrides get_help_record
method to change dynamically the help-text based on the command running.
get_help_record
method checks the name of the current command (ctx.command.name
) and updates the self.help
attribute accordingly before calling the parent class' get_help_record
method.
list_option
is defined with class CustomHelpOption
using cls
parameter to have dynamic help text based on the command.
import sys
import click
# Custom Option class to dynamically set the help text
class CustomHelpOption(click.Option):
def get_help_record(self, ctx):
if ctx.command.name == 'cmd1':
self.help = 'List the apples in the basket'
elif ctx.command.name == 'cmd2':
self.help = 'List the oranges in the container'
return super().get_help_record(ctx)
# Shared option definition with the custom class
list_option = click.option(
"-l",
"--list",
is_flag=True,
cls=CustomHelpOption, # Use the custom class
help="List items.", # This is a placeholder help text
)
@click.group()
@click.pass_context
def cli(ctx):
pass
@cli.command()
@click.pass_context
@list_option
def cmd1(ctx, **kwargs):
print(f'Running cmd1: {kwargs = }')
@cli.command()
@click.pass_context
@list_option
def cmd2(ctx, **kwargs):
print(f'Running cmd2: {kwargs = }')
if __name__ == '__main__':
sys.exit(cli())
Output :
For cmd1
, the help text for --list
will be 'List the apples in the basket'. For cmd2
, the help text for --list
will be 'List the oranges in the container'.
Hope I have understood your question correctly.
Edit after reading the OP's comment:
import sys
import click
# Shared option definition with generic help text
def list_option(func):
@click.option(
'-l',
'--list',
is_flag=True,
help='List items.' # Placeholder help text
)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
# Custom decorator to set specific help text
def custom_list_option(help_text):
def decorator(func):
# Create a new function with the specific help text
@list_option
@click.option(
'-l',
'--list',
is_flag=True,
help=help_text # Custom help text
)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
return decorator
@click.group()
@click.pass_context
def cli(ctx): pass
@cli.command()
@click.pass_context
@custom_list_option('List the apples in the basket')
def cmd1(ctx, **kwargs): print(f'Running cmd1: {kwargs = }')
@cli.command()
@click.pass_context
@custom_list_option('List the oranges in the container')
def cmd2(ctx, **kwargs): print(f'Running cmd2: {kwargs = }')
if __name__ == '__main__': sys.exit(cli())