Coverage for src / lexigram / contracts / exceptions / container.py: 0%
64 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-08-15 18:57 +0800
« prev ^ index » next coverage.py v7.13.5, created at 2026-08-15 18:57 +0800
1"""Container and dependency injection exception classes."""
3from __future__ import annotations
5from dataclasses import dataclass
6from typing import Any
8from lexigram.contracts.exceptions.base import LexigramError
11class ContainerError(LexigramError):
12 """Base container error."""
14 _code = "LEX_ERR_DI_001"
16 def __init__(self, message: str = "Container error", **kwargs: Any) -> None:
17 super().__init__(message, **kwargs)
20class DependencyError(ContainerError):
21 """Dependency resolution error."""
23 _code = "LEX_ERR_DI_002"
25 def __init__(self, message: str = "Dependency error", **kwargs: Any) -> None:
26 super().__init__(message, **kwargs)
29class CircularDependencyError(DependencyError):
30 """Circular dependency detected."""
32 _code = "LEX_ERR_DI_003"
34 def __init__(
35 self,
36 message: str = "Circular dependency detected",
37 **kwargs: Any,
38 ) -> None:
39 kwargs.setdefault(
40 "hint",
41 "Check constructor parameters of the classes in the cycle for mutual references.",
42 )
43 super().__init__(message, **kwargs)
46class UnresolvableDependencyError(DependencyError):
47 """Cannot resolve dependency."""
49 _code = "LEX_ERR_DI_004"
51 def __init__(
52 self,
53 message: str = "Cannot resolve dependency",
54 dependency: str | None = None,
55 **kwargs: Any,
56 ) -> None:
57 details = kwargs.get("details", {})
58 if dependency:
59 details["dependency"] = dependency
60 kwargs["details"] = details
61 kwargs.setdefault(
62 "hint",
63 "Verify the type is registered in a Provider and all its dependencies are resolvable.",
64 )
65 super().__init__(message, **kwargs)
68class RegistrationError(ContainerError):
69 """Service registration error."""
71 _code = "LEX_ERR_DI_005"
73 def __init__(self, message: str = "Registration error", **kwargs: Any) -> None:
74 kwargs.setdefault(
75 "hint",
76 "Ensure the service is registered in a Provider.register() before the container boots.",
77 )
78 super().__init__(message, **kwargs)
81class ContainerBuildError(ContainerError):
82 """Container build/configuration error."""
84 _code = "LEX_ERR_DI_006"
86 def __init__(
87 self,
88 message: str = "Container build error",
89 **kwargs: Any,
90 ) -> None:
91 super().__init__(message, **kwargs)
94class ProtocolValidationError(ContainerError):
95 """Protocol validation error."""
97 _code = "LEX_ERR_DI_007"
99 def __init__(
100 self,
101 message: str = "Protocol validation failed",
102 **kwargs: Any,
103 ) -> None:
104 super().__init__(message, **kwargs)
107class ContainerValidationError(ContainerBuildError):
108 """Container dependency graph validation failed before boot.
110 Raised when ``Container.validate()`` discovers issues (missing
111 dependencies, circular references, scope violations) during the
112 pre-boot validation phase. The ``issues`` attribute contains the
113 full list of problem descriptions.
114 """
116 _code = "LEX_ERR_DI_008"
118 def __init__(
119 self,
120 issues: list[str],
121 message: str | None = None,
122 **kwargs: Any,
123 ) -> None:
124 self.issues = issues
125 formatted = "; ".join(issues)
126 msg = message or f"Container validation failed: {formatted}"
127 kwargs.setdefault("details", {"issues": issues})
128 super().__init__(msg, **kwargs)
131class ScopedResolutionError(UnresolvableDependencyError):
132 """Attempted to resolve a scoped service outside of an active scope.
134 Raised when a service registered with ``container.scoped()`` is resolved
135 directly on the root container instead of inside a
136 ``container.create_scope()`` block.
137 """
139 _code = "LEX_ERR_DI_009"
141 def __init__(
142 self,
143 message: str = "Cannot resolve scoped service outside of an active scope",
144 service: str | None = None,
145 **kwargs: Any,
146 ) -> None:
147 details = kwargs.get("details", {})
148 if service:
149 details["service"] = service
150 kwargs["details"] = details
151 super().__init__(message, dependency=service, **kwargs)
154@dataclass(frozen=True)
155class OrphanedRegistration:
156 """Represents a potentially orphaned service registration.
158 An orphaned registration is a service that is registered in the
159 container but is never depended upon by any other registered service
160 and is not in the container's exports list.
161 """
163 service_key: str
164 """The service type name or key."""
166 registered_at: str
167 """The location/module where the service was registered."""
169 suggestion: str
170 """A suggestion for resolving the orphan."""
173__all__ = [
174 "CircularDependencyError",
175 "ContainerBuildError",
176 "ContainerError",
177 "ContainerValidationError",
178 "DependencyError",
179 "OrphanedRegistration",
180 "ProtocolValidationError",
181 "RegistrationError",
182 "ScopedResolutionError",
183 "UnresolvableDependencyError",
184]