Click – Dynamic default value based on hints for additional options
I’m building the CLI interface using Click. Click on the offerdynamic defaults for prompts, which is great. Also this example gives some insight into how to implement dynamic defaults using custom click classes, providing more flexible options when evaluating defaults.
What I want to do now is get dynamic defaults based on the click option provided by the other, eg
python mymodule --param1 something --param2 somethingelse
Now, if param2
is empty, I’d like to try to get the dynamic default based on the param1
input provided, for example:
@click.command()
@click.option('--param1', prompt=True)
@click.option('--param2', prompt=True, default=lambda: myfunct(param1))
def cmd(param1, param2):
pass
myfunct(param1:str=None):
param2 = None
#get param2 based on param1 input
return param2
Any ideas on what is the best way to get this done?
Is param1
guaranteed to be evaluated (and hinted) before param2
?
Solution
Extensionexample you referencedThe required functionality can be done like this:
#
##自定义类:
import click
class OptionPromptNull(click. Option):
_value_key = '_default_val'
def __init__(self, *args, **kwargs):
self.default_option = kwargs.pop('default_option', None)
super(OptionPromptNull, self).__init__(*args, **kwargs)
def get_default(self, ctx, **kwargs):
if not hasattr(self, self._value_key):
if self.default_option is None:
default = super(OptionPromptNull, self).get_default(ctx, **kwargs)
else:
arg = ctx.params[self.default_option]
default = self.type_cast_value(ctx, self.default(arg))
setattr(self, self._value_key, default)
return getattr(self, self._value_key)
def prompt_for_value(self, ctx):
default = self.get_default(ctx)
# only prompt if the default value is None
if default is None:
return super(OptionPromptNull, self).prompt_for_value(ctx)
return default
#
##使用自定义类:
To use a custom class, you need to pass three parameters to the click.option
decorator, for example:
@click.option('--param3', cls=OptionPromptNull, default_option='param1',
default=lambda x: get_a_value(x), prompt="Enter Param3")
CLS
needs to reference a custom class.default_option
needs to specify which option to pass tothe default
callable.specifies the callable used to get the
default
value.
default
#
##这是如何工作的?
This works because click
is a well-designed OO framework. @click.option()
decorators usually instantiate a click. Option
object, but allows this behavior to be overridden with the cls
parameter. Therefore, inheriting click. Option
and override the required method is a relatively easy thing to do.
In this case, we override click. Option.get_default()
and click. Option.prompt_for_value()
method. In prompt_for_value(),
we only prompt when the default value is None
. Then in get_default(),
we call the default
function to pass the required (previously entered) arguments.
To clarify part of the problem, evaluate options first: in the order in which they are passed on the command line
, and second: in the order in which they are declared for those options that are not passed on the command line.
#
##测试代码:
@click.command()
@click.option('--param1', prompt="Enter Param1")
@click.option('--param2', cls=OptionPromptNull,
default=lambda: get_value_none(), prompt="Enter Param2")
@click.option('--param3', cls=OptionPromptNull, default_option='param1',
default=lambda x: get_a_value(x), prompt="Enter Param3")
def cli(param1, param2, param3):
click.echo("param1: '{}'".format(param1))
click.echo("param2: '{}'".format(param2))
click.echo("param3: '{}'".format(param3))
def get_value_none():
return None
def get_a_value(val):
return val
if __name__ == "__main__":
commands = (
r'',
r'--param3 5',
'--help',
)
import sys, time
time.sleep(1)
print('Click Version: {}'.format(click.__version__))
print('Python Version: {}'.format(sys.version))
for cmd in commands:
try:
time.sleep(0.1)
print('-----------')
print('> ' + cmd)
time.sleep(0.1)
cli(cmd.split())
except BaseException as exc:
if str(exc) != '0' and \
not isinstance(exc, (click. ClickException, SystemExit)):
raise
#
##结果:
Click Version: 6.7
Python Version: 3.6.3 (v3.6.3:2c5fed8, Oct 3 2017, 18:11:49) [MSC v.1900 64 bit (AMD64)]
-----------
>
Enter Param1: 3
Enter Param2: 4
param1: '3'
param2: '4'
param3: '3'
-----------
> --param3 5
Enter Param1: 3
Enter Param2: 4
param1: '3'
param2: '4'
param3: '5'
-----------
> --help
Usage: test.py [OPTIONS]
Options:
--param1 TEXT
--param2 TEXT
--param3 TEXT
--help Show this message and exit.