Advertisement
gruntfutuk

input_validation_3

Aug 4th, 2018
208
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 6.62 KB | None | 0 0
  1. ''' Input validation function v3.1 - requires Python 3.6 or higher '''
  2.  
  3. # provide human readable descriptions of types supported by validation function
  4. TYPE_LABELS = {int: "integer number",
  5.                float: "floating point number",
  6.                str: "string of characters"}
  7.  
  8. def int_validate(value: int, int_range = None, negative_ok: bool = True):
  9.     ''' Checks if integer value is within acceptable ranges, returns True/False and error message
  10.    
  11.        value: integer value to be validated
  12.        int_range: single integer where value >= specified integer
  13.        int_range: tuple or list of two integers where first <= value <= second
  14.        int_range: tuple or list of tuples or lists each as above
  15.        negative_ok: False overides any negative ranges
  16.        returns: tuple(valid:bool, err_msg: str = '')
  17.    '''
  18.    
  19.     def int_validate_range(value: int, int_range):
  20.         ''' return True if value is inside of int_range tuple/list start, finish range
  21.        
  22.            value: integer value to be validated
  23.            int_range: tuple or list of two integers where first <= value <= second
  24.            returns: tuple(valid:bool, err_msg: str = '')
  25.        '''
  26.         if not isinstance(value, int):
  27.             raise TypeError('Value to validate is not an integer.')
  28.         if not isinstance(int_range, (list, tuple)):
  29.             raise ValueError(f'Range specifier is not a tuple or list: {int_range}.')
  30.         if not len(int_range) == 2:
  31.             raise ValueError(f'Range specifier is not a pair {int_range}.')
  32.         if not(isinstance(int_range[0], int) and isinstance(int_range[1], int)):
  33.             raise ValueError(f'Range specifier does not contain single pair of integers {int_range}.')
  34.         if int_range[0] > int_range[1]:
  35.             raise ValueError(f'Range specifier finish is lower than start {int_range}.')
  36.         return int_range[0] <= value <= int_range[1]
  37.    
  38.     if not isinstance(value, int):
  39.         raise TypeError('Value to validate is not an integer.')
  40.    
  41.     if int_range and not isinstance(int_range, (int, list, tuple)):
  42.         raise TypeError('int_range wrong type.')
  43.    
  44.     valid = True
  45.     err_msg = ''
  46.    
  47.     if not negative_ok and value < 0:
  48.         valid = False
  49.         err_msg = 'Integer must not be negative.'
  50.    
  51.     elif isinstance(int_range, int):  # int_range can just be one integer, where value >=
  52.         if value < int_range:
  53.             valid = False
  54.             err_msg = f'Integer must be greater than or equal to {int_range}.'
  55.    
  56.     elif isinstance(int_range, (list, tuple)):
  57.         if len(int_range) == 2 and isinstance(int_range[0], int) and isinstance(int_range[1], int):
  58.             if not int_validate_range(value, (int_range[0], int_range[1])):
  59.                 valid = False
  60.                 err_msg = f'Integer must be within specified range(s): {int_range}.'
  61.         else:  # assume we have list or tuple of one or more pairs of range start, fini integers
  62.             for current_range in int_range:
  63.                 if int_validate_range(value, current_range):
  64.                     break
  65.             else:  # didn't find any valid range for the value
  66.                 valid = False
  67.                 err_msg = f'Integer must be within specified range(s): {int_range}.'
  68.    
  69.     return valid, err_msg
  70.  
  71.  
  72. def get_input(msg = '', type_req = int, int_range = None,
  73.               negative_ok = True,
  74.               blank_ok = False,
  75.               cont = False, cont_qty = None, inc_count = False
  76.              ):
  77.     ''' prompt user for input with msg and return appropriately cast value
  78.    
  79.        msg: optional string message to output as input prompt (optional ± used with inc_count)
  80.        typ_req: input type required, options are int, float, str
  81.        int_range: single integer where value >= specified integer
  82.        int_range: tuple or list of two integers where first <= value <= second
  83.        int_range: tuple or list of tuples or lists each as above
  84.        negative_ok: False overides any negative ranges
  85.        blank_ok: indicated empty input is acceptable, returns empty string or None
  86.        cont: requires list of type_req returned, can be empty if blank_ok, unlimited if no cont_qty
  87.        cont_qty: requires list of this quantity of type_req entries in list, cont not required
  88.        inc_count: add input num to prompt (use ± in msg to delimit before and after text)
  89.        returns: valid input or list of inputs of the require type, or None or "" if blank_ok    
  90.    '''
  91.    
  92.     if not type_req in TYPE_LABELS:
  93.         raise TypeError("Unknown input type requested by calling code.")
  94.    
  95.     if not type_req is int and int_range:
  96.         raise TypeError('Specified an integer range but not asked for an integer input.')
  97.        
  98.     if cont_qty and not(isinstance(cont_qty, int) and cont_qty > 0):
  99.         raise TypeError(f'Specified a list of {type_req} but quantity specified, {cont_qty}, not valid')
  100.    
  101.     if not cont and cont_qty:
  102.         cont = True
  103.    
  104.     if cont:
  105.         inputs = []
  106.    
  107.     prompt = msg
  108.     deliminator = '±'
  109.     while True:
  110.         while True:  # get valid input loop
  111.             if cont and inc_count:
  112.                 before, delim , after = msg.partition(deliminator)
  113.                 if not delim:
  114.                     after = ' '
  115.                 prompt = f'{before}{len(inputs) + 1}{after}'
  116.             response = input(prompt)
  117.  
  118.             if not response and (blank_ok or cont):
  119.                 if type_req is str:
  120.                     value = ''
  121.                 else:
  122.                     value = None
  123.                 break
  124.  
  125.             if response:
  126.                 try:
  127.                     value = type_req(response)
  128.                 except ValueError as e:
  129.                     err_msg = f'{TYPE_LABELS[type_req]} input required.'
  130.                 else:
  131.                     valid = True
  132.                     if type_req is int:
  133.                         valid, err_msg = int_validate(value, int_range, negative_ok)
  134.                     if valid:
  135.                         break
  136.  
  137.             else:
  138.                 err_msg = f'Non-empty {TYPE_LABELS[type_req]} input required.'
  139.  
  140.             print(err_msg + ' Please try again.')
  141.            
  142.         if not cont:  # only wanted one input
  143.             break
  144.        
  145.         if response:
  146.             inputs.append(value)
  147.             if (cont_qty and len(inputs) == cont_qty):
  148.                 break
  149.        
  150.         elif (inputs or blank_ok) and not cont_qty:
  151.             break
  152.            
  153.         else:
  154.             print(f'{TYPE_LABELS[type_req]} input required.')
  155.    
  156.     return inputs if cont else value
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement