Coverage for src / lexigram / ai / relay / errors.py: 100%
29 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-08-08 23:08 +0800
« prev ^ index » next coverage.py v7.13.5, created at 2026-08-08 23:08 +0800
1"""Stable ``RelayError`` factories for the relay conversion engine.
3Mappers and the engine construct errors through these factories so every
4failure carries one stable machine-readable code from
5:class:`lexigram.contracts.ai.exceptions.RelayErrorCode`. Callers branch
6on ``error.code``, never on message text.
7"""
9from __future__ import annotations
11from lexigram.contracts.ai.exceptions import RelayError, RelayErrorCode
13__all__ = [
14 "duplicate_registration",
15 "malformed_payload",
16 "media_resolution_required",
17 "missing_required_option",
18 "serialization_error",
19 "stream_already_finalized",
20 "stream_state_invalid",
21 "translate",
22 "unsupported_feature",
23 "unsupported_format",
24 "unsupported_route",
25]
28def malformed_payload(detail: str) -> RelayError:
29 """The wire payload did not match the expected shape.
31 Args:
32 detail: Human-readable description of the malformed field.
34 Returns:
35 A ``RelayError`` with code ``malformed_payload``.
36 """
37 return RelayError(detail, code=RelayErrorCode.MALFORMED_PAYLOAD)
40def missing_required_option(detail: str) -> RelayError:
41 """A required field or host option is absent.
43 Args:
44 detail: Which required option is missing.
46 Returns:
47 A ``RelayError`` with code ``missing_required_option``.
48 """
49 return RelayError(detail, code=RelayErrorCode.MISSING_REQUIRED_OPTION)
52def unsupported_feature(detail: str) -> RelayError:
53 """The source feature cannot be converted to the target.
55 Args:
56 detail: The feature that could not be converted.
58 Returns:
59 A ``RelayError`` with code ``unsupported_feature``.
60 """
61 return RelayError(detail, code=RelayErrorCode.UNSUPPORTED_FEATURE)
64def unsupported_format(detail: str) -> RelayError:
65 """The payload does not belong to this mapper's wire format.
67 Args:
68 detail: The expected and actual payload shapes.
70 Returns:
71 A ``RelayError`` with code ``unsupported_format``.
72 """
73 return RelayError(detail, code=RelayErrorCode.UNSUPPORTED_FORMAT)
76def unsupported_route(detail: str) -> RelayError:
77 """No mapper exists for the requested source/target route.
79 Args:
80 detail: The route that cannot be converted.
82 Returns:
83 A ``RelayError`` with code ``unsupported_route``.
84 """
85 return RelayError(detail, code=RelayErrorCode.UNSUPPORTED_ROUTE)
88def duplicate_registration(detail: str) -> RelayError:
89 """A mapper was registered twice for one wire format.
91 Args:
92 detail: The format that was registered twice.
94 Returns:
95 A ``RelayError`` with code ``duplicate_registration``.
96 """
97 return RelayError(detail, code=RelayErrorCode.DUPLICATE_REGISTRATION)
100def media_resolution_required(detail: str) -> RelayError:
101 """URL media requires a resolver the host did not supply.
103 Args:
104 detail: The media URL that cannot be resolved.
106 Returns:
107 A ``RelayError`` with code ``media_resolution_required``.
108 """
109 return RelayError(detail, code=RelayErrorCode.MEDIA_RESOLUTION_REQUIRED)
112def serialization_error(detail: str) -> RelayError:
113 """The payload cannot be serialized or deserialized.
115 Args:
116 detail: Human-readable description of the serialization failure.
118 Returns:
119 A ``RelayError`` with code ``serialization_error``.
120 """
121 return RelayError(detail, code=RelayErrorCode.SERIALIZATION_ERROR)
124def stream_state_invalid(detail: str) -> RelayError:
125 """A stream event is out of order or from the wrong source format.
127 Args:
128 detail: The ordering or format violation.
130 Returns:
131 A ``RelayError`` with code ``stream_state_invalid``.
132 """
133 return RelayError(detail, code=RelayErrorCode.STREAM_STATE_INVALID)
136def stream_already_finalized(detail: str) -> RelayError:
137 """An event was accepted after the stream was finalized.
139 Args:
140 detail: What was accepted after finalization.
142 Returns:
143 A ``RelayError`` with code ``stream_already_finalized``.
144 """
145 return RelayError(detail, code=RelayErrorCode.STREAM_ALREADY_FINALIZED)
148def translate(exc: Exception, *, detail: str) -> RelayError:
149 """Translate an unexpected exception into a stable error category.
151 ``RelayError`` passes through unchanged. DTO parsing failures
152 (``ValueError``, ``TypeError``, ``KeyError``) become
153 ``malformed_payload``; any other exception becomes
154 ``serialization_error``.
156 Args:
157 exc: The exception raised inside a mapper.
158 detail: Context describing what was being translated.
160 Returns:
161 A stable ``RelayError``.
162 """
163 if isinstance(exc, RelayError):
164 return exc
165 if isinstance(exc, (ValueError, TypeError, KeyError)):
166 return malformed_payload(detail)
167 return RelayError(detail, code=RelayErrorCode.SERIALIZATION_ERROR)