| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435 |
- # -*- coding: utf-8 -*-
- """
- Fehlerklassen für das Validierungs-Framework.
- Bietet eine Hierarchie von Validierungsfehlern für
- detaillierte Fehlerberichterstattung.
- """
- from __future__ import annotations
- from dataclasses import dataclass, field
- from typing import Any
- @dataclass
- class FieldError:
- """
- Fehler für ein einzelnes Feld.
- Speichert Details über den Validierungsfehler eines
- bestimmten Feldes.
- """
- field: str
- """Name des fehlerhaften Feldes."""
- message: str
- """Fehlermeldung."""
- value: Any = None
- """Der fehlerhafte Wert."""
- code: str = "invalid"
- """Fehlercode für programmatische Behandlung."""
- nested_errors: list["FieldError"] = field(default_factory=list)
- """Verschachtelte Fehler bei komplexen Strukturen."""
- def to_dict(self) -> dict[str, Any]:
- """
- Konvertiert den Fehler in ein Dictionary.
- Returns:
- Dictionary-Repräsentation des Fehlers.
- """
- result: dict[str, Any] = {
- "field": self.field,
- "message": self.message,
- "code": self.code,
- }
- if self.value is not None:
- result["value"] = repr(self.value)
- if self.nested_errors:
- result["nested"] = [e.to_dict() for e in self.nested_errors]
- return result
- def __str__(self) -> str:
- """String-Repräsentation des Fehlers."""
- return f"{self.field}: {self.message}"
- class ValidationError(Exception):
- """
- Basis-Exception für Validierungsfehler.
- Kann einen einzelnen Validierungsfehler oder mehrere
- Fehler enthalten.
- """
- def __init__(
- self,
- message: str,
- field: str | None = None,
- value: Any = None,
- code: str = "invalid",
- errors: list[FieldError] | None = None,
- ) -> None:
- """
- Initialisiert den ValidationError.
- Args:
- message: Fehlermeldung.
- field: Optionaler Feldname.
- value: Optionaler fehlerhafter Wert.
- code: Fehlercode.
- errors: Optionale Liste von FieldErrors.
- """
- super().__init__(message)
- self.message = message
- self.field = field
- self.value = value
- self.code = code
- self.errors = errors or []
- # Einzelfehler auch in die Liste aufnehmen
- if field and not errors:
- self.errors = [
- FieldError(
- field=field,
- message=message,
- value=value,
- code=code,
- )
- ]
- def to_dict(self) -> dict[str, Any]:
- """
- Konvertiert den Fehler in ein Dictionary.
- Returns:
- Dictionary-Repräsentation.
- """
- result: dict[str, Any] = {
- "message": self.message,
- "code": self.code,
- }
- if self.field:
- result["field"] = self.field
- if self.errors:
- result["errors"] = [e.to_dict() for e in self.errors]
- return result
- def __str__(self) -> str:
- """String-Repräsentation des Fehlers."""
- if self.field:
- return f"Validierungsfehler in '{self.field}': {self.message}"
- return f"Validierungsfehler: {self.message}"
- class ValidationErrors(ValidationError):
- """
- Sammlung von mehreren Validierungsfehlern.
- Aggregiert mehrere Fehler zu einem einzelnen Exception-Objekt.
- """
- def __init__(
- self,
- errors: list[FieldError],
- message: str = "Validierung fehlgeschlagen",
- ) -> None:
- """
- Initialisiert die ValidationErrors.
- Args:
- errors: Liste von FieldErrors.
- message: Übergreifende Fehlermeldung.
- """
- super().__init__(
- message=message,
- errors=errors,
- code="validation_failed",
- )
- @classmethod
- def from_dict(cls, errors_dict: dict[str, str]) -> "ValidationErrors":
- """
- Erstellt ValidationErrors aus einem Dictionary.
- Args:
- errors_dict: Mapping von Feldnamen zu Fehlermeldungen.
- Returns:
- ValidationErrors-Instanz.
- """
- errors = [
- FieldError(field=field, message=message)
- for field, message in errors_dict.items()
- ]
- return cls(errors)
- def add_error(
- self,
- field: str,
- message: str,
- value: Any = None,
- code: str = "invalid",
- ) -> None:
- """
- Fügt einen Fehler hinzu.
- Args:
- field: Feldname.
- message: Fehlermeldung.
- value: Optionaler fehlerhafter Wert.
- code: Fehlercode.
- """
- self.errors.append(
- FieldError(
- field=field,
- message=message,
- value=value,
- code=code,
- )
- )
- def has_error(self, field: str) -> bool:
- """
- Prüft, ob ein Fehler für ein Feld existiert.
- Args:
- field: Feldname.
- Returns:
- True wenn Fehler existiert.
- """
- return any(e.field == field for e in self.errors)
- def get_error(self, field: str) -> FieldError | None:
- """
- Gibt den ersten Fehler für ein Feld zurück.
- Args:
- field: Feldname.
- Returns:
- FieldError oder None.
- """
- for error in self.errors:
- if error.field == field:
- return error
- return None
- def get_messages(self) -> dict[str, str]:
- """
- Gibt alle Fehlermeldungen als Dictionary zurück.
- Returns:
- Mapping von Feldnamen zu Fehlermeldungen.
- """
- return {e.field: e.message for e in self.errors}
- @property
- def is_empty(self) -> bool:
- """Prüft, ob keine Fehler vorhanden sind."""
- return len(self.errors) == 0
- def __len__(self) -> int:
- """Anzahl der Fehler."""
- return len(self.errors)
- def __iter__(self):
- """Iterator über die Fehler."""
- return iter(self.errors)
- def __bool__(self) -> bool:
- """True wenn Fehler vorhanden."""
- return len(self.errors) > 0
- def __str__(self) -> str:
- """String-Repräsentation."""
- if not self.errors:
- return "Keine Validierungsfehler"
- lines = [f"{self.message}:"]
- for error in self.errors:
- lines.append(f" - {error}")
- return "\n".join(lines)
- class SchemaError(ValidationError):
- """
- Fehler bei der Schema-Definition.
- Wird geworfen wenn ein Schema selbst ungültig ist,
- nicht wenn Daten das Schema verletzen.
- """
- def __init__(
- self,
- message: str,
- schema_name: str | None = None,
- field: str | None = None,
- ) -> None:
- """
- Initialisiert den SchemaError.
- Args:
- message: Fehlermeldung.
- schema_name: Name des fehlerhaften Schemas.
- field: Optionaler Feldname im Schema.
- """
- super().__init__(
- message=message,
- field=field,
- code="schema_error",
- )
- self.schema_name = schema_name
- def __str__(self) -> str:
- """String-Repräsentation."""
- if self.schema_name:
- if self.field:
- return f"Schema-Fehler in '{self.schema_name}.{self.field}': {self.message}"
- return f"Schema-Fehler in '{self.schema_name}': {self.message}"
- return f"Schema-Fehler: {self.message}"
- class TypeValidationError(ValidationError):
- """
- Fehler bei Typ-Validierung.
- Spezialisierter Fehler für Typ-Unstimmigkeiten.
- """
- def __init__(
- self,
- field: str,
- expected_type: type | str,
- actual_type: type | str,
- value: Any = None,
- ) -> None:
- """
- Initialisiert den TypeValidationError.
- Args:
- field: Feldname.
- expected_type: Erwarteter Typ.
- actual_type: Tatsächlicher Typ.
- value: Der fehlerhafte Wert.
- """
- expected_name = (
- expected_type.__name__
- if isinstance(expected_type, type)
- else str(expected_type)
- )
- actual_name = (
- actual_type.__name__
- if isinstance(actual_type, type)
- else str(actual_type)
- )
- super().__init__(
- message=f"Erwartet Typ '{expected_name}', erhalten '{actual_name}'",
- field=field,
- value=value,
- code="type_error",
- )
- self.expected_type = expected_type
- self.actual_type = actual_type
- class RangeValidationError(ValidationError):
- """
- Fehler bei Bereichs-Validierung.
- Spezialisierter Fehler für Werte außerhalb des Bereichs.
- """
- def __init__(
- self,
- field: str,
- value: Any,
- min_value: Any | None = None,
- max_value: Any | None = None,
- ) -> None:
- """
- Initialisiert den RangeValidationError.
- Args:
- field: Feldname.
- value: Der fehlerhafte Wert.
- min_value: Minimaler erlaubter Wert.
- max_value: Maximaler erlaubter Wert.
- """
- if min_value is not None and max_value is not None:
- msg = f"Wert muss zwischen {min_value} und {max_value} liegen"
- elif min_value is not None:
- msg = f"Wert muss mindestens {min_value} sein"
- elif max_value is not None:
- msg = f"Wert darf höchstens {max_value} sein"
- else:
- msg = "Wert außerhalb des Bereichs"
- super().__init__(
- message=msg,
- field=field,
- value=value,
- code="range_error",
- )
- self.min_value = min_value
- self.max_value = max_value
- class RequiredFieldError(ValidationError):
- """
- Fehler für fehlende Pflichtfelder.
- """
- def __init__(self, field: str) -> None:
- """
- Initialisiert den RequiredFieldError.
- Args:
- field: Name des fehlenden Feldes.
- """
- super().__init__(
- message="Dieses Feld ist erforderlich",
- field=field,
- code="required",
- )
- class PatternValidationError(ValidationError):
- """
- Fehler bei Pattern-Validierung.
- """
- def __init__(
- self,
- field: str,
- value: str,
- pattern: str,
- ) -> None:
- """
- Initialisiert den PatternValidationError.
- Args:
- field: Feldname.
- value: Der fehlerhafte Wert.
- pattern: Das nicht-gematchte Pattern.
- """
- super().__init__(
- message=f"Wert entspricht nicht dem Pattern '{pattern}'",
- field=field,
- value=value,
- code="pattern_error",
- )
- self.pattern = pattern
|