from enum import Enum
from typing import IO, Any, Optional, Sequence, Tuple, Type
from click import Choice, Command, Context, Group, HelpFormatter, Parameter, echo, style
from click._compat import get_text_stderr
[docs]class EnumChoice(Choice):
def __init__(self, choices: Type[Enum], case_sensitive: bool = True):
self.enum = choices
return super().__init__(choices._member_names_, case_sensitive)
[docs] def convert(
self, value: Any, param: Optional[Parameter], ctx: Optional[Context]
) -> Any:
ret = super().convert(value, param, ctx)
return self.enum(ret)
# todo(ayush): override `shell_complete` once we support it
[docs]class LatchCommand(Command):
[docs]class LatchGroup(LatchCommand, Group): ...
[docs]def colored_exception_show(self, file: Optional[IO] = None) -> None:
if file is None:
file = get_text_stderr()
echo(
style(f"Error: {style(self.format_message(), bold=True)}", fg="red"), file=file
)
[docs]def colored_usage_error_show(self, file: Optional[IO] = None) -> None:
if file is None:
file = get_text_stderr()
color = None
hint = ""
if self.ctx is not None and self.ctx.command.get_help_option(self.ctx) is not None:
hint = (
"Try "
+ style(
f"'{self.ctx.command_path} {self.ctx.help_option_names[0]}'",
bold=True,
)
+ " for help."
)
hint = f"{hint}\n"
if self.ctx is not None:
color = self.ctx.color
echo(f"{self.ctx.get_usage()}\n{hint}", file=file, color=color)
echo(
style(f"Error: {style(self.format_message(), bold=True)}", fg="red"),
file=file,
color=color,
)
[docs]def patch():
import click
click.Context.formatter_class = ColoredHelpFormatter
old_group = click.group
click.group = lambda *args, **kwargs: old_group(*args, **kwargs, cls=LatchGroup)
Group.command_class = LatchCommand
click.ClickException.show = colored_exception_show
click.UsageError.show = colored_usage_error_show
[docs]class AnsiCodes:
full_reset = "\x1b[0m"
color = "\x1b[38;5;39m"
reset_color = "\x1b[39m"
bold = "\x1b[1m"
reset_bold = "\x1b[22m"
underline = "\x1b[4m"
no_underline = "\x1b[24m"
# todo(maximsmol): use in supported terminals?
url_href = "\x1b]8;;"
url_name = "\x1b\\"
url_end = "\x1b]8;;\x1b\\"
[docs]def bold(s: str) -> str:
return f"{AnsiCodes.bold}{s}{AnsiCodes.reset_bold}"
[docs]def underline(s: str) -> str:
return f"{AnsiCodes.underline}{s}{AnsiCodes.no_underline}"
[docs]def color(s: str, *, color: str = AnsiCodes.color):
return f"{color}{s}{AnsiCodes.reset_color}"