123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606 |
- import os
- import sys
- import struct
- import inspect
- import itertools
- from ._compat import raw_input, text_type, string_types, \
- isatty, strip_ansi, get_winterm_size, DEFAULT_COLUMNS, WIN
- from .utils import echo
- from .exceptions import Abort, UsageError
- from .types import convert_type, Choice, Path
- from .globals import resolve_color_default
- # The prompt functions to use. The doc tools currently override these
- # functions to customize how they work.
- visible_prompt_func = raw_input
- _ansi_colors = {
- 'black': 30,
- 'red': 31,
- 'green': 32,
- 'yellow': 33,
- 'blue': 34,
- 'magenta': 35,
- 'cyan': 36,
- 'white': 37,
- 'reset': 39,
- 'bright_black': 90,
- 'bright_red': 91,
- 'bright_green': 92,
- 'bright_yellow': 93,
- 'bright_blue': 94,
- 'bright_magenta': 95,
- 'bright_cyan': 96,
- 'bright_white': 97,
- }
- _ansi_reset_all = '\033[0m'
- def hidden_prompt_func(prompt):
- import getpass
- return getpass.getpass(prompt)
- def _build_prompt(text, suffix, show_default=False, default=None, show_choices=True, type=None):
- prompt = text
- if type is not None and show_choices and isinstance(type, Choice):
- prompt += ' (' + ", ".join(map(str, type.choices)) + ')'
- if default is not None and show_default:
- prompt = '%s [%s]' % (prompt, default)
- return prompt + suffix
- def prompt(text, default=None, hide_input=False, confirmation_prompt=False,
- type=None, value_proc=None, prompt_suffix=': ', show_default=True,
- err=False, show_choices=True):
- """Prompts a user for input. This is a convenience function that can
- be used to prompt a user for input later.
- If the user aborts the input by sending a interrupt signal, this
- function will catch it and raise a :exc:`Abort` exception.
- .. versionadded:: 7.0
- Added the show_choices parameter.
- .. versionadded:: 6.0
- Added unicode support for cmd.exe on Windows.
- .. versionadded:: 4.0
- Added the `err` parameter.
- :param text: the text to show for the prompt.
- :param default: the default value to use if no input happens. If this
- is not given it will prompt until it's aborted.
- :param hide_input: if this is set to true then the input value will
- be hidden.
- :param confirmation_prompt: asks for confirmation for the value.
- :param type: the type to use to check the value against.
- :param value_proc: if this parameter is provided it's a function that
- is invoked instead of the type conversion to
- convert a value.
- :param prompt_suffix: a suffix that should be added to the prompt.
- :param show_default: shows or hides the default value in the prompt.
- :param err: if set to true the file defaults to ``stderr`` instead of
- ``stdout``, the same as with echo.
- :param show_choices: Show or hide choices if the passed type is a Choice.
- For example if type is a Choice of either day or week,
- show_choices is true and text is "Group by" then the
- prompt will be "Group by (day, week): ".
- """
- result = None
- def prompt_func(text):
- f = hide_input and hidden_prompt_func or visible_prompt_func
- try:
- # Write the prompt separately so that we get nice
- # coloring through colorama on Windows
- echo(text, nl=False, err=err)
- return f('')
- except (KeyboardInterrupt, EOFError):
- # getpass doesn't print a newline if the user aborts input with ^C.
- # Allegedly this behavior is inherited from getpass(3).
- # A doc bug has been filed at https://bugs.python.org/issue24711
- if hide_input:
- echo(None, err=err)
- raise Abort()
- if value_proc is None:
- value_proc = convert_type(type, default)
- prompt = _build_prompt(text, prompt_suffix, show_default, default, show_choices, type)
- while 1:
- while 1:
- value = prompt_func(prompt)
- if value:
- break
- elif default is not None:
- if isinstance(value_proc, Path):
- # validate Path default value(exists, dir_okay etc.)
- value = default
- break
- return default
- try:
- result = value_proc(value)
- except UsageError as e:
- echo('Error: %s' % e.message, err=err)
- continue
- if not confirmation_prompt:
- return result
- while 1:
- value2 = prompt_func('Repeat for confirmation: ')
- if value2:
- break
- if value == value2:
- return result
- echo('Error: the two entered values do not match', err=err)
- def confirm(text, default=False, abort=False, prompt_suffix=': ',
- show_default=True, err=False):
- """Prompts for confirmation (yes/no question).
- If the user aborts the input by sending a interrupt signal this
- function will catch it and raise a :exc:`Abort` exception.
- .. versionadded:: 4.0
- Added the `err` parameter.
- :param text: the question to ask.
- :param default: the default for the prompt.
- :param abort: if this is set to `True` a negative answer aborts the
- exception by raising :exc:`Abort`.
- :param prompt_suffix: a suffix that should be added to the prompt.
- :param show_default: shows or hides the default value in the prompt.
- :param err: if set to true the file defaults to ``stderr`` instead of
- ``stdout``, the same as with echo.
- """
- prompt = _build_prompt(text, prompt_suffix, show_default,
- default and 'Y/n' or 'y/N')
- while 1:
- try:
- # Write the prompt separately so that we get nice
- # coloring through colorama on Windows
- echo(prompt, nl=False, err=err)
- value = visible_prompt_func('').lower().strip()
- except (KeyboardInterrupt, EOFError):
- raise Abort()
- if value in ('y', 'yes'):
- rv = True
- elif value in ('n', 'no'):
- rv = False
- elif value == '':
- rv = default
- else:
- echo('Error: invalid input', err=err)
- continue
- break
- if abort and not rv:
- raise Abort()
- return rv
- def get_terminal_size():
- """Returns the current size of the terminal as tuple in the form
- ``(width, height)`` in columns and rows.
- """
- # If shutil has get_terminal_size() (Python 3.3 and later) use that
- if sys.version_info >= (3, 3):
- import shutil
- shutil_get_terminal_size = getattr(shutil, 'get_terminal_size', None)
- if shutil_get_terminal_size:
- sz = shutil_get_terminal_size()
- return sz.columns, sz.lines
- # We provide a sensible default for get_winterm_size() when being invoked
- # inside a subprocess. Without this, it would not provide a useful input.
- if get_winterm_size is not None:
- size = get_winterm_size()
- if size == (0, 0):
- return (79, 24)
- else:
- return size
- def ioctl_gwinsz(fd):
- try:
- import fcntl
- import termios
- cr = struct.unpack(
- 'hh', fcntl.ioctl(fd, termios.TIOCGWINSZ, '1234'))
- except Exception:
- return
- return cr
- cr = ioctl_gwinsz(0) or ioctl_gwinsz(1) or ioctl_gwinsz(2)
- if not cr:
- try:
- fd = os.open(os.ctermid(), os.O_RDONLY)
- try:
- cr = ioctl_gwinsz(fd)
- finally:
- os.close(fd)
- except Exception:
- pass
- if not cr or not cr[0] or not cr[1]:
- cr = (os.environ.get('LINES', 25),
- os.environ.get('COLUMNS', DEFAULT_COLUMNS))
- return int(cr[1]), int(cr[0])
- def echo_via_pager(text_or_generator, color=None):
- """This function takes a text and shows it via an environment specific
- pager on stdout.
- .. versionchanged:: 3.0
- Added the `color` flag.
- :param text_or_generator: the text to page, or alternatively, a
- generator emitting the text to page.
- :param color: controls if the pager supports ANSI colors or not. The
- default is autodetection.
- """
- color = resolve_color_default(color)
- if inspect.isgeneratorfunction(text_or_generator):
- i = text_or_generator()
- elif isinstance(text_or_generator, string_types):
- i = [text_or_generator]
- else:
- i = iter(text_or_generator)
- # convert every element of i to a text type if necessary
- text_generator = (el if isinstance(el, string_types) else text_type(el)
- for el in i)
- from ._termui_impl import pager
- return pager(itertools.chain(text_generator, "\n"), color)
- def progressbar(iterable=None, length=None, label=None, show_eta=True,
- show_percent=None, show_pos=False,
- item_show_func=None, fill_char='#', empty_char='-',
- bar_template='%(label)s [%(bar)s] %(info)s',
- info_sep=' ', width=36, file=None, color=None):
- """This function creates an iterable context manager that can be used
- to iterate over something while showing a progress bar. It will
- either iterate over the `iterable` or `length` items (that are counted
- up). While iteration happens, this function will print a rendered
- progress bar to the given `file` (defaults to stdout) and will attempt
- to calculate remaining time and more. By default, this progress bar
- will not be rendered if the file is not a terminal.
- The context manager creates the progress bar. When the context
- manager is entered the progress bar is already displayed. With every
- iteration over the progress bar, the iterable passed to the bar is
- advanced and the bar is updated. When the context manager exits,
- a newline is printed and the progress bar is finalized on screen.
- No printing must happen or the progress bar will be unintentionally
- destroyed.
- Example usage::
- with progressbar(items) as bar:
- for item in bar:
- do_something_with(item)
- Alternatively, if no iterable is specified, one can manually update the
- progress bar through the `update()` method instead of directly
- iterating over the progress bar. The update method accepts the number
- of steps to increment the bar with::
- with progressbar(length=chunks.total_bytes) as bar:
- for chunk in chunks:
- process_chunk(chunk)
- bar.update(chunks.bytes)
- .. versionadded:: 2.0
- .. versionadded:: 4.0
- Added the `color` parameter. Added a `update` method to the
- progressbar object.
- :param iterable: an iterable to iterate over. If not provided the length
- is required.
- :param length: the number of items to iterate over. By default the
- progressbar will attempt to ask the iterator about its
- length, which might or might not work. If an iterable is
- also provided this parameter can be used to override the
- length. If an iterable is not provided the progress bar
- will iterate over a range of that length.
- :param label: the label to show next to the progress bar.
- :param show_eta: enables or disables the estimated time display. This is
- automatically disabled if the length cannot be
- determined.
- :param show_percent: enables or disables the percentage display. The
- default is `True` if the iterable has a length or
- `False` if not.
- :param show_pos: enables or disables the absolute position display. The
- default is `False`.
- :param item_show_func: a function called with the current item which
- can return a string to show the current item
- next to the progress bar. Note that the current
- item can be `None`!
- :param fill_char: the character to use to show the filled part of the
- progress bar.
- :param empty_char: the character to use to show the non-filled part of
- the progress bar.
- :param bar_template: the format string to use as template for the bar.
- The parameters in it are ``label`` for the label,
- ``bar`` for the progress bar and ``info`` for the
- info section.
- :param info_sep: the separator between multiple info items (eta etc.)
- :param width: the width of the progress bar in characters, 0 means full
- terminal width
- :param file: the file to write to. If this is not a terminal then
- only the label is printed.
- :param color: controls if the terminal supports ANSI colors or not. The
- default is autodetection. This is only needed if ANSI
- codes are included anywhere in the progress bar output
- which is not the case by default.
- """
- from ._termui_impl import ProgressBar
- color = resolve_color_default(color)
- return ProgressBar(iterable=iterable, length=length, show_eta=show_eta,
- show_percent=show_percent, show_pos=show_pos,
- item_show_func=item_show_func, fill_char=fill_char,
- empty_char=empty_char, bar_template=bar_template,
- info_sep=info_sep, file=file, label=label,
- width=width, color=color)
- def clear():
- """Clears the terminal screen. This will have the effect of clearing
- the whole visible space of the terminal and moving the cursor to the
- top left. This does not do anything if not connected to a terminal.
- .. versionadded:: 2.0
- """
- if not isatty(sys.stdout):
- return
- # If we're on Windows and we don't have colorama available, then we
- # clear the screen by shelling out. Otherwise we can use an escape
- # sequence.
- if WIN:
- os.system('cls')
- else:
- sys.stdout.write('\033[2J\033[1;1H')
- def style(text, fg=None, bg=None, bold=None, dim=None, underline=None,
- blink=None, reverse=None, reset=True):
- """Styles a text with ANSI styles and returns the new string. By
- default the styling is self contained which means that at the end
- of the string a reset code is issued. This can be prevented by
- passing ``reset=False``.
- Examples::
- click.echo(click.style('Hello World!', fg='green'))
- click.echo(click.style('ATTENTION!', blink=True))
- click.echo(click.style('Some things', reverse=True, fg='cyan'))
- Supported color names:
- * ``black`` (might be a gray)
- * ``red``
- * ``green``
- * ``yellow`` (might be an orange)
- * ``blue``
- * ``magenta``
- * ``cyan``
- * ``white`` (might be light gray)
- * ``bright_black``
- * ``bright_red``
- * ``bright_green``
- * ``bright_yellow``
- * ``bright_blue``
- * ``bright_magenta``
- * ``bright_cyan``
- * ``bright_white``
- * ``reset`` (reset the color code only)
- .. versionadded:: 2.0
- .. versionadded:: 7.0
- Added support for bright colors.
- :param text: the string to style with ansi codes.
- :param fg: if provided this will become the foreground color.
- :param bg: if provided this will become the background color.
- :param bold: if provided this will enable or disable bold mode.
- :param dim: if provided this will enable or disable dim mode. This is
- badly supported.
- :param underline: if provided this will enable or disable underline.
- :param blink: if provided this will enable or disable blinking.
- :param reverse: if provided this will enable or disable inverse
- rendering (foreground becomes background and the
- other way round).
- :param reset: by default a reset-all code is added at the end of the
- string which means that styles do not carry over. This
- can be disabled to compose styles.
- """
- bits = []
- if fg:
- try:
- bits.append('\033[%dm' % (_ansi_colors[fg]))
- except KeyError:
- raise TypeError('Unknown color %r' % fg)
- if bg:
- try:
- bits.append('\033[%dm' % (_ansi_colors[bg] + 10))
- except KeyError:
- raise TypeError('Unknown color %r' % bg)
- if bold is not None:
- bits.append('\033[%dm' % (1 if bold else 22))
- if dim is not None:
- bits.append('\033[%dm' % (2 if dim else 22))
- if underline is not None:
- bits.append('\033[%dm' % (4 if underline else 24))
- if blink is not None:
- bits.append('\033[%dm' % (5 if blink else 25))
- if reverse is not None:
- bits.append('\033[%dm' % (7 if reverse else 27))
- bits.append(text)
- if reset:
- bits.append(_ansi_reset_all)
- return ''.join(bits)
- def unstyle(text):
- """Removes ANSI styling information from a string. Usually it's not
- necessary to use this function as Click's echo function will
- automatically remove styling if necessary.
- .. versionadded:: 2.0
- :param text: the text to remove style information from.
- """
- return strip_ansi(text)
- def secho(message=None, file=None, nl=True, err=False, color=None, **styles):
- """This function combines :func:`echo` and :func:`style` into one
- call. As such the following two calls are the same::
- click.secho('Hello World!', fg='green')
- click.echo(click.style('Hello World!', fg='green'))
- All keyword arguments are forwarded to the underlying functions
- depending on which one they go with.
- .. versionadded:: 2.0
- """
- if message is not None:
- message = style(message, **styles)
- return echo(message, file=file, nl=nl, err=err, color=color)
- def edit(text=None, editor=None, env=None, require_save=True,
- extension='.txt', filename=None):
- r"""Edits the given text in the defined editor. If an editor is given
- (should be the full path to the executable but the regular operating
- system search path is used for finding the executable) it overrides
- the detected editor. Optionally, some environment variables can be
- used. If the editor is closed without changes, `None` is returned. In
- case a file is edited directly the return value is always `None` and
- `require_save` and `extension` are ignored.
- If the editor cannot be opened a :exc:`UsageError` is raised.
- Note for Windows: to simplify cross-platform usage, the newlines are
- automatically converted from POSIX to Windows and vice versa. As such,
- the message here will have ``\n`` as newline markers.
- :param text: the text to edit.
- :param editor: optionally the editor to use. Defaults to automatic
- detection.
- :param env: environment variables to forward to the editor.
- :param require_save: if this is true, then not saving in the editor
- will make the return value become `None`.
- :param extension: the extension to tell the editor about. This defaults
- to `.txt` but changing this might change syntax
- highlighting.
- :param filename: if provided it will edit this file instead of the
- provided text contents. It will not use a temporary
- file as an indirection in that case.
- """
- from ._termui_impl import Editor
- editor = Editor(editor=editor, env=env, require_save=require_save,
- extension=extension)
- if filename is None:
- return editor.edit(text)
- editor.edit_file(filename)
- def launch(url, wait=False, locate=False):
- """This function launches the given URL (or filename) in the default
- viewer application for this file type. If this is an executable, it
- might launch the executable in a new session. The return value is
- the exit code of the launched application. Usually, ``0`` indicates
- success.
- Examples::
- click.launch('https://click.palletsprojects.com/')
- click.launch('/my/downloaded/file', locate=True)
- .. versionadded:: 2.0
- :param url: URL or filename of the thing to launch.
- :param wait: waits for the program to stop.
- :param locate: if this is set to `True` then instead of launching the
- application associated with the URL it will attempt to
- launch a file manager with the file located. This
- might have weird effects if the URL does not point to
- the filesystem.
- """
- from ._termui_impl import open_url
- return open_url(url, wait=wait, locate=locate)
- # If this is provided, getchar() calls into this instead. This is used
- # for unittesting purposes.
- _getchar = None
- def getchar(echo=False):
- """Fetches a single character from the terminal and returns it. This
- will always return a unicode character and under certain rare
- circumstances this might return more than one character. The
- situations which more than one character is returned is when for
- whatever reason multiple characters end up in the terminal buffer or
- standard input was not actually a terminal.
- Note that this will always read from the terminal, even if something
- is piped into the standard input.
- Note for Windows: in rare cases when typing non-ASCII characters, this
- function might wait for a second character and then return both at once.
- This is because certain Unicode characters look like special-key markers.
- .. versionadded:: 2.0
- :param echo: if set to `True`, the character read will also show up on
- the terminal. The default is to not show it.
- """
- f = _getchar
- if f is None:
- from ._termui_impl import getchar as f
- return f(echo)
- def raw_terminal():
- from ._termui_impl import raw_terminal as f
- return f()
- def pause(info='Press any key to continue ...', err=False):
- """This command stops execution and waits for the user to press any
- key to continue. This is similar to the Windows batch "pause"
- command. If the program is not run through a terminal, this command
- will instead do nothing.
- .. versionadded:: 2.0
- .. versionadded:: 4.0
- Added the `err` parameter.
- :param info: the info string to print before pausing.
- :param err: if set to message goes to ``stderr`` instead of
- ``stdout``, the same as with echo.
- """
- if not isatty(sys.stdin) or not isatty(sys.stdout):
- return
- try:
- if info:
- echo(info, nl=False, err=err)
- try:
- getchar()
- except (KeyboardInterrupt, EOFError):
- pass
- finally:
- if info:
- echo(err=err)
|