I am often asked a question asking users for input. I always just wrote my “as needed” tips in my main execution scripts. This is disgusting, and because I often ask you to enter the same input types of several scripts, a ton of my code is just copy / paste the contours of the queries. Here is what I have done in the past:
while True: username = input("Enter New Username: ") if ldap.search(username): print " [!] Username already taken." if not validator.validate_username(username): print " [!] Invalid Username." else: break
I would like to create something that can be called:
username = prompt(prompt="Enter New Username: ", default=None, rules=["user_does_not_exist", "valid_username"])
Then the query function looks like this:
def prompt(prompt, default, rules): while True: retval = input(prompt) if default and retval == "": break return default if not rule_match(retval, rules): continue break return retval def rule_match(value, rules): if "user_does_not_exist" in rules: if not user.user_exists(value): return False if "valid_username" in rules: if not validator.username(value): return False if "y_n_or_yes_no" in rules: if "ignore_case" in rules: if value.lower() not in ["y", "yes", "n", "no"]: return False else: if value not in ["y", "yes", "n", "no"]: return False return True
An alternative that I am considering is to create a Prompt class that will increase the flexibility of the results. For example, if I want to convert "y" or "n" to True or False, this does not work.
create_another = Prompt(prompt="Create another user? (y/n): ," default=False, rules=["y_n_or_yes_no", "ignore_case"]).prompt().convert_to_bool()
Another alternative that I am considering is simply to make individualized prompts and call them, each of them written in the same way as my original code. This does not change anything. It simply serves to get these loops from my main execution code, which makes it easier to see the main execution code:
username = prompt("get_new_username") def prompt(prompt_name): if prompt_name == "get_new_username": while True: username = input("Enter New Username: ") if ldap.search(username): print " [!] Username already taken." if not validator.validate_username(username): print " [!] Invalid Username." else: break return username if prompt_name == "y_n_yes_no_ignore_case": # do prompt if prompt_name == "y_n_yes_no": # do prompt if prompt_name == "y_n": # do prompt if prompt_name == "y_n_ignore_case": # do prompt if prompt_name == "yes_no": # do prompt if prompt_name == "yes_no_ignore_case": # do prompt
I understand that it might just be nice to agree to one accepted "y / n" format for all my programs, and I will. This is just to show that in cases where I need a very similar, but slightly different invitation, this will lead to a lot of copy / paste of the code (generally there is no flexibility in this method).
What is a good approach to writing clean, flexible, and easy-to-maintain custom tips?
(I saw this: User request for input until he gives a valid answer and some other answers. My question is not how to get input and confirm it, how to create a flexible input system that can be reused use with several programs).