Closed OriBenHur-akeyless closed 7 months ago
Hi @OriBenHur-akeyless,
That's an interesting error!
If I had to guess what's happening:
rich
is counting the number of characters to set the border for the panel.click.style()
adds ANSI escape codes to color the text.rich
includes the ANSI escape codes as part of the count.So part of the issue here is, I think this is actually a bug in Rich, not a bug with rich-click.
I would check the Rich open issues and see if this is a known issue.
I understand that, in this specific case, it's unfortunate that Rich may have this bug because click.style()
is technically part of the rich-click API. So you'd expect them to kind of work together. I totally understand the motivation for wanting this to be fixed.
I can do a little bit of sleuthing as well, and if there is a quick fix then I can put it in as a patch update, but I will say that I'm not totally prioritizing this, because... (read the next section of this post 👀)
Fear not though, there is a workaround!
import rich_click as click
class OriginalMutexOption(click.Option):
def __init__(self, *args, **kwargs):
self.not_required_if: list = kwargs.pop("not_required_if")
assert self.not_required_if, "'not_required_if' parameter required"
kwargs["help"] = f'''{kwargs.get("help", "")} {click.style("NOTE:", bold=True, fg="magenta")} {click.style(f'Mutually exclusive with: {", ".join(self.not_required_if) if len(self.not_required_if) < 2 else ", ".join(self.not_required_if[:-1]) + " and " + self.not_required_if[-1]}', fg='magenta')}'''.strip()
super(OriginalMutexOption, self).__init__(*args, **kwargs)
def handle_parse_result(self, ctx, opts, args):
current_opt: bool = self.name in opts
for mutex_opt in self.not_required_if:
if mutex_opt in opts:
if current_opt:
raise click.UsageError(f'Illegal usage: {str(self.name)} '
f'is mutually exclusive with {str(mutex_opt)}.')
else:
self.required = None
return super(OriginalMutexOption, self).handle_parse_result(ctx, opts, args)
class UpdatedMutexOption(click.Option):
def __init__(self, *args, **kwargs):
self.not_required_if: list = kwargs.pop("not_required_if")
assert self.not_required_if, "'not_required_if' parameter required"
kwargs["help"] = f'''{kwargs.get("help", "")} [magenta][bold]NOTE:[/bold] Mutually exclusive with: {", ".join(self.not_required_if) if len(self.not_required_if) < 2 else ", ".join(self.not_required_if[:-1]) + " and " + self.not_required_if[-1]}[/magenta]'''.strip()
super(UpdatedMutexOption, self).__init__(*args, **kwargs)
def handle_parse_result(self, ctx, opts, args):
current_opt: bool = self.name in opts
for mutex_opt in self.not_required_if:
if mutex_opt in opts:
if current_opt:
raise click.UsageError(f'Illegal usage: {str(self.name)} '
f'is mutually exclusive with {str(mutex_opt)}.')
else:
self.required = None
return super(UpdatedMutexOption, self).handle_parse_result(ctx, opts, args)
@click.command
@click.option("--foo", cls=OriginalMutexOption, help="foo option", not_required_if=["a"])
@click.option("--bar", cls=UpdatedMutexOption, help="bar option", not_required_if=["a"])
@click.rich_config(help_config={"use_rich_markup": True})
def cli(foo):
"""my app"""
if __name__ == "__main__":
cli()
TLDR of the differences:
use_rich_markup=True
to the CLI's config.
text_markup="rich"
instead, but use_rich_markup
will be supported indefinitely into the future.)[magenta]
and [bold]
instead of click.style
Also, if you don't mind, even if the above fixes your issue, I'd still like to keep this one open. I think it's a genuine problem, even if there's an idiomatic workaround.
it's not working I'm afraid, it prints the control signs
Did you include use_rich_markup=True
in the config as well? That part is necessary, otherwise it will just render as plain text.
it's working
@OriBenHur-akeyless You can see in the above example how to make use of it.
Basically, add @click.rich_config(help_config={"use_rich_markup": True})
as a decorator for your command.
@click.command()
@click.rich_config(help_config={"use_rich_markup": True})
def cli():
...
# code goes here
Yay, I'm happy to hear it's working! 😄
Again, let's keep this issue open, if you don't mind! This is a genuinely strange behavior, and it's intuitive that you believed it would work without causing an error. Ideally your code would have worked just fine.
Fixed via #193. Thanks for flagging this issue; going forward in 1.8, click.style()
will work 😄
I would like to color only part of the helm message using
click.style
but for some reason, the spacing got brokenThe reason I would do such a thing is to outline a note, in the help message
result with
but using
click.style
result with this
Is there any way to keep the spacing using
click.style
or can you please propose another way to achieve that?