"""Functions for terminal input.
.. versionadded:: 0.3.0
.. versionchanged:: 0.16.0
The ``exc_on_cancel`` parameters of the functions ``read``, ``select``,
``yesno``, and ``menu`` now default to ``None``. This means that the value
of the new module level attribute ``exception_on_cancel`` will be used. As
this defaults to ``True`` the default behavior of the functions has changed.
To restore the old behavior set ``exception_on_cancel``
to ``False``.
The terminal must support `ANSI escape sequences
<https://en.wikipedia.org/wiki/ANSI_escape_code>`_.
"""
import math
from getpass import getpass
from .utils import check_type
__all__ = ['exception_on_cancel', 'check_float', 'check_int', 'check_str',
'menu', 'read', 'select', 'yesno']
exception_on_cancel = True #: Default value for ``exc_on_cancel`` parameters.
[docs]def read(prompt='', default=None, check=None, exc_on_cancel=None,
*, noecho=False):
"""Read a line of input and check if it is allowed.
If the input is not allowed the prompt will be shown again. The
input can be cancelled with EOF (``^D``).
If the ``check`` parameter is set to ``None`` any input is allowed,
else it must be a ``callable`` that takes a string as a parameter
and returns the (converted) input value or raises
:exc:`ValueError` if the input is not allowed.
There are 3 predefined check functions in this module:
:func:`check_str`, :func:`check_int` and :func:`check_float`.
>>> read('Number: ', default='42')
Number: 21
'21'
>>> read('Number: ', default='42', check=int)
Number:
42
:param str prompt: the prompt
:param default: default value that will be used if no input is provided
:type default: str or None
:param check: the check parameter (see above)
:type check: callable(str) or None
:param bool exc_on_cancel: if ``True`` an EOF will cause an Exception;
if ``None`` the value of ``exception_on_cancel``
will be used
:param bool noecho: if set to ``True`` :func:`~getpass.getpass` will be used
instead of :func:`input`
:return: (converted) input value or ``None`` if input was cancelled and
``exc_on_cancel=False``
:rtype: str or return-type of the ``check`` callable or None
:raises EOFError: if input was cancelled and ``exc_on_cancel=True``
:raises TypeError: if ``default`` is not of type ``str``
.. versionchanged:: 0.15.0 Add parameter ``noecho``
"""
default is not None and check_type(default, str, 'default')
exc_on_cancel = (exception_on_cancel
if exc_on_cancel is None else exc_on_cancel)
value = None
lines = prompt.split('\n')
line = lines[-1]
if len(lines) > 1:
print('\n'.join(lines[:-1]))
print('\n\x1b[A\x1b[s', end='', flush=True) # CHANGELOG for version 0.7.2
while True:
try:
if noecho:
a = getpass(prompt) or default
else:
a = input(line) or default
except EOFError:
print()
if exc_on_cancel:
raise
break
if a is not None:
if check:
try:
value = check(a)
break
except ValueError:
pass
else:
value = a
break
print('\x1b[u\x1b[J', end='', flush=True)
return value
[docs]def yesno(prompt, yesno, exc_on_cancel=None):
"""Show yes/no input prompt.
If the typed character (case-insensitive) is not allowed
(i.e. not in ``yesno``) the prompt will be shown again.
The input can be cancelled with EOF (``^D``).
>>> yesno('Exit program?', 'yN')
Exit program? [yN] Y
True
:param str prompt: the prompt
:param str yesno: the characters for ``yes`` (index 0) and ``no`` (index 1);
if one is upper and the other lower case, the upper case
character is the default
:param bool exc_on_cancel: if ``True`` an EOF will cause an Exception;
if ``None`` the value of ``exception_on_cancel``
will be used
:return: ``True`` for ``yes``, ``False`` for ``no``, ``None`` if cancelled
and ``exc_on_cancel=False``
:rtype: bool or None
:raises EOFError: if input was cancelled and ``exc_on_cancel=True``
:raises TypeError: if ``yesno`` is not of type ``str``
:raises ValueError: if ``yesno`` is not of length 2
"""
check_type(yesno, str, 'yesno argument')
if len(yesno) != 2:
raise ValueError('argument yesno must be a string of to 2 characters')
if yesno[0].isupper() and yesno[1].islower():
default = yesno[0]
elif yesno[0].islower() and yesno[1].isupper():
default = yesno[1]
else:
default = None
def f(s):
s1 = s.lower()
s2 = yesno.lower()
if s1 not in s2:
raise ValueError
return s1 == s2[0]
return read('%s [%s%s] ' % (prompt, *yesno), default=default, check=f,
exc_on_cancel=exc_on_cancel)
[docs]def select(prompt, options, default=None, case_sensitive=False,
exc_on_cancel=None):
"""Show an input with selectable options.
If the input is not allowed the prompt will be shown again. The
input can be cancelled with EOF (``^D``).
>>> select('Select: [T]op, [B]ottom, [L]eft, [R]ight > ', 'TBLR')
Select: [T]op, [B]ottom, [L]eft, [R]ight > b
1
:param str prompt: the prompt
:param options: the options; if all options are only 1 character a string
can be used, else a tuple of strings
:type options: str or tuple
:param default: default value that will be used if no input is provided
:type default: str or None
:param bool case_sensitive: if ``False`` case of typed characters will
be ignored
:param bool exc_on_cancel: if ``True`` an EOF will cause an Exception;
if ``None`` the value of ``exception_on_cancel``
will be used
:return: index of the selected option in ``options`` or None if cancelled
and ``exc_on_cancel=False``
:rtype: int or None
:raises EOFError: if input was cancelled and ``exc_on_cancel=True``
:raises TypeError: if ``options`` is not str or tuple of strings
.. versionadded:: 0.4.0
"""
check_type(options, (str, tuple), 'options')
if not case_sensitive:
options = tuple(map(str.lower, options))
def f(s):
if not case_sensitive:
s = s.lower()
if s not in options:
raise ValueError
return options.index(s)
return read(prompt, default=default, check=f, exc_on_cancel=exc_on_cancel)
[docs]def check_str(min_len=0, max_len=None, chars=None, negate=False):
"""Return a check function for strings.
The returned function can be used as the ``check`` argument in the
:func:`read` function.
:param int min_len: minimal length of the string
:param int max_len: maximal length of the string (``None`` means no limit)
:param str chars: allowed characters
:param bool negate: if ``True`` only characters not in ``chars``
are allowed
.. versionadded:: 0.6.0
"""
def f(s):
nonlocal min_len
if min_len < 0:
min_len = 0
if max_len is None:
len_ok = min_len <= len(s)
else:
len_ok = min_len <= len(s) <= max_len
if len_ok:
if chars:
x = (c in chars for c in s)
if negate and not any(x) or all(x):
return s
else:
return s
raise ValueError
return f
[docs]def check_int(predicate):
"""Return a check function for integers.
The returned function can be used as the ``check`` argument in the
:func:`read` function.
:param predicate: predicate function
:type predicate: callable(int)
.. versionadded:: 0.6.0
"""
def f(s):
i = int(s)
if predicate(i):
return i
raise ValueError
return f
[docs]def check_float(predicate):
"""Return a check function for floats.
The returned function can be used as the ``check`` argument in the
:func:`read` function.
:param predicate: predicate function
:type predicate: callable(float)
.. versionadded:: 0.6.0
"""
def f(s):
i = float(s)
if predicate(i):
return i
raise ValueError
return f