File size: 2,653 Bytes
4107841 6494c95 4107841 1da1792 4107841 6494c95 4107841 d8ebb7a 4107841 6494c95 4107841 c566758 4107841 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 | """Typed errors. Each maps to a locale key; raw details never reach visitors (spec section 15)."""
from __future__ import annotations
class AppError(Exception):
code = "unexpected"
message_key = "error.generic"
# True only when a failure after the provider call is known not to have been billed.
unbilled = False
class EmptyInputError(AppError):
code = "empty_input"
message_key = "error.empty"
class TooLongInputError(AppError):
code = "too_long"
message_key = "error.too_long"
class ProviderTimeoutError(AppError):
code = "timeout"
message_key = "error.timeout"
class ProviderError(AppError):
"""A provider call failed or returned something unusable. Details stay in server logs."""
code = "provider_error"
message_key = "error.generic"
class MalformedReplyError(ProviderError):
"""The provider answered, but not with usable JSON. Worth one retry, unlike a refusal."""
code = "malformed_reply"
class RateLimitedError(AppError):
code = "rate_limited"
message_key = "error.busy"
unbilled = True
class CooldownError(AppError):
code = "cooldown"
message_key = "error.cooldown"
class BudgetReachedError(AppError):
code = "budget_reached"
unbilled = True
message_key = "error.budget"
class PausedError(AppError):
code = "paused"
message_key = "error.paused"
class SafetyRefusalError(AppError):
message_key = "error.safety"
def __init__(self, code: str = "refused", rewrite: str | None = None) -> None:
super().__init__(code)
self.code = code
self.rewrite = rewrite
class CheckFailedError(AppError):
"""A safety check could not run. Generation stops (fail closed)."""
code = "check_failed"
message_key = "error.generic"
class BadImageError(AppError):
"""An uploaded photo is not an image we can use (not an image, too big, unreadable)."""
code = "bad_image"
message_key = "photo.bad_image"
unbilled = True
class ConsentRequiredError(AppError):
"""The visitor did not confirm that the photo is sent to an external service."""
code = "consent_required"
message_key = "photo.need_consent"
unbilled = True
class FeatureOffError(AppError):
"""The photo studio is switched off (it is switched on from the admin account)."""
code = "feature_off"
message_key = "photo.off"
unbilled = True
def error_code(exc: BaseException) -> str:
return exc.code if isinstance(exc, AppError) else AppError.code
def error_message_key(exc: BaseException) -> str:
return exc.message_key if isinstance(exc, AppError) else AppError.message_key
|