from abc import ABC, abstractmethod import re class ValidationError(Exception): pass class Validator(ABC): @staticmethod @abstractmethod def validate(value, field_object): pass class ValidateNull(Validator): @staticmethod def validate(value, field_object): if value is None and not field_object.nullable: raise ValidationError('Value cannot be NULL') class ValidateType(Validator): @staticmethod def validate(value, field_object): if field_object.nullable and value is None: return if not isinstance(value, field_object.data_type): raise ValidationError('Value is of wrong type') class ValidateLength(Validator): @staticmethod def validate(value, field_object): if hasattr(field_object, 'is_variable_length') and not field_object.is_variable_length: if len(str(value)) < field_object.max_length: raise ValidationError('Value has too few characters') if len(str(value)) > field_object.max_length: raise ValidationError('Value has too many characters') class ValidateTime(Validator): @staticmethod def validate(value, field_object): if not isinstance(value, str): return match = re.fullmatch(r'(\d\d):(\d\d):(\d\d)', value) if not match: raise ValidationError('Wrong time format') else: hour, minute, second = map(int, match.groups()) if hour not in range(0, 24): raise ValidationError('Wrong hour value') if minute not in range(0, 60): raise ValidationError('Wrong minute value') if second not in range(0, 60): raise ValidationError('Wrong second value') class ValidateDate(Validator): @staticmethod def validate(value, field_object): if not isinstance(value, str): return match = re.fullmatch(r'(\d\d\d\d)-(\d\d)-(\d\d)', value) if not match: raise ValidationError('Wrong date format') else: year, month, day = map(int, match.groups()) if year < 0: raise ValidationError('Wrong year value') if month not in range(1, 12 + 1): raise ValidationError('Wrong month value') if month == 2: if year % 4 == 0 and year % 100 != 0 or year % 400 == 0: febr_range = range(1, 29 + 1) else: febr_range = range(1, 28 + 1) if day not in febr_range: raise ValidationError('Wrong day value') else: days_count = { 1: 31, 3: 31, 4: 30, 5: 31, 6: 30, 7: 31, 8: 31, 9: 30, 10: 31, 11: 30, 12: 31 }.get(month) if day not in range(1, days_count + 1): raise ValidationError('Wrong day value') class ValidateDatetime(Validator): @staticmethod def validate(value, field_object): if not isinstance(value, str): return datetime = value.split() if len(datetime) != 2: raise ValidationError('Wrong datetime format') else: ValidateDate.validate(datetime[0], {}) ValidateTime.validate(datetime[1], {})