import inspect
import io
import itertools
import sys
import typing
as t
from gettext
import gettext
as _
from ._compat
import isatty
from ._compat
import strip_ansi
from .exceptions
import Abort
from .exceptions
import UsageError
from .globals
import resolve_color_default
from .types
import Choice
from .types
import convert_type
from .types
import ParamType
from .utils
import echo
from .utils
import LazyFile
if t.TYPE_CHECKING:
from ._termui_impl
import ProgressBar
V = t.TypeVar(
"V")
# The prompt functions to use. The doc tools currently override these
# functions to customize how they work.
visible_prompt_func: t.Callable[[str], str] = 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: str) -> str:
import getpass
return getpass.getpass(prompt)
def _build_prompt(
text: str,
suffix: str,
show_default: bool =
False,
default: t.Optional[t.Any] =
None,
show_choices: bool =
True,
type: t.Optional[ParamType] =
None,
) -> str:
prompt = text
if type
is not None and show_choices
and isinstance(type, Choice):
prompt += f
" ({', '.join(map(str, type.choices))})"
if default
is not None and show_default:
prompt = f
"{prompt} [{_format_default(default)}]"
return f
"{prompt}{suffix}"
def _format_default(default: t.Any) -> t.Any:
if isinstance(default, (io.IOBase, LazyFile))
and hasattr(default,
"name"):
return default.name
return default
def prompt(
text: str,
default: t.Optional[t.Any] =
None,
hide_input: bool =
False,
confirmation_prompt: t.Union[bool, str] =
False,
type: t.Optional[t.Union[ParamType, t.Any]] =
None,
value_proc: t.Optional[t.Callable[[str], t.Any]] =
None,
prompt_suffix: str =
": ",
show_default: bool =
True,
err: bool =
False,
show_choices: bool =
True,
) -> t.Any:
"""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 an interrupt signal, this
function will catch it
and raise a :exc:`Abort` exception.
: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: Prompt a second time to confirm the
value. Can be set to a string instead of ``
True`` to customize
the message.
: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): ".
.. versionadded:: 8.0
``confirmation_prompt`` can be a custom string.
.. 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.
"""
def prompt_func(text: str) -> str:
f = hidden_prompt_func
if hide_input
else visible_prompt_func
try:
# Write the prompt separately so that we get nice
# coloring through colorama on Windows
echo(text.rstrip(
" "), nl=
False, err=err)
# Echo a space to stdout to work around an issue where
# readline causes backspace to clear the whole line.
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()
from None
if value_proc
is None:
value_proc = convert_type(type, default)
prompt = _build_prompt(
text, prompt_suffix, show_default, default, show_choices, type
)
if confirmation_prompt:
if confirmation_prompt
is True:
confirmation_prompt = _(
"Repeat for confirmation")
confirmation_prompt = _build_prompt(confirmation_prompt, prompt_suffix)
while True:
while True:
value = prompt_func(prompt)
if value:
break
elif default
is not None:
value = default
break
try:
result = value_proc(value)
except UsageError
as e:
if hide_input:
echo(_(
"Error: The value you entered was invalid."), err=err)
else:
echo(_(
"Error: {e.message}").format(e=e), err=err)
# noqa: B306
continue
if not confirmation_prompt:
return result
while True:
value2 = prompt_func(confirmation_prompt)
is_empty =
not value
and not value2
if value2
or is_empty:
break
if value == value2:
return result
echo(_(
"Error: The two entered values do not match."), err=err)
def confirm(
text: str,
default: t.Optional[bool] =
False,
abort: bool =
False,
prompt_suffix: str =
": ",
show_default: bool =
True,
err: bool =
False,
) -> bool:
"""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.
:param text: the question to ask.
:param default: The default value to use when no input
is given.
If
``
None``, repeat until input
is given.
: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.
.. versionchanged:: 8.0
Repeat until input
is given
if ``default``
is ``
None``.
.. versionadded:: 4.0
Added the ``err`` parameter.
"""
prompt = _build_prompt(
text,
prompt_suffix,
show_default,
"y/n" if default
is None else (
"Y/n" if default
else "y/N"),
)
while True:
try:
# Write the prompt separately so that we get nice
# coloring through colorama on Windows
echo(prompt.rstrip(
" "), nl=
False, err=err)
# Echo a space to stdout to work around an issue where
# readline causes backspace to clear the whole line.
value = visible_prompt_func(
" ").lower().strip()
except (KeyboardInterrupt, EOFError):
raise Abort()
from None
if value
in (
"y",
"yes"):
rv =
True
elif value
in (
"n",
"no"):
rv =
False
elif default
is not None and value ==
"":
rv = default
else:
echo(_(
"Error: invalid input"), err=err)
continue
break
if abort
and not rv:
raise Abort()
return rv
def echo_via_pager(
text_or_generator: t.Union[t.Iterable[str], t.Callable[[], t.Iterable[str]], str],
color: t.Optional[bool] =
None,
) ->
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 = t.cast(t.Callable[[], t.Iterable[str]], text_or_generator)()
elif isinstance(text_or_generator, str):
i = [text_or_generator]
else:
i = iter(t.cast(t.Iterable[str], text_or_generator))
# convert every element of i to a text type if necessary
text_generator = (el
if isinstance(el, str)
else str(el)
for el
in i)
from ._termui_impl
import pager
return pager(itertools.chain(text_generator,
"\n"), color)
def progressbar(
iterable: t.Optional[t.Iterable[V]] =
None,
length: t.Optional[int] =
None,
label: t.Optional[str] =
None,
show_eta: bool =
True,
show_percent: t.Optional[bool] =
None,
show_pos: bool =
False,
item_show_func: t.Optional[t.Callable[[t.Optional[V]], t.Optional[str]]] =
None,
fill_char: str =
"#",
empty_char: str =
"-",
bar_template: str =
"%(label)s [%(bar)s] %(info)s",
info_sep: str =
" ",
width: int = 36,
file: t.Optional[t.TextIO] =
None,
color: t.Optional[bool] =
None,
update_min_steps: int = 1,
) ->
"ProgressBar[V]":
"""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 created.
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.
Note: The progress bar
is currently designed
for use cases where the
total progress can be expected to take at least several seconds.
Because of this, the ProgressBar
class object won
't display
progress that
is considered too fast,
and progress where the time
between steps
is less than a second.
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)
The ``update()`` method also takes an optional value specifying the
``current_item`` at the new position. This
is useful when used
together
with ``item_show_func`` to customize the output
for each
manual step::
with click.progressbar(
length=total_size,
label=
'Unzipping archive',
item_show_func=
lambda a: a.filename
)
as bar:
for archive
in zip_file:
archive.extract()
bar.update(archive.size, archive)
: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 next to the progress bar.
If the
function returns ``
None`` nothing
is shown. The current item can
be ``
None``, such
as when entering
and exiting the bar.
: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.
:param update_min_steps: Render only when this many updates have
completed. This allows tuning
for very fast iterators.
.. versionchanged:: 8.0
Output
is shown even
if execution time
is less than 0.5 seconds.
.. versionchanged:: 8.0
``item_show_func`` shows the current item,
not the previous one.
.. versionchanged:: 8.0
Labels are echoed
if the output
is not a TTY. Reverts a change
in 7.0 that removed all output.
.. versionadded:: 8.0
Added the ``update_min_steps`` parameter.
.. versionchanged:: 4.0
Added the ``color`` parameter. Added the ``update`` method to
the object.
.. versionadded:: 2.0
"""
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,
update_min_steps=update_min_steps,
)
def clear() ->
None:
"""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
# ANSI escape \033[2J clears the screen, \033[1;1H moves the cursor
echo(
"\033[2J\033[1;1H", nl=
False)
def _interpret_color(
color: t.Union[int, t.Tuple[int, int, int], str], offset: int = 0
) -> str:
if isinstance(color, int):
return f
"{38 + offset};5;{color:d}"
if isinstance(color, (tuple, list)):
r, g, b = color
return f
"{38 + offset};2;{r:d};{g:d};{b:d}"
return str(_ansi_colors[color] + offset)
def style(
text: t.Any,
fg: t.Optional[t.Union[int, t.Tuple[int, int, int], str]] =
None,
bg: t.Optional[t.Union[int, t.Tuple[int, int, int], str]] =
None,
bold: t.Optional[bool] =
None,
dim: t.Optional[bool] =
None,
underline: t.Optional[bool] =
None,
overline: t.Optional[bool] =
None,
italic: t.Optional[bool] =
None,
blink: t.Optional[bool] =
None,
reverse: t.Optional[bool] =
None,
strikethrough: t.Optional[bool] =
None,
reset: bool =
True,
) -> str:
"""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'))
click.echo(click.style(
'More colors', fg=(255, 12, 128), bg=117))
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)
If the terminal supports it, color may also be specified
as:
- An integer
in the interval [0, 255]. The terminal must support
8-bit/256-color mode.
- An RGB tuple of three integers
in [0, 255]. The terminal must
support 24-bit/true-color mode.
See
https://en.wikipedia.org/wiki/ANSI_color and
https://gist.github.com/XVilka/8346728 for more information.
: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 overline:
if provided this will enable
or disable overline.
:param italic:
if provided this will enable
or disable italic.
: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 strikethrough:
if provided this will enable
or disable
striking through text.
: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.
.. versionchanged:: 8.0
A non-string ``message``
is converted to a string.
.. versionchanged:: 8.0
Added support
for 256
and RGB color codes.
.. versionchanged:: 8.0
Added the ``strikethrough``, ``italic``,
and ``overline``
parameters.
.. versionchanged:: 7.0
Added support
for bright colors.
.. versionadded:: 2.0
"""
if not isinstance(text, str):
text = str(text)
bits = []
if fg:
try:
bits.append(f
"\033[{_interpret_color(fg)}m")
except KeyError:
raise TypeError(f
"Unknown color {fg!r}")
from None
if bg:
try:
bits.append(f
"\033[{_interpret_color(bg, 10)}m")
except KeyError:
raise TypeError(f
"Unknown color {bg!r}")
from None
if bold
is not None:
bits.append(f
"\033[{1 if bold else 22}m")
if dim
is not None:
bits.append(f
"\033[{2 if dim else 22}m")
if underline
is not None:
bits.append(f
"\033[{4 if underline else 24}m")
if overline
is not None:
bits.append(f
"\033[{53 if overline else 55}m")
if italic
is not None:
bits.append(f
"\033[{3 if italic else 23}m")
if blink
is not None:
bits.append(f
"\033[{5 if blink else 25}m")
if reverse
is not None:
bits.append(f
"\033[{7 if reverse else 27}m")
if strikethrough
is not None:
bits.append(f
"\033[{9 if strikethrough else 29}m")
bits.append(text)
if reset:
bits.append(_ansi_reset_all)
return "".join(bits)
def unstyle(text: str) -> str:
"""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: t.Optional[t.Any] =
None,
file: t.Optional[t.IO[t.AnyStr]] =
None,
nl: bool =
True,
err: bool =
False,
color: t.Optional[bool] =
None,
**styles: t.Any,
) ->
None:
"""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.
Non-string types will be converted to :
class:`str`. However,
:
class:`bytes` are passed directly to :meth:`echo` without applying
style.
If you want to style bytes that represent text, call
:meth:`bytes.decode` first.
.. versionchanged:: 8.0
A non-string ``message``
is converted to a string. Bytes are
passed through without style applied.
.. versionadded:: 2.0
"""
if message
is not None and not isinstance(message, (bytes, bytearray)):
message = style(message, **styles)
return echo(message, file=file, nl=nl, err=err, color=color)
def edit(
text: t.Optional[t.AnyStr] =
None,
editor: t.Optional[str] =
None,
env: t.Optional[t.Mapping[str, str]] =
None,
require_save: bool =
True,
extension: str =
".txt",
filename: t.Optional[str] =
None,
) -> t.Optional[t.AnyStr]:
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
ed = Editor(editor=editor, env=env, require_save=require_save, extension=extension)
if filename
is None:
return ed.edit(text)
ed.edit_file(filename)
return None
def launch(url: str, wait: bool =
False, locate: bool =
False) -> int:
"""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: Wait
for the program to exit before returning. This
only works
if the launched program blocks.
In particular,
``xdg-open`` on Linux does
not block.
: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: t.Optional[t.Callable[[bool], str]] =
None
def getchar(echo: bool =
False) -> str:
"""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.
"""
global _getchar
if _getchar
is None:
from ._termui_impl
import getchar
as f
_getchar = f
return _getchar(echo)
def raw_terminal() -> t.ContextManager[int]:
from ._termui_impl
import raw_terminal
as f
return f()
def pause(info: t.Optional[str] =
None, err: bool =
False) ->
None:
"""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 message to print before pausing. Defaults to
``
"Press any key to continue..."``.
: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
if info
is None:
info = _(
"Press any key to continue...")
try:
if info:
echo(info, nl=
False, err=err)
try:
getchar()
except (KeyboardInterrupt, EOFError):
pass
finally:
if info:
echo(err=err)