问题
In click, I'm defining this command:
@click.command('time', short_help='Timesheet Generator')
@click.argument('time_command', type=click.Choice(['this', 'last']))
@click.argument('data_mode', type=click.Choice(['excel', 'exchange']), default='exchange')
@click.option('--password', prompt=True, hide_input=True, confirmation_prompt=False)
@pass_context
def cli(ctx, time_command, data_mode, password):
The issue I have is that I only want the password to prompt if the data_mode
argument equals exchange
. How can I pull this off?
回答1:
We can remove the need for a prompt if another parameter does not match a specific value by building a custom class derived from click.Option
, and in that class over riding the click.Option.handle_parse_result()
method like:
Custom Class:
import click
def PromptIf(arg_name, arg_value):
class Cls(click.Option):
def __init__(self, *args, **kwargs):
kwargs['prompt'] = kwargs.get('prompt', True)
super(Cls, self).__init__(*args, **kwargs)
def handle_parse_result(self, ctx, opts, args):
assert any(c.name == arg_name for c in ctx.command.params), \
"Param '{}' not found for option '{}'".format(
arg_name, self.name)
if arg_name not in opts:
raise click.UsageError(
"Illegal usage: `%s` is a required parameter with" % (
arg_name))
# remove prompt from
if opts[arg_name] != arg_value:
self.prompt = None
return super(Cls, self).handle_parse_result(ctx, opts, args)
return Cls
Using Custom Class:
To use the custom class, pass the cls
parameter to click.option
decorator like:
@click.option('--an_option', cls=PromptIf('an_argument', 'an_arg_value'))
pass in the name of the parameter to examine for the desired value, and the value to check for.
How does this work?
This works because click is a well designed OO framework. The @click.option()
decorator usually instantiates a click.Option
object but allows this behavior to be overridden with the cls
parameter. So it is a relatively easy matter to inherit from click.Option
in our own class and over ride the desired methods.
In this case we over ride click.Option.handle_parse_result()
and disable the need to prompt if the other specified parameter does not match the desired.
Note: This answer was inspired by this answer.
Test Code:
@click.command()
@click.argument('an_argument', type=click.Choice(['excel', 'exchange']),
default='exchange')
@click.option('--password', hide_input=True, confirmation_prompt=False,
cls=PromptIf('an_argument', 'exchange'))
def cli(an_argument, password):
click.echo(an_argument)
click.echo(password)
cli('exchange'.split())
回答2:
You can try splitting this up to multiple commands.
For example, time
would be the entry point command. Either time_excel
or time_exchange
would then be invoked by time
based on the value of data_mode
. One could have a password prompt while the other wouldn't.
See Invoking Other Commands in Click's documentation.
来源:https://stackoverflow.com/questions/35727520/click-password-option-only-if-argument-equals-something