import click
from click.parser import normalize_opt
from koapy.cli.extensions.functools import update_wrapper_with_click_params
[docs]class VerboseOption(click.Option):
def __init__(self, *decls, **attrs):
self._flag_value = attrs.pop("flag_value", None)
super().__init__(*decls, **attrs)
self._parser = None
self._parser__match_long_opt = None
self._parser__match_short_opt = None
self._parser__get_value_from_state = None
def _match_long_opt(self, opt, explicit_value, state):
# pylint: disable=protected-access
if opt in self.opts and opt in self._parser._long_opt:
option = self._parser._long_opt[opt]
option.action = "store"
self._parser__match_long_opt(opt, explicit_value, state)
def _match_short_opt(self, arg, state):
# pylint: disable=protected-access
prefix = arg[0]
for ch in arg[1:]:
opt = normalize_opt(f"{prefix}{ch}", self._parser.ctx)
if opt in self.opts and opt in self._parser._short_opt:
option = self._parser._short_opt[opt]
option.action = "count"
self._parser__match_short_opt(arg, state)
def _get_value_from_state(self, option_name, option, state):
# pylint: disable=protected-access
try:
value = self._parser__get_value_from_state(option_name, option, state)
except click.BadOptionUsage:
value = self._flag_value
else:
if option_name in self.opts:
if (
isinstance(value, str)
and value[:1] in self._parser._opt_prefixes
and len(value) > 1
):
state.rargs.insert(0, value)
value = self._flag_value
return value
def _patch_parser(self, parser):
# pylint: disable=protected-access
self._parser = parser
self._parser__match_short_opt = self._parser._match_short_opt
self._parser._match_short_opt = self._match_short_opt
self._parser__match_long_opt = self._parser._match_long_opt
self._parser._match_long_opt = self._match_long_opt
self._parser__get_value_from_state = self._parser._get_value_from_state
self._parser._get_value_from_state = self._get_value_from_state
[docs] def add_to_parser(self, parser, ctx):
self._patch_parser(parser)
super().add_to_parser(parser, ctx)
[docs]def verbose_flag_option(
default=0,
flag_value=1,
show_default=False,
metavar="[0...5]",
help="Set verbosity level.",
):
# pylint: disable=redefined-builtin
return click.option(
"-v",
"--verbose",
type=int,
default=default,
flag_value=flag_value,
show_default=show_default,
metavar=metavar,
help=help,
cls=VerboseOption,
)
[docs]def no_verbose_flag_option(
help="Force zero verbosity.",
):
# pylint: disable=redefined-builtin
return click.option(
"-s",
"--no-verbose",
"--slient",
is_flag=True,
default=False,
help=help,
)
[docs]def verbose_option(
dest="verbose",
default=0,
flag_value=1,
callback=None,
expose_value=False,
show_default=False,
):
def decorator(f):
@click.pass_context
@verbose_flag_option(
default=default,
flag_value=flag_value,
show_default=show_default,
)
@no_verbose_flag_option()
def new_func(ctx, *args, **kwargs):
verbose = kwargs.pop("verbose")
no_verbose = kwargs.pop("no_verbose")
if no_verbose:
verbose = 0
if expose_value:
kwargs[dest] = verbose
if callable(callback):
param = ctx.params.get("verbose")
value = verbose
callback(ctx, param, value)
return ctx.invoke(f, *args, **kwargs)
return update_wrapper_with_click_params(new_func, f)
return decorator