Source code for latch_cli.click_utils

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 ColoredHelpFormatter(HelpFormatter):
[docs] def write_usage( self, prog: str, args: str = "", prefix: Optional[str] = None ) -> None: if prefix is None: prefix = "Usage: " prefix = style(prefix, fg="bright_blue") return super().write_usage(prog, args, prefix)
[docs] def write_heading(self, heading: str): return super().write_heading(style(heading, fg="bright_blue"))
[docs] def write_dl( self, rows: Sequence[Tuple[str, str]], col_max: int = 30, col_spacing: int = 2 ) -> None: return super().write_dl( [(style(x[0], fg="bright_green", italic=True), x[1]) for x in rows], col_max, col_spacing, )
[docs]class LatchCommand(Command):
[docs] def format_epilog(self, ctx: Context, formatter: HelpFormatter) -> None: formatter.write_paragraph() formatter.write_text( style("See " + style("https://docs.latch.bio/", underline=True), dim=True) + style(" for manuals, tutorials, examples, and an API reference", dim=True) )
[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}"