Skip to content

Instantly share code, notes, and snippets.

@seblin
Created March 20, 2018 19:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save seblin/7b77cfa1285b093b3c9e632d01f3f78b to your computer and use it in GitHub Desktop.
Save seblin/7b77cfa1285b093b3c9e632d01f3f78b to your computer and use it in GitHub Desktop.
# -*- coding: utf-8 -*-
# Copyright (c) 2018, Sebastian Linke
# Released under the Simplified BSD license
"""
Simple interface to get user input.
Examples:
>>> import easyinput
>>> easyinput.ask_input('Give me a number: ', int)
Give me a number: foo
Invalid value
Give me a number: 42
42
>>> easyinput.ask_input('Give me a number (1-10): ', int, 1, 10)
Give me a number (1-10): 42
Invalid value
Give me a number (1-10): 10
10
>>> easyinput.ask_choice(['Apple', 'Banana', 'Cherry'])
Choose an item:
[1] Apple
[2] Banana
[3] Cherry
Choice: 5
Invalid value
Choice: 2
2
>>> easyinput.ask_input('Want to proceed (Yes/No)? ', easyinput.yesno)
Want to proceed (Yes/No)? spam
Invalid value
Want to proceed (Yes/No)? yes
True
"""
from __future__ import print_function
__author__ = 'Sebastian Linke'
__version__ = '0.1'
__all__ = ['ask_choice', 'ask_input', 'yesno', 'yesno_false', 'yn', 'yn_false']
try:
# Python 2
input_func = raw_input
except NameError:
# Python 3
input_func = input
# Default messages
CHOOSE_TEXT = 'Choose an item:'
CHOICE_TEXT = 'Choice: '
ERROR_TEXT = 'Invalid value'
def ask_input(
prompt='', val_type=str, val_min=None, val_max=None, error=ERROR_TEXT,
error_prompt=''
):
"""
Print *prompt* and then read from STDIN. Return the result converted to
*val_type*.
*val_min* and *val_max* define the limits within which the input must be
given. Using ``None`` means unlimited input.
This function asks for input until a correct value is given. If the input
is invalid then it will print the given *error* instead of returning the
value and it will ask again by printing *error_prompt*. If *error_prompt*
is not given (e.g. empty string or ``None``) then it will reuse *prompt*
instead.
"""
while True:
try:
value = val_type(input_func(prompt))
return get_validated(value, val_min, val_max)
except Exception:
print(error + '\n')
if error_prompt:
prompt = error_prompt
def ask_choice(
items, text_before=CHOOSE_TEXT, text_after=CHOICE_TEXT, error=ERROR_TEXT
):
"""
Print numbered choices for *items*. Then read user's choice (an integer
between 1 and len(items)) from STDIN and return the result.
*text_before* and *text_after* are set around the numbered choices.
This function asks for input until a valid choice is given. If the input
is invalid then it will print the given *error* instead of returning the
choice and it will ask again by printing *text_after*. If *text_after* is
not given (e.g. empty string or ``None``) then it will reuse the numbered
choices instead.
"""
if not items:
raise ValueError('Need at least one item')
choices = '\n'.join(
'[{}] {}'.format(number, text)
for number, text in enumerate(items, 1)
)
prompt = '{}\n{}\n{}'.format(text_before, choices, text_after)
return ask_input(prompt, int, 1, len(items), error, text_after)
def get_yesno_converter(yes_values, no_values, default=True, lower=True):
"""
Return a converter meant to be used as ``val_type`` in ``ask_input()``.
The converter returns ``True`` if the input matches one of the *yes_values*
or ``False`` if the input matches one of the *no_values*. If the input is
an empty string then *default* is returned. Any other string will result in
a ``ValueError``.
The input is lower-cased by default. Setting *lower* to ``False`` means to
use the original input instead.
"""
def converter(value):
if not value:
return default
if lower:
value = value.lower()
if value in yes_values:
return True
if value in no_values:
return False
raise ValueError(ERROR_TEXT)
return converter
# Matches "yes" / "y" and "no" / "n" (default: True)
yesno = get_yesno_converter(['yes', 'y'], ['no', 'n'])
# Matches "yes" / "y" and "no" / "n" (default: False)
yesno_false = get_yesno_converter(['yes', 'y'], ['no', 'n'], False)
# Matches "y" and "n" (default: True)
yn = get_yesno_converter(['y'], ['n'])
# Matches "y" and "n" (default: False)
yn_false = get_yesno_converter(['y'], ['n'], False)
def get_validated(value, val_min, val_max):
"""
Return *value* if ``val_min <= value <= val_max``. Otherwise, raise
``ValueError`` instead of returning the value.
Use ``None`` to omit an upper or lower limit.
"""
if (
val_min is not None and value < val_min
or val_max is not None and value > val_max
):
raise ValueError(ERROR_TEXT)
return value
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment