Writing clean, flexible, and easy to maintain user input prompts Writing clean, flexible, and easy to maintain user input prompts python-3.x python-3.x

Writing clean, flexible, and easy to maintain user input prompts


I once wrote a function for something similar. The explanation is in the doc-string:

def xory(question = "", setx = ["yes"], sety = ["no"], setz = [], strict = False):    """xory([question][, setx][, sety][, setz][, strict]) -> string    Asks question.  If the answer is equal to one of the elements in setx,    returns True.  If the answer is equal to one of the elements in sety,    returns False.  If the answer is equal to one of the elements in setz,    returns the element in setz that answer is equal to.  If the answer is    not in any of the sets, reasks the question.  Strict controls whether    the answer is case-sensitive.  If show is True, an indication of the    acceptable answers will be displayed next to the prompt."""    if isinstance(setx, str):        setx = [setx]    if isinstance(sety, str):        sety = [sety]    if isinstance(setz, str):        setz = [setz]    if (setx[0])[0] != (sety[0])[0]:        setx = [(setx[0])[0]] + setx        sety = [(sety[0])[0]] + sety    question = question.strip(" ") + " "    while True:        if show:            shows = "[%s/%s] " % (setx[0], sety[0])        else:            shows = ""        user_input = raw_input(question + shows)        for y in [setx, sety, setz]:            for x in y:                if (user_input == x) or ((not strict) and (user_input.lower() == x.lower())):                    if y is setx:                        return True                    elif y is sety:                        return False                    else:                        return x        question = ""        show = True

Examples:

>>> response = xory("1 or 0?", ["1", "one", "uno"], ["0", "zero", "null"], ["quit", "exit"])1 or 0? x[1/0] eante[1/0] uno>>> print(response)True>>> response = xory("Is that so?")Is that so? Who knows?[y/n] no>>> print(response)False>>> response = xory("Will you do it?", setz=["quit", "exit", "restart"])Will you do it? hm[y/n] quit>>> print(response)quit


I'd advise to write a library that contains a number of very clearly defined building blocks, each one as small and light-weight as possible, without too many assumptions baked in about how you're going to put the pieces together.

That is, I'd include one function that does the loop, but instead of passing in a set of rules, I'd allow for exactly one function to be passed in that either returns a value (if a valid input was given and after converting it in any way necessary) or raises a ValueError if the input wasn't usable. Other building blocks would implement certain checks or transformations (like resolution of 'y' and 'n' into boolean values).

This way, you would leave it completely up to the user to assemble the stuff in a way suitable for the use case.

# library:def prompt(prompt, default, postprocess):    value = input('{} ({}): '.format(prompt, default)) or default    try:        return postprocess(value)    except ValueError:        continuedef check_lower(value):    if not value.islower():        raise ValueError()def to_bool(value):    return value in 'yes'# using the library:def postprocess(value):    check_lower(value)    return to_bool(value)prompt('Really?', 'n', postprocess)


I would create a prompt function as such:

def prompt(prompt, default=None, rules=[]):    while True:        response = input(prompt)        if response:            valid = [rule(response) for rule in rules]            if not(False in valid):                return response            else:                print('Invalid input')        else:            return default

You could then create different validation functions such as

def filterValidEmail(string):    if '@' in string:        if '.' in string.split('@')[1]:            return True        else:            return False    else:        return False

And call these functions like so:

prompt('What is your email? ', rules=[filterValidEmail])

You could also tweak this so that you can tell the user what verification they failed or disallow blank inputs.