Вы можете принудительно использовать все параметры в группе, создав собственный класс, производный от click.Option
, и в этом классе использовать метод click.Option.handle_parse_result()
, например:
Класс пользовательских опций:
import click
class GroupedOptions(click.Option):
def __init__(self, *args, **kwargs):
self.opt_group = kwargs.pop('opt_group')
assert self.opt_group, "'opt_group' parameter required"
super(GroupedOptions, self).__init__(*args, **kwargs)
def handle_parse_result(self, ctx, opts, args):
if self.name in opts:
opts_in_group = [param.name for param in ctx.command.params
if isinstance(param, GroupedOptions) and
param.opt_group == self.opt_group]
missing_specified = tuple(name for name in opts_in_group
if name not in opts)
if missing_specified:
raise click.UsageError(
"Illegal usage: When using option '{}' must also use "
"all of options {}".format(self.name, missing_specified)
)
return super(GroupedOptions, self).handle_parse_result(
ctx, opts, args)
Использование пользовательского класса:
Чтобы использовать пользовательский класс, передайте параметр cls
декоратору click.option
, например:
@click.option('--opt1', cls=GroupedOptions, opt_group=1)
Дополнительно укажите номер группы опций с параметром opt_group
.
Как это работает?
Это работает, потому что click - это хорошо спроектированная OO-инфраструктура Декоратор @click.option()
обычно создает экземпляр объекта click.Option
, но позволяет изменить это поведение с помощью параметра cls
. Так что относительно просто унаследовать от click.Option
в нашем собственном классе и перебрать нужные методы.
В этом случае мы переходим click.Option.handle_parse_result()
и проверяем, указаны ли другие параметры в нашей группе.
Примечание. Этот ответ был вдохновлен этим ответом
Тестовый код:
@click.command()
@click.option('--opt1', cls=GroupedOptions, opt_group=1)
@click.option('--opt2', cls=GroupedOptions, opt_group=1)
@click.option('--opt3', cls=GroupedOptions, opt_group=1)
@click.option('--opt4', cls=GroupedOptions, opt_group=2)
@click.option('--opt5', cls=GroupedOptions, opt_group=2)
def cli(**kwargs):
for arg, value in kwargs.items():
click.echo("{}: {}".format(arg, value))
if __name__ == "__main__":
commands = (
'--opt1=x',
'--opt4=a',
'--opt4=a --opt5=b',
'--opt1=x --opt2=y --opt3=z --opt4=a --opt5=b',
'--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)]
-----------
> --opt1=x
Error: Illegal usage: When using option 'opt1' must also use all of options ('opt2', 'opt3')
-----------
> --opt4=a
Error: Illegal usage: When using option 'opt4' must also use all of options ('opt5',)
-----------
> --opt4=a --opt5=b
opt4: a
opt5: b
opt1: None
opt2: None
opt3: None
-----------
> --opt1=x --opt2=y --opt3=z --opt4=a --opt5=b
opt1: x
opt2: y
opt3: z
opt4: a
opt5: b
-----------
> --help
Usage: test.py [OPTIONS]
Options:
--opt1 TEXT
--opt2 TEXT
--opt3 TEXT
--opt4 TEXT
--opt5 TEXT
--help Show this message and exit.
-----------
>
opt1: None
opt2: None
opt3: None
opt4: None
opt5: None