Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- ''' Input validation function v3.1 - requires Python 3.6 or higher '''
- # provide human readable descriptions of types supported by validation function
- TYPE_LABELS = {int: "integer number",
- float: "floating point number",
- str: "string of characters"}
- def int_validate(value: int, int_range = None, negative_ok: bool = True):
- ''' Checks if integer value is within acceptable ranges, returns True/False and error message
- value: integer value to be validated
- int_range: single integer where value >= specified integer
- int_range: tuple or list of two integers where first <= value <= second
- int_range: tuple or list of tuples or lists each as above
- negative_ok: False overides any negative ranges
- returns: tuple(valid:bool, err_msg: str = '')
- '''
- def int_validate_range(value: int, int_range):
- ''' return True if value is inside of int_range tuple/list start, finish range
- value: integer value to be validated
- int_range: tuple or list of two integers where first <= value <= second
- returns: tuple(valid:bool, err_msg: str = '')
- '''
- if not isinstance(value, int):
- raise TypeError('Value to validate is not an integer.')
- if not isinstance(int_range, (list, tuple)):
- raise ValueError(f'Range specifier is not a tuple or list: {int_range}.')
- if not len(int_range) == 2:
- raise ValueError(f'Range specifier is not a pair {int_range}.')
- if not(isinstance(int_range[0], int) and isinstance(int_range[1], int)):
- raise ValueError(f'Range specifier does not contain single pair of integers {int_range}.')
- if int_range[0] > int_range[1]:
- raise ValueError(f'Range specifier finish is lower than start {int_range}.')
- return int_range[0] <= value <= int_range[1]
- if not isinstance(value, int):
- raise TypeError('Value to validate is not an integer.')
- if int_range and not isinstance(int_range, (int, list, tuple)):
- raise TypeError('int_range wrong type.')
- valid = True
- err_msg = ''
- if not negative_ok and value < 0:
- valid = False
- err_msg = 'Integer must not be negative.'
- elif isinstance(int_range, int): # int_range can just be one integer, where value >=
- if value < int_range:
- valid = False
- err_msg = f'Integer must be greater than or equal to {int_range}.'
- elif isinstance(int_range, (list, tuple)):
- if len(int_range) == 2 and isinstance(int_range[0], int) and isinstance(int_range[1], int):
- if not int_validate_range(value, (int_range[0], int_range[1])):
- valid = False
- err_msg = f'Integer must be within specified range(s): {int_range}.'
- else: # assume we have list or tuple of one or more pairs of range start, fini integers
- for current_range in int_range:
- if int_validate_range(value, current_range):
- break
- else: # didn't find any valid range for the value
- valid = False
- err_msg = f'Integer must be within specified range(s): {int_range}.'
- return valid, err_msg
- def get_input(msg = '', type_req = int, int_range = None,
- negative_ok = True,
- blank_ok = False,
- cont = False, cont_qty = None, inc_count = False
- ):
- ''' prompt user for input with msg and return appropriately cast value
- msg: optional string message to output as input prompt (optional ± used with inc_count)
- typ_req: input type required, options are int, float, str
- int_range: single integer where value >= specified integer
- int_range: tuple or list of two integers where first <= value <= second
- int_range: tuple or list of tuples or lists each as above
- negative_ok: False overides any negative ranges
- blank_ok: indicated empty input is acceptable, returns empty string or None
- cont: requires list of type_req returned, can be empty if blank_ok, unlimited if no cont_qty
- cont_qty: requires list of this quantity of type_req entries in list, cont not required
- inc_count: add input num to prompt (use ± in msg to delimit before and after text)
- returns: valid input or list of inputs of the require type, or None or "" if blank_ok
- '''
- if not type_req in TYPE_LABELS:
- raise TypeError("Unknown input type requested by calling code.")
- if not type_req is int and int_range:
- raise TypeError('Specified an integer range but not asked for an integer input.')
- if cont_qty and not(isinstance(cont_qty, int) and cont_qty > 0):
- raise TypeError(f'Specified a list of {type_req} but quantity specified, {cont_qty}, not valid')
- if not cont and cont_qty:
- cont = True
- if cont:
- inputs = []
- prompt = msg
- deliminator = '±'
- while True:
- while True: # get valid input loop
- if cont and inc_count:
- before, delim , after = msg.partition(deliminator)
- if not delim:
- after = ' '
- prompt = f'{before}{len(inputs) + 1}{after}'
- response = input(prompt)
- if not response and (blank_ok or cont):
- if type_req is str:
- value = ''
- else:
- value = None
- break
- if response:
- try:
- value = type_req(response)
- except ValueError as e:
- err_msg = f'{TYPE_LABELS[type_req]} input required.'
- else:
- valid = True
- if type_req is int:
- valid, err_msg = int_validate(value, int_range, negative_ok)
- if valid:
- break
- else:
- err_msg = f'Non-empty {TYPE_LABELS[type_req]} input required.'
- print(err_msg + ' Please try again.')
- if not cont: # only wanted one input
- break
- if response:
- inputs.append(value)
- if (cont_qty and len(inputs) == cont_qty):
- break
- elif (inputs or blank_ok) and not cont_qty:
- break
- else:
- print(f'{TYPE_LABELS[type_req]} input required.')
- return inputs if cont else value
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement