errors.py
python
sha256:fc4c9ad652d1fff3dc508cb6ea02ee710ee6dfc4cb3761291d9900b5e029ea8a
feat(slice-7): T3 dataset review lifecycle, job queue, prov…
Human
minor
⚠ breaking
42 days ago
| 1 | """Stable, sanitized error model for the Scooling Lab API.""" |
| 2 | |
| 3 | from __future__ import annotations |
| 4 | |
| 5 | from dataclasses import dataclass |
| 6 | from enum import Enum |
| 7 | |
| 8 | |
| 9 | class ErrorCode(str, Enum): |
| 10 | """Public API error codes that do not expose internals.""" |
| 11 | |
| 12 | VALIDATION_ERROR = "VALIDATION_ERROR" |
| 13 | MALFORMED_JSON = "MALFORMED_JSON" |
| 14 | NOT_FOUND = "NOT_FOUND" |
| 15 | INVALID_TRANSITION = "INVALID_TRANSITION" |
| 16 | QUEUE_LIMIT_EXCEEDED = "QUEUE_LIMIT_EXCEEDED" |
| 17 | METHOD_NOT_ALLOWED = "METHOD_NOT_ALLOWED" |
| 18 | CONFLICT = "CONFLICT" |
| 19 | INTERNAL_ERROR = "INTERNAL_ERROR" |
| 20 | DATASET_NOT_APPROVED = "DATASET_NOT_APPROVED" |
| 21 | |
| 22 | |
| 23 | SAFE_ERROR_MESSAGES: dict[ErrorCode, str] = { |
| 24 | ErrorCode.VALIDATION_ERROR: "The request is not accepted by the training contract.", |
| 25 | ErrorCode.MALFORMED_JSON: "The request body must be valid JSON.", |
| 26 | ErrorCode.NOT_FOUND: "The requested training resource was not found.", |
| 27 | ErrorCode.INVALID_TRANSITION: "The requested job state change is not allowed.", |
| 28 | ErrorCode.QUEUE_LIMIT_EXCEEDED: "The fixture training queue is full.", |
| 29 | ErrorCode.METHOD_NOT_ALLOWED: "The HTTP method is not allowed for this route.", |
| 30 | ErrorCode.CONFLICT: "The request conflicts with an existing training job.", |
| 31 | ErrorCode.INTERNAL_ERROR: "The training service could not complete the request.", |
| 32 | ErrorCode.DATASET_NOT_APPROVED: "The dataset has not been approved for job submission.", |
| 33 | } |
| 34 | |
| 35 | |
| 36 | @dataclass(frozen=True) |
| 37 | class ApiError(Exception): |
| 38 | """Exception carrying only a stable public code and HTTP status.""" |
| 39 | |
| 40 | code: ErrorCode |
| 41 | status: int |
| 42 | |
| 43 | @property |
| 44 | def message(self) -> str: |
| 45 | """Return the public message for the error code.""" |
| 46 | |
| 47 | return SAFE_ERROR_MESSAGES[self.code] |
| 48 | |
| 49 | |
| 50 | def error_payload(error: ApiError) -> dict[str, dict[str, str]]: |
| 51 | """Build a safe JSON error payload with no request echo or paths.""" |
| 52 | |
| 53 | return { |
| 54 | "error": { |
| 55 | "code": error.code.value, |
| 56 | "message": error.message, |
| 57 | } |
| 58 | } |
File History
1 commit
sha256:fc4c9ad652d1fff3dc508cb6ea02ee710ee6dfc4cb3761291d9900b5e029ea8a
feat(slice-7): T3 dataset review lifecycle, job queue, prov…
Human
minor
⚠
42 days ago