In multiple parts of my program, user input needs to be validated and categorized. As these "validation trees" can get pretty unwieldy, I've started splitting them out in different functions, for example like this:
from enum import Enum, auto
class InputCat(Enum):
INVALID = auto()
HELP = auto()
SELECT = auto()
GOTO_PAGE = auto()
GOTO_PLAYER = auto()
def _goto_player(user_input: str) -> InputCat:
"""Checks if string is of type GOTO_PLAYER."""
for char in user_input[1:]:
if not (char.isalpha() or char in {' ', '-'}):
print("Invalid input: if character after '>' is alpha, all other characters must be alpha, ' ' or '-'.")
return InputCat.INVALID
return InputCat.GOTO_PLAYER
def _goto_page(user_input: str) -> InputCat:
"""Checks if string is of type GOTO_PAGE."""
for char in user_input[1:]:
if not char.isnumeric():
print("Invalid input: if character after '>' is numeric, all other characters must be numeric too.")
return InputCat.INVALID
return InputCat.GOTO_PAGE
def _goto(user_input: str) -> InputCat:
"""Checks if string is of type GOTO_PAGE or GOTO_PLAYER."""
if len(user_input) == 1:
print("Invalid input: need more input after '>'.")
return InputCat.INVALID
if user_input[1].isnumeric():
return _goto_page(user_input)
elif user_input[1].isalpha():
return _goto_player(user_input)
else:
print("Invalid input: character after '>' must be alphanumeric.")
return InputCat.INVALID
def _select(user_input: str) -> InputCat:
"""Checks if string is of type SELECT."""
for char in user_input:
if not char.isnumeric():
print("Invalid input: if first character is numeric, all other characters must be numeric too.")
return InputCat.INVALID
return InputCat.SELECT
def _help(user_input: str) -> InputCat:
"""Checks if string is of type HELP."""
if len(user_input) > 1:
print("Invalid input: when using '?', no other characters are allowed.")
return InputCat.INVALID
return InputCat.HELP
def get_category(user_input: str) -> InputCat:
"""Checks if string is of type HELP, SELECT, GOTO_PAGE, GOTO_PLAYER or INVALID."""
if not user_input:
print('Invalid input: no input.')
return InputCat.INVALID
if user_input[0] == '?':
return _help(user_input)
elif user_input[0].isnumeric():
return _select(user_input)
elif user_input[0] == '>':
return _goto(user_input)
else:
print("Invalid input: first char must be alphanumeric, '>' or '?'.")
return InputCat.INVALID
I was wondering if this is a readable and 'pythonic' way of doing this? If not, what would be better alternatives?
raise ValueError(msg)
syntax, in this way you can inform the user what has gone wrong and immediately stop the code execution. It will replace your print + return combination. But by doing this you won't be able toreturn InputCat.INVALID
, though it might not be needed. This is a design choice but using exceptions is the more Pythonic method here. \$\endgroup\$