3
\$\begingroup\$

As writing code to handle error-checked input or to present functions to execute to the user is boring and error-prone, I wrote a module to automate these tasks:

user_interface.py

def general_input(prompt="", type_=str, min_=None, max_=None,
                  max_length=None, set_=None, cap_sensitive=True,
                  help_=lambda: 0):
    """
    Takes care of input validation, by diplaying meaningful
    messages to the user in case of invalid input.

    @ prompt: The text to be shown to the user.
    @ type_: The type the input must be convertible to. (And will be converted to).
    @ max_length: The maximum length of the input.
    @ set_: The set of things the input must in. (This may be a `range` object or any iterable).
    @ cap_sensitive: If False the input will be lowercased. (Defult True)
    @ help_: What to do when the user types in `help`
    """
    while True:
        input_ = input(prompt)
        input_ = input_ if cap_sensitive else input_.lower()
        if input_ == "help":
            help_()
            continue
        try:
            if not type_:
                type_ = type(input_)
            input_ = type_(input_)
        except ValueError:
            print("Expected input of type {} but got {}".format(
                type_, type(input_)))
            continue
        if max_length and len(input_) > max_length:
            print("Input is too long, the max length allowed is {}".format(
                max_length))
            continue
        if set_ and input_ not in set_:
            # Fixing that the last item in ranges is exclusive (weird for normal users)
            to_print_set = None
            if type(set_) == range:
                to_print_set = range(min(set_), max(set_))
            print("Input is not in the set of possible inputs, enter one of {}".format(
                to_print_set or set_))
            continue
        return input_


def menu(functions):
    """
    Allows for easy presentation to the user of functions, also
    allowing him to get fast help.
    """
    functions = list(sorted(functions, key=lambda fun: fun.__name__))
    for index, func in enumerate(functions):
        print("{} - {}".format(
            index + 1, func.__name__))
    def help_():
        function_to_be_explained = general_input("Which function do you want help on? ",
                                type_=int, set_=range(0,len(functions))) - 1
        help(functions[function_to_be_explained])

    user_choice = general_input("Which function do you want to execute? (Type `help` for help). ",
                                type_=int, set_=range(0,len(functions)+1),
                                help_= help_) - 1
    print(functions[user_choice](eval(input("Comma separated args for `{}`: ".format(
          functions[user_choice].__name__))))); print()

def menu_for_module(module, private_shown=False):
    """
    Shows a menu containing all the functions in a module
    """
    priv_cond = lambda fun: fun.__name__.startswith('_') if not private_shown else False
    menu([i for i in module.__dict__.values() if type(i) == type(lambda:0) and not priv_cond(i)])
\$\endgroup\$
1
3
\$\begingroup\$

There's something in here that doesn't seem to make a lot of sense:

    try:
        if not type_:
            type_ = type(input_)
        input_ = type_(input_)
    except ValueError:
        print("Expected input of type {} but got {}".format(
            type_, type(input_)))
        continue

If type is falsy, get the type of the input and re-cast the input? That seems unnecessary. And in this case the whole type validation seems unnecessary. You could skip the validation of the specified type is falsy.

A very minor thing, the default start of a range is 0, so you could just skip that.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.