Python 点击库中急切选项的非交互式确认
Noninteractive confirmation of eager options in the Python click library
我有一个用于重置配置文件的点击子命令,myprog config-reset
:
import click
@click.command()
@click.confirmation_option(prompt="Are you sure?")
def config_reset():
"""Reset the user configuration."""
click.echo("Success")
使用 click.confirmation_option
让我可以通过 --yes
绕过“你确定吗?”提示,这对测试很有用:
$ myprog config-reset --yes # No interactive confirmation required.
出于其他原因,我现在将 config-reset
子命令合并为通用 config
子命令,以便用户可以调用 myprog config --reset
。我通过使用一个急切的选项来实现这一点,如果 --reset
存在,则执行函数 config_reset
,而不是执行主函数体:
def config_reset(ctx, option, value):
if not value or ctx.resilient_parsing: # Copied from click docs.
return
click.confirm("Are you sure?", abort=True)
# If we got this far, the config should be reset...
@click.command()
@click.option(
"--reset",
is_flag=True,
callback=config_reset,
expose_value=False,
is_eager=True,
help="Reset the user configuration to the default.",
)
def config():
"""Show the configuration."""
# print config here...
现在的问题是,我没有办法通过 --yes
选项来绕过交互式“你确定吗?”确认。有没有办法结合 is_eager=True
来实现 click.confirmation_option
?本质上我希望能够写...
$ myprog config --reset --yes
...并让配置在没有确认的情况下自行重置。
我们可以使用 --yes
选项通过创建自定义 click.Option
class.
来跳过急切回调中的确认
自定义Class
def confirm_eager_option(*args, confirm_skip_flag_name='yes'):
class ConfirmEagerOption(click.Option):
def __init__(self, param_decls, **kwargs):
assert not param_decls, 'pass options names into confirm_eager_option()'
self.original_callback = kwargs['callback']
kwargs['callback'] = self.callback_handler
kwargs['is_eager'] = True
kwargs['is_flag'] = True
kwargs['expose_value'] = False
super().__init__(args, **kwargs)
def handle_parse_result(self, ctx, opts, args):
# grab the parsed options for later reference
self.parse_opts = opts
return super().handle_parse_result(ctx, opts, args)
def callback_handler(self, ctx, option, value):
if value and not self.parse_opts.get('help'):
# If we have the confirm flag, then set value to None
if self.parse_opts.get(confirm_skip_flag_name):
value = None
self.original_callback(ctx, option, value)
# build a decorator with our skip flag
confirm_skip_option = click.option(
f'--{confirm_skip_flag_name}', is_flag=True, expose_value=False, is_eager=True,
help=f"Skip confirmation for {args[0]}"
)
return ConfirmEagerOption
使用自定义 Class:
要使用自定义 class,首先通过调用 confirm_eager_option()
构建 class 并传递
它是选项名称和跳过标志名称,如:
confirm_eager_option_cls = confirm_eager_option(
'--reset', confirm_skip_flag_name='yes')
然后将 class 作为 click.option
装饰器的 cls
参数传递,并添加另一个
跳过选项的装饰器,如::
@click.command()
@click.option(cls=confirm_eager_option_cls, callback=config_reset,
help="Reset the user configuration to the default.")
@confirm_eager_option_cls.confirm_skip_option
def cli():
...
这是如何工作的?
之所以可行,是因为 click 是一个设计良好的 OO 框架。 @click.option()
装饰器
通常实例化一个 click.Option
对象,但允许这种行为被 cls
覆盖
范围。所以在我们自己的class以上继承click.Option
是一件比较容易的事情
乘坐所需的方法。
在这种情况下,我们重写 handle_parse_result()
方法。重写的方法允许我们
捕获其他已解析的选项,然后在急切的回调挂钩中查看这些选项以决定是否
允许跳过确认。
测试代码:
confirm_eager_option_cls = confirm_eager_option(
'--reset', confirm_skip_flag_name='yes')
def config_reset(ctx, option, value):
if value is None or value:
if value:
click.echo('Confirming reset...')
# click.confirm("Are you sure?", abort=True)
click.echo('Resetting...')
@click.command()
@click.option(cls=confirm_eager_option_cls, callback=config_reset,
help="Reset the user configuration to the default.")
@confirm_eager_option_cls.confirm_skip_option
def cli():
click.echo('running command')
if __name__ == "__main__":
commands = (
'',
'--reset',
'--reset --yes',
'--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: 7.1.2
Python Version: 3.8.5 (tags/v3.8.5:580fbb0, Jul 20 2020, 15:57:54) [MSC v.1924 64 bit (AMD64)]
-----------
>
running command
-----------
> --reset
Confirming reset...
Resetting...
running command
-----------
> --reset --yes
Resetting...
running command
-----------
> --help
Usage: test_code.py [OPTIONS]
Options:
--reset Reset the user configuration to the default.
--yes Skip confirmation for --reset
--help Show this message and exit.
您不必创建自定义选项。
见 documentation:
All eager parameters are evaluated before all non-eager parameters,
but again in the order as they were provided on the command line by
the user.
所以你需要做的是,添加 --yes 参数,这也是急切的,并在重置参数之前提供它。
这是一个示例命令:
@cli.command()
@click.option('--yes', is_flag=True, is_eager=True)
@click.option(
"--reset",
is_flag=True,
callback=config_reset,
is_eager=True,
)
def my_command(reset, yes):
pass
如果你这样调用命令:
$ my_command --yes --reset
您将有权访问回调中的 --yes 选项:
def config_reset(ctx, option, value):
print(ctx.params) # prints {'yes': True}
所以,您需要做的是:
def config_reset(ctx, option, value):
if not ctx.params.get('yes'):
click.confirm("Are you sure?", abort=True)
请记住,您的参数顺序确实符合:
$ my_command --reset --yes
不适合你。
我有一个用于重置配置文件的点击子命令,myprog config-reset
:
import click
@click.command()
@click.confirmation_option(prompt="Are you sure?")
def config_reset():
"""Reset the user configuration."""
click.echo("Success")
使用 click.confirmation_option
让我可以通过 --yes
绕过“你确定吗?”提示,这对测试很有用:
$ myprog config-reset --yes # No interactive confirmation required.
出于其他原因,我现在将 config-reset
子命令合并为通用 config
子命令,以便用户可以调用 myprog config --reset
。我通过使用一个急切的选项来实现这一点,如果 --reset
存在,则执行函数 config_reset
,而不是执行主函数体:
def config_reset(ctx, option, value):
if not value or ctx.resilient_parsing: # Copied from click docs.
return
click.confirm("Are you sure?", abort=True)
# If we got this far, the config should be reset...
@click.command()
@click.option(
"--reset",
is_flag=True,
callback=config_reset,
expose_value=False,
is_eager=True,
help="Reset the user configuration to the default.",
)
def config():
"""Show the configuration."""
# print config here...
现在的问题是,我没有办法通过 --yes
选项来绕过交互式“你确定吗?”确认。有没有办法结合 is_eager=True
来实现 click.confirmation_option
?本质上我希望能够写...
$ myprog config --reset --yes
...并让配置在没有确认的情况下自行重置。
我们可以使用 --yes
选项通过创建自定义 click.Option
class.
自定义Class
def confirm_eager_option(*args, confirm_skip_flag_name='yes'):
class ConfirmEagerOption(click.Option):
def __init__(self, param_decls, **kwargs):
assert not param_decls, 'pass options names into confirm_eager_option()'
self.original_callback = kwargs['callback']
kwargs['callback'] = self.callback_handler
kwargs['is_eager'] = True
kwargs['is_flag'] = True
kwargs['expose_value'] = False
super().__init__(args, **kwargs)
def handle_parse_result(self, ctx, opts, args):
# grab the parsed options for later reference
self.parse_opts = opts
return super().handle_parse_result(ctx, opts, args)
def callback_handler(self, ctx, option, value):
if value and not self.parse_opts.get('help'):
# If we have the confirm flag, then set value to None
if self.parse_opts.get(confirm_skip_flag_name):
value = None
self.original_callback(ctx, option, value)
# build a decorator with our skip flag
confirm_skip_option = click.option(
f'--{confirm_skip_flag_name}', is_flag=True, expose_value=False, is_eager=True,
help=f"Skip confirmation for {args[0]}"
)
return ConfirmEagerOption
使用自定义 Class:
要使用自定义 class,首先通过调用 confirm_eager_option()
构建 class 并传递
它是选项名称和跳过标志名称,如:
confirm_eager_option_cls = confirm_eager_option(
'--reset', confirm_skip_flag_name='yes')
然后将 class 作为 click.option
装饰器的 cls
参数传递,并添加另一个
跳过选项的装饰器,如::
@click.command()
@click.option(cls=confirm_eager_option_cls, callback=config_reset,
help="Reset the user configuration to the default.")
@confirm_eager_option_cls.confirm_skip_option
def cli():
...
这是如何工作的?
之所以可行,是因为 click 是一个设计良好的 OO 框架。 @click.option()
装饰器
通常实例化一个 click.Option
对象,但允许这种行为被 cls
覆盖
范围。所以在我们自己的class以上继承click.Option
是一件比较容易的事情
乘坐所需的方法。
在这种情况下,我们重写 handle_parse_result()
方法。重写的方法允许我们
捕获其他已解析的选项,然后在急切的回调挂钩中查看这些选项以决定是否
允许跳过确认。
测试代码:
confirm_eager_option_cls = confirm_eager_option(
'--reset', confirm_skip_flag_name='yes')
def config_reset(ctx, option, value):
if value is None or value:
if value:
click.echo('Confirming reset...')
# click.confirm("Are you sure?", abort=True)
click.echo('Resetting...')
@click.command()
@click.option(cls=confirm_eager_option_cls, callback=config_reset,
help="Reset the user configuration to the default.")
@confirm_eager_option_cls.confirm_skip_option
def cli():
click.echo('running command')
if __name__ == "__main__":
commands = (
'',
'--reset',
'--reset --yes',
'--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: 7.1.2
Python Version: 3.8.5 (tags/v3.8.5:580fbb0, Jul 20 2020, 15:57:54) [MSC v.1924 64 bit (AMD64)]
-----------
>
running command
-----------
> --reset
Confirming reset...
Resetting...
running command
-----------
> --reset --yes
Resetting...
running command
-----------
> --help
Usage: test_code.py [OPTIONS]
Options:
--reset Reset the user configuration to the default.
--yes Skip confirmation for --reset
--help Show this message and exit.
您不必创建自定义选项。 见 documentation:
All eager parameters are evaluated before all non-eager parameters, but again in the order as they were provided on the command line by the user.
所以你需要做的是,添加 --yes 参数,这也是急切的,并在重置参数之前提供它。 这是一个示例命令:
@cli.command()
@click.option('--yes', is_flag=True, is_eager=True)
@click.option(
"--reset",
is_flag=True,
callback=config_reset,
is_eager=True,
)
def my_command(reset, yes):
pass
如果你这样调用命令:
$ my_command --yes --reset
您将有权访问回调中的 --yes 选项:
def config_reset(ctx, option, value):
print(ctx.params) # prints {'yes': True}
所以,您需要做的是:
def config_reset(ctx, option, value):
if not ctx.params.get('yes'):
click.confirm("Are you sure?", abort=True)
请记住,您的参数顺序确实符合:
$ my_command --reset --yes
不适合你。