/
liquid-g
/
liquid-code
Обзор
Документация
Войти
/
liquid-g
/
liquid-code
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
develop-0.4
src/liquidcode/validation.py
149 строк
5 KB
User
0.4.1 - продолжаю переписывать тесты. пофиксил несколько багов по дороге.
04 июл 2026, 23:46
04 июл 2026, 23:46
7632abc
Код
Авторство
О чём код?
""" Система валидации для LiquidCode. Предоставляет базовые валидаторы, декоратор @validate и интеграцию с ArgumentResolver. """ import re from dataclasses import dataclass from typing import Any, Dict, List, Type, Union class ValidationError(Exception): """Исключение, выбрасываемое при ошибках валидации.""" def __init__(self, errors: Dict[str, List[str]]): self.errors = errors super().__init__(str(errors)) @dataclass class Constraint: """Базовый класс для ограничений валидации.""" message: str = "Invalid value" class Required(Constraint): """Поле обязательно для заполнения.""" def __call__(self, value: Any) -> bool: return value is not None and value != '' class Email(Constraint): """Проверка email адреса.""" pattern = re.compile(r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$') def __call__(self, value: str) -> bool: if value is None: return False return bool(self.pattern.match(value)) class MinLength(Constraint): """Минимальная длина строки.""" def __init__(self, length: int, message: str = None): self.length = length self.message = message or f"Минимальная длина {length} символов" def __call__(self, value: str) -> bool: return len(value) >= self.length class MaxLength(Constraint): """Максимальная длина строки.""" def __init__(self, length: int, message: str = None): self.length = length self.message = message or f"Максимальная длина {length} символов" def __call__(self, value: str) -> bool: return len(value) <= self.length class Min(Constraint): """Минимальное значение.""" def __init__(self, value: Union[int, float], message: str = None): self.value = value self.message = message or f"Значение должно быть не меньше {value}" def __call__(self, value: Union[int, float]) -> bool: return value >= self.value class Max(Constraint): """Максимальное значение.""" def __init__(self, value: Union[int, float], message: str = None): self.value = value self.message = message or f"Значение должно быть не больше {value}" def __call__(self, value: Union[int, float]) -> bool: return value <= self.value class Regex(Constraint): """Проверка по регулярному выражению.""" def __init__(self, pattern: str, message: str = None): self.pattern = re.compile(pattern) self.message = message or f"Значение не соответствует формату" def __call__(self, value: str) -> bool: return bool(self.pattern.match(value)) def validate(data: Any, expected_type: Type) -> bool: """ Валидирует значение по ожидаемому типу. Args: data: Значение для валидации. expected_type: Ожидаемый тип (int, str, list, dict, и т.д.) или Constraint. Returns: True, если значение валидно, иначе False. """ # Если expected_type - это Constraint (instance) if isinstance(expected_type, Constraint): return expected_type(data) # Если expected_type - это тип (int, str, list, dict, etc.) if isinstance(expected_type, type): # special case для None if expected_type is type(None): return data is None # special case для Any if expected_type is Any: return True return isinstance(data, expected_type) # Если expected_type - это словарь constraints if isinstance(expected_type, dict) or hasattr(expected_type, 'items'): errors = {} constraints = expected_type for field, rules in constraints.items(): value = data.get(field) if isinstance(data, dict) else getattr(data, field, None) for rule in rules if isinstance(rules, list) else [rules]: if not rule(value): errors.setdefault(field, []).append(rule.message) return not bool(errors) # Fallback return True def validate_or_raise(data: Any, expected_type: Type) -> None: """ Валидирует данные и выбрасывает ValidationError при ошибках. """ if not validate(data, expected_type): if isinstance(expected_type, Constraint): raise ValidationError({"value": [expected_type.message]}) elif isinstance(expected_type, type): type_name = expected_type.__name__ raise ValidationError({"value": [f"Expected {type_name}, got {type(data).__name__}"]}) else: raise ValidationError({"value": ["Validation failed"]})