python 中的互斥命令 单击
mutually exclusive commands in python Click
我有一个包含三个命令的 click
应用程序:
import click
@click.group(chain=True)
def cli():
print("MAIN")
@cli.command()
def initialize():
print("INITIALIZING")
@cli.command()
def update():
print("UPDATING")
@cli.command()
def process():
print("PROCESSING")
以这种方式定义,所有命令都可以链接起来。
但是,如何使 initialize
和 update
互斥?即:应该是:
合法 运行:
initialize
-> process
和
update
-> process
对运行不合法:
initialize
-> update
-> process
您可以通过创建自定义 click.Group
class.
将可链接的命令标记为互斥的
自定义Class
class MutuallyExclusiveCommandGroup(click.Group):
def __init__(self, *args, **kwargs):
kwargs['chain'] = True
self.mutually_exclusive = []
super().__init__(*args, **kwargs)
def command(self, *args, mutually_exclusive=False, **kwargs):
"""Track the commands marked as mutually exclusive"""
super_decorator = super().command(*args, **kwargs)
def decorator(f):
command = super_decorator(f)
if mutually_exclusive:
self.mutually_exclusive.append(command)
return command
return decorator
def resolve_command(self, ctx, args):
"""Hook the command resolving and verify mutual exclusivity"""
cmd_name, cmd, args = super().resolve_command(ctx, args)
# find the commands which are going to be run
if not hasattr(ctx, 'resolved_commands'):
ctx.resolved_commands = set()
ctx.resolved_commands.add(cmd_name)
# if args is empty we have have found all of the commands to be run
if not args:
mutually_exclusive = ctx.resolved_commands & set(
cmd.name for cmd in self.mutually_exclusive)
if len(mutually_exclusive) > 1:
raise click.UsageError(
"Illegal usage: commands: `{}` are mutually exclusive".format(
', '.join(mutually_exclusive)))
return cmd_name, cmd, args
def get_help(self, ctx):
"""Extend the short help for the mutually exclusive commands"""
for cmd in self.mutually_exclusive:
mutually_exclusive = set(self.mutually_exclusive)
if not cmd.short_help:
cmd.short_help = 'mutually exclusive with: {}'.format(', '.join(
c.name for c in mutually_exclusive if c.name != cmd.name))
return super().get_help(ctx)
使用自定义 Class:
要使用自定义 class,请将其作为 cls
参数传递给 click.group
装饰器,例如:
@click.group(cls=MutuallyExclusiveCommandGroup)
@click.pass_context
def cli(ctx):
...
然后使用 mutually_exclusive
参数给 cli.command
装饰器来标记命令
作为互斥组的一部分。
@cli.command(mutually_exclusive=True)
def update():
...
这是如何工作的?
之所以可行,是因为 click 是一个设计良好的 OO 框架。 @click.group()
装饰器
通常实例化一个 click.Group
对象,但允许此行为被 cls
覆盖
范围。所以在我们自己的class以上继承click.Group
是一件比较容易的事情
乘坐所需的方法。
在这种情况下,我们重写了三个方法:command(), resolve_command() & get_help()
。被覆盖的
command()
方法允许我们跟踪哪些命令标有 mutually_exclusive
标志。这
重写的resolve_command()
方法用于观察命令解析过程,注意哪个
命令将是 运行。如果互斥命令是 运行,它会抛出一个错误。这
覆盖的 get_help
方法设置 short_help
属性以显示哪些命令是互斥的。
测试代码:
import click
@click.group(chain=True, cls=MutuallyExclusiveCommandGroup)
@click.pass_context
def cli(ctx):
print("MAIN")
@cli.command()
def initialize():
print("INITIALIZING")
@cli.command(mutually_exclusive=True)
def update():
print("UPDATING")
@cli.command(mutually_exclusive=True)
def process():
print("PROCESSING")
if __name__ == "__main__":
commands = (
'',
'initialize',
'update',
'process',
'initialize process',
'update process',
'initialize update process',
'--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)]
-----------
>
Usage: test_code.py [OPTIONS] COMMAND1 [ARGS]... [COMMAND2 [ARGS]...]...
Options:
--help Show this message and exit.
Commands:
initialize
process mutually exclusive with: update
update mutually exclusive with: process
-----------
> initialize
MAIN
INITIALIZING
-----------
> update
MAIN
UPDATING
-----------
> process
MAIN
PROCESSING
-----------
> initialize process
MAIN
INITIALIZING
PROCESSING
-----------
> update process
MAIN
Error: Illegal usage: commands: `update, process` are mutually exclusive
-----------
> initialize update process
MAIN
Error: Illegal usage: commands: `update, process` are mutually exclusive
-----------
> --help
Usage: test_code.py [OPTIONS] COMMAND1 [ARGS]... [COMMAND2 [ARGS]...]...
Options:
--help Show this message and exit.
Commands:
initialize
process mutually exclusive with: update
update mutually exclusive with: process.
我有一个包含三个命令的 click
应用程序:
import click
@click.group(chain=True)
def cli():
print("MAIN")
@cli.command()
def initialize():
print("INITIALIZING")
@cli.command()
def update():
print("UPDATING")
@cli.command()
def process():
print("PROCESSING")
以这种方式定义,所有命令都可以链接起来。
但是,如何使 initialize
和 update
互斥?即:应该是:
合法 运行:
initialize
-> process
和
update
-> process
对运行不合法:
initialize
-> update
-> process
您可以通过创建自定义 click.Group
class.
自定义Class
class MutuallyExclusiveCommandGroup(click.Group):
def __init__(self, *args, **kwargs):
kwargs['chain'] = True
self.mutually_exclusive = []
super().__init__(*args, **kwargs)
def command(self, *args, mutually_exclusive=False, **kwargs):
"""Track the commands marked as mutually exclusive"""
super_decorator = super().command(*args, **kwargs)
def decorator(f):
command = super_decorator(f)
if mutually_exclusive:
self.mutually_exclusive.append(command)
return command
return decorator
def resolve_command(self, ctx, args):
"""Hook the command resolving and verify mutual exclusivity"""
cmd_name, cmd, args = super().resolve_command(ctx, args)
# find the commands which are going to be run
if not hasattr(ctx, 'resolved_commands'):
ctx.resolved_commands = set()
ctx.resolved_commands.add(cmd_name)
# if args is empty we have have found all of the commands to be run
if not args:
mutually_exclusive = ctx.resolved_commands & set(
cmd.name for cmd in self.mutually_exclusive)
if len(mutually_exclusive) > 1:
raise click.UsageError(
"Illegal usage: commands: `{}` are mutually exclusive".format(
', '.join(mutually_exclusive)))
return cmd_name, cmd, args
def get_help(self, ctx):
"""Extend the short help for the mutually exclusive commands"""
for cmd in self.mutually_exclusive:
mutually_exclusive = set(self.mutually_exclusive)
if not cmd.short_help:
cmd.short_help = 'mutually exclusive with: {}'.format(', '.join(
c.name for c in mutually_exclusive if c.name != cmd.name))
return super().get_help(ctx)
使用自定义 Class:
要使用自定义 class,请将其作为 cls
参数传递给 click.group
装饰器,例如:
@click.group(cls=MutuallyExclusiveCommandGroup)
@click.pass_context
def cli(ctx):
...
然后使用 mutually_exclusive
参数给 cli.command
装饰器来标记命令
作为互斥组的一部分。
@cli.command(mutually_exclusive=True)
def update():
...
这是如何工作的?
之所以可行,是因为 click 是一个设计良好的 OO 框架。 @click.group()
装饰器
通常实例化一个 click.Group
对象,但允许此行为被 cls
覆盖
范围。所以在我们自己的class以上继承click.Group
是一件比较容易的事情
乘坐所需的方法。
在这种情况下,我们重写了三个方法:command(), resolve_command() & get_help()
。被覆盖的
command()
方法允许我们跟踪哪些命令标有 mutually_exclusive
标志。这
重写的resolve_command()
方法用于观察命令解析过程,注意哪个
命令将是 运行。如果互斥命令是 运行,它会抛出一个错误。这
覆盖的 get_help
方法设置 short_help
属性以显示哪些命令是互斥的。
测试代码:
import click
@click.group(chain=True, cls=MutuallyExclusiveCommandGroup)
@click.pass_context
def cli(ctx):
print("MAIN")
@cli.command()
def initialize():
print("INITIALIZING")
@cli.command(mutually_exclusive=True)
def update():
print("UPDATING")
@cli.command(mutually_exclusive=True)
def process():
print("PROCESSING")
if __name__ == "__main__":
commands = (
'',
'initialize',
'update',
'process',
'initialize process',
'update process',
'initialize update process',
'--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)]
-----------
>
Usage: test_code.py [OPTIONS] COMMAND1 [ARGS]... [COMMAND2 [ARGS]...]...
Options:
--help Show this message and exit.
Commands:
initialize
process mutually exclusive with: update
update mutually exclusive with: process
-----------
> initialize
MAIN
INITIALIZING
-----------
> update
MAIN
UPDATING
-----------
> process
MAIN
PROCESSING
-----------
> initialize process
MAIN
INITIALIZING
PROCESSING
-----------
> update process
MAIN
Error: Illegal usage: commands: `update, process` are mutually exclusive
-----------
> initialize update process
MAIN
Error: Illegal usage: commands: `update, process` are mutually exclusive
-----------
> --help
Usage: test_code.py [OPTIONS] COMMAND1 [ARGS]... [COMMAND2 [ARGS]...]...
Options:
--help Show this message and exit.
Commands:
initialize
process mutually exclusive with: update
update mutually exclusive with: process.