Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
termui.py785 linesDownload Raw Back to click
1import inspect2import io3import itertools4import sys5import typing as t6from gettext import gettext as _7 8from ._compat import isatty9from ._compat import strip_ansi10from .exceptions import Abort11from .exceptions import UsageError12from .globals import resolve_color_default13from .types import Choice14from .types import convert_type15from .types import ParamType16from .utils import echo17from .utils import LazyFile18 19if t.TYPE_CHECKING:20    from ._termui_impl import ProgressBar21 22V = t.TypeVar("V")23 24# The prompt functions to use.  The doc tools currently override these25# functions to customize how they work.26visible_prompt_func: t.Callable[[str], str] = input27 28_ansi_colors = {29    "black": 30,30    "red": 31,31    "green": 32,32    "yellow": 33,33    "blue": 34,34    "magenta": 35,35    "cyan": 36,36    "white": 37,37    "reset": 39,38    "bright_black": 90,39    "bright_red": 91,40    "bright_green": 92,41    "bright_yellow": 93,42    "bright_blue": 94,43    "bright_magenta": 95,44    "bright_cyan": 96,45    "bright_white": 97,46}47_ansi_reset_all = "\033[0m"48 49 50def hidden_prompt_func(prompt: str) -> str:51    import getpass52 53    return getpass.getpass(prompt)54 55 56def _build_prompt(57    text: str,58    suffix: str,59    show_default: bool = False,60    default: t.Optional[t.Any] = None,61    show_choices: bool = True,62    type: t.Optional[ParamType] = None,63) -> str:64    prompt = text65    if type is not None and show_choices and isinstance(type, Choice):66        prompt += f" ({', '.join(map(str, type.choices))})"67    if default is not None and show_default:68        prompt = f"{prompt} [{_format_default(default)}]"69    return f"{prompt}{suffix}"70 71 72def _format_default(default: t.Any) -> t.Any:73    if isinstance(default, (io.IOBase, LazyFile)) and hasattr(default, "name"):74        return default.name75 76    return default77 78 79def prompt(80    text: str,81    default: t.Optional[t.Any] = None,82    hide_input: bool = False,83    confirmation_prompt: t.Union[bool, str] = False,84    type: t.Optional[t.Union[ParamType, t.Any]] = None,85    value_proc: t.Optional[t.Callable[[str], t.Any]] = None,86    prompt_suffix: str = ": ",87    show_default: bool = True,88    err: bool = False,89    show_choices: bool = True,90) -> t.Any:91    """Prompts a user for input.  This is a convenience function that can92    be used to prompt a user for input later.93 94    If the user aborts the input by sending an interrupt signal, this95    function will catch it and raise a :exc:`Abort` exception.96 97    :param text: the text to show for the prompt.98    :param default: the default value to use if no input happens.  If this99                    is not given it will prompt until it's aborted.100    :param hide_input: if this is set to true then the input value will101                       be hidden.102    :param confirmation_prompt: Prompt a second time to confirm the103        value. Can be set to a string instead of ``True`` to customize104        the message.105    :param type: the type to use to check the value against.106    :param value_proc: if this parameter is provided it's a function that107                       is invoked instead of the type conversion to108                       convert a value.109    :param prompt_suffix: a suffix that should be added to the prompt.110    :param show_default: shows or hides the default value in the prompt.111    :param err: if set to true the file defaults to ``stderr`` instead of112                ``stdout``, the same as with echo.113    :param show_choices: Show or hide choices if the passed type is a Choice.114                         For example if type is a Choice of either day or week,115                         show_choices is true and text is "Group by" then the116                         prompt will be "Group by (day, week): ".117 118    .. versionadded:: 8.0119        ``confirmation_prompt`` can be a custom string.120 121    .. versionadded:: 7.0122        Added the ``show_choices`` parameter.123 124    .. versionadded:: 6.0125        Added unicode support for cmd.exe on Windows.126 127    .. versionadded:: 4.0128        Added the `err` parameter.129 130    """131 132    def prompt_func(text: str) -> str:133        f = hidden_prompt_func if hide_input else visible_prompt_func134        try:135            # Write the prompt separately so that we get nice136            # coloring through colorama on Windows137            echo(text.rstrip(" "), nl=False, err=err)138            # Echo a space to stdout to work around an issue where139            # readline causes backspace to clear the whole line.140            return f(" ")141        except (KeyboardInterrupt, EOFError):142            # getpass doesn't print a newline if the user aborts input with ^C.143            # Allegedly this behavior is inherited from getpass(3).144            # A doc bug has been filed at https://bugs.python.org/issue24711145            if hide_input:146                echo(None, err=err)147            raise Abort() from None148 149    if value_proc is None:150        value_proc = convert_type(type, default)151 152    prompt = _build_prompt(153        text, prompt_suffix, show_default, default, show_choices, type154    )155 156    if confirmation_prompt:157        if confirmation_prompt is True:158            confirmation_prompt = _("Repeat for confirmation")159 160        confirmation_prompt = _build_prompt(confirmation_prompt, prompt_suffix)161 162    while True:163        while True:164            value = prompt_func(prompt)165            if value:166                break167            elif default is not None:168                value = default169                break170        try:171            result = value_proc(value)172        except UsageError as e:173            if hide_input:174                echo(_("Error: The value you entered was invalid."), err=err)175            else:176                echo(_("Error: {e.message}").format(e=e), err=err)  # noqa: B306177            continue178        if not confirmation_prompt:179            return result180        while True:181            value2 = prompt_func(confirmation_prompt)182            is_empty = not value and not value2183            if value2 or is_empty:184                break185        if value == value2:186            return result187        echo(_("Error: The two entered values do not match."), err=err)188 189 190def confirm(191    text: str,192    default: t.Optional[bool] = False,193    abort: bool = False,194    prompt_suffix: str = ": ",195    show_default: bool = True,196    err: bool = False,197) -> bool:198    """Prompts for confirmation (yes/no question).199 200    If the user aborts the input by sending a interrupt signal this201    function will catch it and raise a :exc:`Abort` exception.202 203    :param text: the question to ask.204    :param default: The default value to use when no input is given. If205        ``None``, repeat until input is given.206    :param abort: if this is set to `True` a negative answer aborts the207                  exception by raising :exc:`Abort`.208    :param prompt_suffix: a suffix that should be added to the prompt.209    :param show_default: shows or hides the default value in the prompt.210    :param err: if set to true the file defaults to ``stderr`` instead of211                ``stdout``, the same as with echo.212 213    .. versionchanged:: 8.0214        Repeat until input is given if ``default`` is ``None``.215 216    .. versionadded:: 4.0217        Added the ``err`` parameter.218    """219    prompt = _build_prompt(220        text,221        prompt_suffix,222        show_default,223        "y/n" if default is None else ("Y/n" if default else "y/N"),224    )225 226    while True:227        try:228            # Write the prompt separately so that we get nice229            # coloring through colorama on Windows230            echo(prompt.rstrip(" "), nl=False, err=err)231            # Echo a space to stdout to work around an issue where232            # readline causes backspace to clear the whole line.233            value = visible_prompt_func(" ").lower().strip()234        except (KeyboardInterrupt, EOFError):235            raise Abort() from None236        if value in ("y", "yes"):237            rv = True238        elif value in ("n", "no"):239            rv = False240        elif default is not None and value == "":241            rv = default242        else:243            echo(_("Error: invalid input"), err=err)244            continue245        break246    if abort and not rv:247        raise Abort()248    return rv249 250 251def echo_via_pager(252    text_or_generator: t.Union[t.Iterable[str], t.Callable[[], t.Iterable[str]], str],253    color: t.Optional[bool] = None,254) -> None:255    """This function takes a text and shows it via an environment specific256    pager on stdout.257 258    .. versionchanged:: 3.0259       Added the `color` flag.260 261    :param text_or_generator: the text to page, or alternatively, a262                              generator emitting the text to page.263    :param color: controls if the pager supports ANSI colors or not.  The264                  default is autodetection.265    """266    color = resolve_color_default(color)267 268    if inspect.isgeneratorfunction(text_or_generator):269        i = t.cast(t.Callable[[], t.Iterable[str]], text_or_generator)()270    elif isinstance(text_or_generator, str):271        i = [text_or_generator]272    else:273        i = iter(t.cast(t.Iterable[str], text_or_generator))274 275    # convert every element of i to a text type if necessary276    text_generator = (el if isinstance(el, str) else str(el) for el in i)277 278    from ._termui_impl import pager279 280    return pager(itertools.chain(text_generator, "\n"), color)281 282 283def progressbar(284    iterable: t.Optional[t.Iterable[V]] = None,285    length: t.Optional[int] = None,286    label: t.Optional[str] = None,287    show_eta: bool = True,288    show_percent: t.Optional[bool] = None,289    show_pos: bool = False,290    item_show_func: t.Optional[t.Callable[[t.Optional[V]], t.Optional[str]]] = None,291    fill_char: str = "#",292    empty_char: str = "-",293    bar_template: str = "%(label)s  [%(bar)s]  %(info)s",294    info_sep: str = "  ",295    width: int = 36,296    file: t.Optional[t.TextIO] = None,297    color: t.Optional[bool] = None,298    update_min_steps: int = 1,299) -> "ProgressBar[V]":300    """This function creates an iterable context manager that can be used301    to iterate over something while showing a progress bar.  It will302    either iterate over the `iterable` or `length` items (that are counted303    up).  While iteration happens, this function will print a rendered304    progress bar to the given `file` (defaults to stdout) and will attempt305    to calculate remaining time and more.  By default, this progress bar306    will not be rendered if the file is not a terminal.307 308    The context manager creates the progress bar.  When the context309    manager is entered the progress bar is already created.  With every310    iteration over the progress bar, the iterable passed to the bar is311    advanced and the bar is updated.  When the context manager exits,312    a newline is printed and the progress bar is finalized on screen.313 314    Note: The progress bar is currently designed for use cases where the315    total progress can be expected to take at least several seconds.316    Because of this, the ProgressBar class object won't display317    progress that is considered too fast, and progress where the time318    between steps is less than a second.319 320    No printing must happen or the progress bar will be unintentionally321    destroyed.322 323    Example usage::324 325        with progressbar(items) as bar:326            for item in bar:327                do_something_with(item)328 329    Alternatively, if no iterable is specified, one can manually update the330    progress bar through the `update()` method instead of directly331    iterating over the progress bar.  The update method accepts the number332    of steps to increment the bar with::333 334        with progressbar(length=chunks.total_bytes) as bar:335            for chunk in chunks:336                process_chunk(chunk)337                bar.update(chunks.bytes)338 339    The ``update()`` method also takes an optional value specifying the340    ``current_item`` at the new position. This is useful when used341    together with ``item_show_func`` to customize the output for each342    manual step::343 344        with click.progressbar(345            length=total_size,346            label='Unzipping archive',347            item_show_func=lambda a: a.filename348        ) as bar:349            for archive in zip_file:350                archive.extract()351                bar.update(archive.size, archive)352 353    :param iterable: an iterable to iterate over.  If not provided the length354                     is required.355    :param length: the number of items to iterate over.  By default the356                   progressbar will attempt to ask the iterator about its357                   length, which might or might not work.  If an iterable is358                   also provided this parameter can be used to override the359                   length.  If an iterable is not provided the progress bar360                   will iterate over a range of that length.361    :param label: the label to show next to the progress bar.362    :param show_eta: enables or disables the estimated time display.  This is363                     automatically disabled if the length cannot be364                     determined.365    :param show_percent: enables or disables the percentage display.  The366                         default is `True` if the iterable has a length or367                         `False` if not.368    :param show_pos: enables or disables the absolute position display.  The369                     default is `False`.370    :param item_show_func: A function called with the current item which371        can return a string to show next to the progress bar. If the372        function returns ``None`` nothing is shown. The current item can373        be ``None``, such as when entering and exiting the bar.374    :param fill_char: the character to use to show the filled part of the375                      progress bar.376    :param empty_char: the character to use to show the non-filled part of377                       the progress bar.378    :param bar_template: the format string to use as template for the bar.379                         The parameters in it are ``label`` for the label,380                         ``bar`` for the progress bar and ``info`` for the381                         info section.382    :param info_sep: the separator between multiple info items (eta etc.)383    :param width: the width of the progress bar in characters, 0 means full384                  terminal width385    :param file: The file to write to. If this is not a terminal then386        only the label is printed.387    :param color: controls if the terminal supports ANSI colors or not.  The388                  default is autodetection.  This is only needed if ANSI389                  codes are included anywhere in the progress bar output390                  which is not the case by default.391    :param update_min_steps: Render only when this many updates have392        completed. This allows tuning for very fast iterators.393 394    .. versionchanged:: 8.0395        Output is shown even if execution time is less than 0.5 seconds.396 397    .. versionchanged:: 8.0398        ``item_show_func`` shows the current item, not the previous one.399 400    .. versionchanged:: 8.0401        Labels are echoed if the output is not a TTY. Reverts a change402        in 7.0 that removed all output.403 404    .. versionadded:: 8.0405       Added the ``update_min_steps`` parameter.406 407    .. versionchanged:: 4.0408        Added the ``color`` parameter. Added the ``update`` method to409        the object.410 411    .. versionadded:: 2.0412    """413    from ._termui_impl import ProgressBar414 415    color = resolve_color_default(color)416    return ProgressBar(417        iterable=iterable,418        length=length,419        show_eta=show_eta,420        show_percent=show_percent,421        show_pos=show_pos,422        item_show_func=item_show_func,423        fill_char=fill_char,424        empty_char=empty_char,425        bar_template=bar_template,426        info_sep=info_sep,427        file=file,428        label=label,429        width=width,430        color=color,431        update_min_steps=update_min_steps,432    )433 434 435def clear() -> None:436    """Clears the terminal screen.  This will have the effect of clearing437    the whole visible space of the terminal and moving the cursor to the438    top left.  This does not do anything if not connected to a terminal.439 440    .. versionadded:: 2.0441    """442    if not isatty(sys.stdout):443        return444 445    # ANSI escape \033[2J clears the screen, \033[1;1H moves the cursor446    echo("\033[2J\033[1;1H", nl=False)447 448 449def _interpret_color(450    color: t.Union[int, t.Tuple[int, int, int], str], offset: int = 0451) -> str:452    if isinstance(color, int):453        return f"{38 + offset};5;{color:d}"454 455    if isinstance(color, (tuple, list)):456        r, g, b = color457        return f"{38 + offset};2;{r:d};{g:d};{b:d}"458 459    return str(_ansi_colors[color] + offset)460 461 462def style(463    text: t.Any,464    fg: t.Optional[t.Union[int, t.Tuple[int, int, int], str]] = None,465    bg: t.Optional[t.Union[int, t.Tuple[int, int, int], str]] = None,466    bold: t.Optional[bool] = None,467    dim: t.Optional[bool] = None,468    underline: t.Optional[bool] = None,469    overline: t.Optional[bool] = None,470    italic: t.Optional[bool] = None,471    blink: t.Optional[bool] = None,472    reverse: t.Optional[bool] = None,473    strikethrough: t.Optional[bool] = None,474    reset: bool = True,475) -> str:476    """Styles a text with ANSI styles and returns the new string.  By477    default the styling is self contained which means that at the end478    of the string a reset code is issued.  This can be prevented by479    passing ``reset=False``.480 481    Examples::482 483        click.echo(click.style('Hello World!', fg='green'))484        click.echo(click.style('ATTENTION!', blink=True))485        click.echo(click.style('Some things', reverse=True, fg='cyan'))486        click.echo(click.style('More colors', fg=(255, 12, 128), bg=117))487 488    Supported color names:489 490    * ``black`` (might be a gray)491    * ``red``492    * ``green``493    * ``yellow`` (might be an orange)494    * ``blue``495    * ``magenta``496    * ``cyan``497    * ``white`` (might be light gray)498    * ``bright_black``499    * ``bright_red``500    * ``bright_green``501    * ``bright_yellow``502    * ``bright_blue``503    * ``bright_magenta``504    * ``bright_cyan``505    * ``bright_white``506    * ``reset`` (reset the color code only)507 508    If the terminal supports it, color may also be specified as:509 510    -   An integer in the interval [0, 255]. The terminal must support511        8-bit/256-color mode.512    -   An RGB tuple of three integers in [0, 255]. The terminal must513        support 24-bit/true-color mode.514 515    See https://en.wikipedia.org/wiki/ANSI_color and516    https://gist.github.com/XVilka/8346728 for more information.517 518    :param text: the string to style with ansi codes.519    :param fg: if provided this will become the foreground color.520    :param bg: if provided this will become the background color.521    :param bold: if provided this will enable or disable bold mode.522    :param dim: if provided this will enable or disable dim mode.  This is523                badly supported.524    :param underline: if provided this will enable or disable underline.525    :param overline: if provided this will enable or disable overline.526    :param italic: if provided this will enable or disable italic.527    :param blink: if provided this will enable or disable blinking.528    :param reverse: if provided this will enable or disable inverse529                    rendering (foreground becomes background and the530                    other way round).531    :param strikethrough: if provided this will enable or disable532        striking through text.533    :param reset: by default a reset-all code is added at the end of the534                  string which means that styles do not carry over.  This535                  can be disabled to compose styles.536 537    .. versionchanged:: 8.0538        A non-string ``message`` is converted to a string.539 540    .. versionchanged:: 8.0541       Added support for 256 and RGB color codes.542 543    .. versionchanged:: 8.0544        Added the ``strikethrough``, ``italic``, and ``overline``545        parameters.546 547    .. versionchanged:: 7.0548        Added support for bright colors.549 550    .. versionadded:: 2.0551    """552    if not isinstance(text, str):553        text = str(text)554 555    bits = []556 557    if fg:558        try:559            bits.append(f"\033[{_interpret_color(fg)}m")560        except KeyError:561            raise TypeError(f"Unknown color {fg!r}") from None562 563    if bg:564        try:565            bits.append(f"\033[{_interpret_color(bg, 10)}m")566        except KeyError:567            raise TypeError(f"Unknown color {bg!r}") from None568 569    if bold is not None:570        bits.append(f"\033[{1 if bold else 22}m")571    if dim is not None:572        bits.append(f"\033[{2 if dim else 22}m")573    if underline is not None:574        bits.append(f"\033[{4 if underline else 24}m")575    if overline is not None:576        bits.append(f"\033[{53 if overline else 55}m")577    if italic is not None:578        bits.append(f"\033[{3 if italic else 23}m")579    if blink is not None:580        bits.append(f"\033[{5 if blink else 25}m")581    if reverse is not None:582        bits.append(f"\033[{7 if reverse else 27}m")583    if strikethrough is not None:584        bits.append(f"\033[{9 if strikethrough else 29}m")585    bits.append(text)586    if reset:587        bits.append(_ansi_reset_all)588    return "".join(bits)589 590 591def unstyle(text: str) -> str:592    """Removes ANSI styling information from a string.  Usually it's not593    necessary to use this function as Click's echo function will594    automatically remove styling if necessary.595 596    .. versionadded:: 2.0597 598    :param text: the text to remove style information from.599    """600    return strip_ansi(text)601 602 603def secho(604    message: t.Optional[t.Any] = None,605    file: t.Optional[t.IO[t.AnyStr]] = None,606    nl: bool = True,607    err: bool = False,608    color: t.Optional[bool] = None,609    **styles: t.Any,610) -> None:611    """This function combines :func:`echo` and :func:`style` into one612    call.  As such the following two calls are the same::613 614        click.secho('Hello World!', fg='green')615        click.echo(click.style('Hello World!', fg='green'))616 617    All keyword arguments are forwarded to the underlying functions618    depending on which one they go with.619 620    Non-string types will be converted to :class:`str`. However,621    :class:`bytes` are passed directly to :meth:`echo` without applying622    style. If you want to style bytes that represent text, call623    :meth:`bytes.decode` first.624 625    .. versionchanged:: 8.0626        A non-string ``message`` is converted to a string. Bytes are627        passed through without style applied.628 629    .. versionadded:: 2.0630    """631    if message is not None and not isinstance(message, (bytes, bytearray)):632        message = style(message, **styles)633 634    return echo(message, file=file, nl=nl, err=err, color=color)635 636 637def edit(638    text: t.Optional[t.AnyStr] = None,639    editor: t.Optional[str] = None,640    env: t.Optional[t.Mapping[str, str]] = None,641    require_save: bool = True,642    extension: str = ".txt",643    filename: t.Optional[str] = None,644) -> t.Optional[t.AnyStr]:645    r"""Edits the given text in the defined editor.  If an editor is given646    (should be the full path to the executable but the regular operating647    system search path is used for finding the executable) it overrides648    the detected editor.  Optionally, some environment variables can be649    used.  If the editor is closed without changes, `None` is returned.  In650    case a file is edited directly the return value is always `None` and651    `require_save` and `extension` are ignored.652 653    If the editor cannot be opened a :exc:`UsageError` is raised.654 655    Note for Windows: to simplify cross-platform usage, the newlines are656    automatically converted from POSIX to Windows and vice versa.  As such,657    the message here will have ``\n`` as newline markers.658 659    :param text: the text to edit.660    :param editor: optionally the editor to use.  Defaults to automatic661                   detection.662    :param env: environment variables to forward to the editor.663    :param require_save: if this is true, then not saving in the editor664                         will make the return value become `None`.665    :param extension: the extension to tell the editor about.  This defaults666                      to `.txt` but changing this might change syntax667                      highlighting.668    :param filename: if provided it will edit this file instead of the669                     provided text contents.  It will not use a temporary670                     file as an indirection in that case.671    """672    from ._termui_impl import Editor673 674    ed = Editor(editor=editor, env=env, require_save=require_save, extension=extension)675 676    if filename is None:677        return ed.edit(text)678 679    ed.edit_file(filename)680    return None681 682 683def launch(url: str, wait: bool = False, locate: bool = False) -> int:684    """This function launches the given URL (or filename) in the default685    viewer application for this file type.  If this is an executable, it686    might launch the executable in a new session.  The return value is687    the exit code of the launched application.  Usually, ``0`` indicates688    success.689 690    Examples::691 692        click.launch('https://click.palletsprojects.com/')693        click.launch('/my/downloaded/file', locate=True)694 695    .. versionadded:: 2.0696 697    :param url: URL or filename of the thing to launch.698    :param wait: Wait for the program to exit before returning. This699        only works if the launched program blocks. In particular,700        ``xdg-open`` on Linux does not block.701    :param locate: if this is set to `True` then instead of launching the702                   application associated with the URL it will attempt to703                   launch a file manager with the file located.  This704                   might have weird effects if the URL does not point to705                   the filesystem.706    """707    from ._termui_impl import open_url708 709    return open_url(url, wait=wait, locate=locate)710 711 712# If this is provided, getchar() calls into this instead.  This is used713# for unittesting purposes.714_getchar: t.Optional[t.Callable[[bool], str]] = None715 716 717def getchar(echo: bool = False) -> str:718    """Fetches a single character from the terminal and returns it.  This719    will always return a unicode character and under certain rare720    circumstances this might return more than one character.  The721    situations which more than one character is returned is when for722    whatever reason multiple characters end up in the terminal buffer or723    standard input was not actually a terminal.724 725    Note that this will always read from the terminal, even if something726    is piped into the standard input.727 728    Note for Windows: in rare cases when typing non-ASCII characters, this729    function might wait for a second character and then return both at once.730    This is because certain Unicode characters look like special-key markers.731 732    .. versionadded:: 2.0733 734    :param echo: if set to `True`, the character read will also show up on735                 the terminal.  The default is to not show it.736    """737    global _getchar738 739    if _getchar is None:740        from ._termui_impl import getchar as f741 742        _getchar = f743 744    return _getchar(echo)745 746 747def raw_terminal() -> t.ContextManager[int]:748    from ._termui_impl import raw_terminal as f749 750    return f()751 752 753def pause(info: t.Optional[str] = None, err: bool = False) -> None:754    """This command stops execution and waits for the user to press any755    key to continue.  This is similar to the Windows batch "pause"756    command.  If the program is not run through a terminal, this command757    will instead do nothing.758 759    .. versionadded:: 2.0760 761    .. versionadded:: 4.0762       Added the `err` parameter.763 764    :param info: The message to print before pausing. Defaults to765        ``"Press any key to continue..."``.766    :param err: if set to message goes to ``stderr`` instead of767                ``stdout``, the same as with echo.768    """769    if not isatty(sys.stdin) or not isatty(sys.stdout):770        return771 772    if info is None:773        info = _("Press any key to continue...")774 775    try:776        if info:777            echo(info, nl=False, err=err)778        try:779            getchar()780        except (KeyboardInterrupt, EOFError):781            pass782    finally:783        if info:784            echo(err=err)785 
codekingpro/portable-devtools · Team Ai