Coverage for src / lexigram / contracts / exceptions / infra.py: 21%
56 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-08-19 05:41 +0800
« prev ^ index » next coverage.py v7.13.5, created at 2026-08-19 05:41 +0800
1"""Infrastructure-related exception classes."""
3from __future__ import annotations
5from typing import Any
7from lexigram.contracts.exceptions.base import LexigramError
10class InfrastructureError(LexigramError):
11 """System-level failures (DB down, network timeouts)."""
13 _code = "LEX_ERR_INFRA_001"
15 def __init__(self, message: str = "Infrastructure error", **kwargs: Any) -> None:
16 super().__init__(message, **kwargs)
19class DatabaseError(InfrastructureError):
20 """Database-related error (infrastructure-level)."""
22 _code = "LEX_ERR_DB_001"
24 def __init__(self, message: str = "Database error", **kwargs: Any) -> None:
25 super().__init__(message, **kwargs)
28class MigrationError(InfrastructureError):
29 """Database migration error."""
31 _code = "LEX_ERR_DB_002"
33 def __init__(self, message: str = "Migration error", **kwargs: Any) -> None:
34 super().__init__(message, **kwargs)
37class LockError(InfrastructureError):
38 """Distributed lock error."""
40 _code = "LEX_ERR_INFRA_002"
42 def __init__(self, message: str = "Lock error", **kwargs: Any) -> None:
43 super().__init__(message, **kwargs)
46class LockConflictError(LockError):
47 """Raised when a lock conflict occurs (resource already locked)."""
49 _code = "LEX_ERR_INFRA_003"
51 def __init__(
52 self,
53 resource: str,
54 owner: str | None = None,
55 message: str | None = None,
56 **kwargs: Any,
57 ) -> None:
58 if message is None:
59 message = f"Lock conflict on '{resource}'"
60 if owner:
61 message += f" (held by: {owner})"
63 super().__init__(
64 message,
65 details={"resource": resource, "owner": owner},
66 **kwargs,
67 )
68 self.resource = resource
69 self.owner = owner
72class RegistryError(LexigramError):
73 """Base exception for registry errors."""
75 _code = "LEX_ERR_REG_001"
77 def __init__(self, message: str = "Registry error", **kwargs: Any) -> None:
78 super().__init__(message, **kwargs)
81class RegistryKeyError(RegistryError, KeyError):
82 """Raised when a key is not found in a registry."""
84 _code = "LEX_ERR_REG_002"
86 def __init__(self, message: str = "Key not found", **kwargs: Any) -> None:
87 super().__init__(message, **kwargs)
90class RegistryAlreadyExistsError(RegistryError):
91 """Raised when attempting to register a duplicate key in a registry."""
93 _code = "LEX_ERR_REG_003"
95 def __init__(self, message: str = "Key already exists", **kwargs: Any) -> None:
96 super().__init__(message, **kwargs)
99class IntegrityError(DatabaseError):
100 """Raised when a database integrity constraint is violated."""
102 _code = "LEX_ERR_DB_003"
104 def __init__(
105 self, message: str = "Integrity constraint violated", **kwargs: Any
106 ) -> None:
107 super().__init__(message, **kwargs)
110class ConstraintError(IntegrityError):
111 """Raised when a generic database constraint is violated."""
113 _code = "LEX_ERR_DB_004"
115 def __init__(self, message: str = "Constraint violated", **kwargs: Any) -> None:
116 super().__init__(message, **kwargs)
119class DuplicateKeyError(ConstraintError):
120 """Raised when a unique-key constraint is violated (duplicate record)."""
122 _code = "LEX_ERR_DB_005"
124 def __init__(self, message: str = "Duplicate key", **kwargs: Any) -> None:
125 super().__init__(message, **kwargs)
128class NoPrimaryBackendError(DatabaseError):
129 """Raised when a multi-backend setup has no backend marked as primary.
131 For multi-backend SQL configurations, exactly one backend must be marked
132 with primary: true. This error is raised when no backend is marked primary.
133 """
135 _code = "LEX_ERR_DB_011"
138__all__ = [
139 "ConstraintError",
140 "DatabaseError",
141 "DuplicateKeyError",
142 "InfrastructureError",
143 "IntegrityError",
144 "LockConflictError",
145 "LockError",
146 "MigrationError",
147 "NoPrimaryBackendError",
148 "RegistryAlreadyExistsError",
149 "RegistryError",
150 "RegistryKeyError",
151]