Source code for alchemiq.exceptions

"""Alchemiq exception hierarchy: all library-specific errors live here."""

from __future__ import annotations


[docs] class AlchemiqError(Exception): """Root of all Alchemiq exceptions."""
[docs] class ConfigError(AlchemiqError): """Invalid model/field declaration, or a missing optional dependency."""
[docs] class ValidationError(AlchemiqError): """Eager field validation failure (on assignment or construction).""" def __init__( self, *, reason: str, field: str | None = None, value: object = None, model: str | None = None, errors: list[ValidationError] | None = None, ) -> None: self.reason = reason self.field = field self.value = value self.model = model self.errors: list[ValidationError] = errors or [] super().__init__(self._render()) def _render(self) -> str: if self.errors: return f"{len(self.errors)} validation errors for {self.model or '?'}: " + "; ".join( e._render() for e in self.errors ) loc = f"{self.model + '.' if self.model else ''}{self.field or '?'}" return f"{loc}: {self.reason} (got {self.value!r})"
[docs] @classmethod def aggregate( cls, errors: list[ValidationError], *, model: str | None = None ) -> ValidationError: """Collapse a list of errors into one; returns the single error unchanged when len == 1.""" if len(errors) == 1: return errors[0] return cls(reason="multiple errors", model=model, errors=list(errors))
[docs] class QueryError(AlchemiqError): """Invalid query: bad field, operator, or value at build/compile time."""
[docs] class UnknownFieldError(QueryError): """Referenced field or traversal path does not exist on the model."""
[docs] class UnknownOperatorError(QueryError): """Unknown lookup suffix (operator)."""
[docs] class DeserializationError(QueryError): """Malformed Q wire payload."""
[docs] class DisallowedFieldError(QueryError): """Field/path rejected by the deserialization whitelist (security)."""
[docs] class InvalidCursorError(QueryError): """Malformed, tampered, or stale pagination cursor."""
[docs] class PersistenceError(AlchemiqError): """Runtime data-access failure (engine, session, repository, loading)."""
[docs] class EngineNotConfiguredError(PersistenceError): """A repository/UnitOfWork was used before alchemiq.configure(dsn)."""
[docs] class NotFoundError(PersistenceError): """Expected exactly one row but found none."""
[docs] class MultipleResultsFound(PersistenceError): """Expected at most one row but found several."""
[docs] class ConcurrentModificationError(PersistenceError): """Optimistic-lock conflict: the row was modified since it was read."""
[docs] class RelationNotLoaded(PersistenceError): """Accessed a relationship that was not eager-loaded (no implicit lazy IO)."""
[docs] class ClickHouseError(PersistenceError): """ClickHouse IO failure (connection, query, insert, DDL)."""
[docs] class ClickHouseNotConfiguredError(ClickHouseError): """A ClickHouse operation was used before configure_clickhouse()."""
[docs] class UnsupportedOperationError(ClickHouseError): """The operation is not supported by ClickHouse (e.g. row UPDATE/DELETE)."""