pumpwood_communication.exceptions

Define PumpWood exceptions to be treated as API errors.

Define specific errors for the PumpWood platform. These errors are handled explicitly and do not result in default 500 responses.

  1"""Define PumpWood exceptions to be treated as API errors.
  2
  3Define specific errors for the PumpWood platform. These errors are handled
  4explicitly and do not result in default 500 responses.
  5"""
  6from typing import Any
  7from loguru import logger
  8
  9
 10class PumpWoodException(Exception): # NOQA
 11    """Special exception used in PumpWood systems.
 12
 13    It permits handling raises in applications by serializing responses
 14    with ``to_dict`` and exposing the HTTP status as ``status_code``.
 15    """
 16
 17    status_code: int = 400
 18    """PumpWoodException will return status 400 on Pumpwood backend."""
 19
 20    message: str
 21    """Message associated with raise."""
 22
 23    payload: dict
 24    """Dictionary payload returned by ``to_dict`` and used to format
 25       the message string."""
 26
 27    i8n_object: None
 28    """I8n object used to translate the message."""
 29
 30    was_translated: bool
 31    """If the message was translated."""
 32
 33    tag: str
 34    """Tag used to disambiguate translation context."""
 35
 36    parallel: bool
 37    """If error was raised on a parallel request."""
 38
 39    def __repr__(self):
 40        """@private."""
 41        message_fmt = self.format_message()
 42        template = "{class_name}[status_code={status_code}]: " + \
 43            "{message_fmt}\nerror payload={payload}"
 44        return template.format(
 45            class_name=self.__class__.__name__,
 46            status_code=self.status_code,
 47            message_fmt=message_fmt,
 48            payload=self.payload)
 49
 50    def __str__(self):
 51        """@private."""
 52        message_fmt = self.format_message()
 53        template = "{class_name}[status_code={status_code}]: " + \
 54            "{message_fmt}\nerror payload={payload}"
 55        return template.format(
 56            class_name=self.__class__.__name__,
 57            status_code=self.status_code,
 58            message_fmt=message_fmt,
 59            payload=self.payload)
 60
 61    def __init__(self, message: str, payload: dict = None,
 62                 status_code: int = None, tag: str = '',
 63                 parallel: bool = False, i8n_object: None = None,
 64                 was_translated: bool = False):
 65        """Initialize the PumpWood exception.
 66
 67        Args:
 68            message (str):
 69                Message formatted with payload data using ``{key}``
 70                placeholders.
 71            payload (dict):
 72                Payload data passed as a dictionary. Returned in
 73                ``to_dict`` and used to format the message.
 74                Defaults to None.
 75            status_code (int):
 76                HTTP status code override. Defaults to None.
 77            i8n_object (None):
 78                I8n object used to translate the message.
 79                Defaults to None.
 80            tag (str):
 81                Tag used to disambiguate translation context.
 82                Defaults to empty string.
 83            parallel (bool):
 84                Whether the error occurred during parallel work.
 85                Defaults to False.
 86        """
 87        Exception.__init__(self)
 88
 89        # Initialize payload to avoid mutable default issues
 90        if payload is None:
 91            payload = {}
 92
 93        self.message = message
 94        if status_code is not None:
 95            self.status_code = status_code
 96        self.payload = payload
 97        self.i8n_object = i8n_object
 98        self.was_translated = was_translated
 99        self.tag = tag
100        self.parallel = parallel
101
102    def format_message(self) -> str:
103        """Format exception message using payload data.
104
105        Substitute placeholders at exception message with payload.
106
107        Returns:
108            str:
109                Message with placeholders substituted from payload data.
110        """
111        try:
112            # If i8n_object is not None and was_translated is False,
113            # use it to translate the message
114            message = self.message
115            if (self.i8n_object is not None) and (not self.was_translated):
116                message = self.i8n_object.t(
117                    sentence=self.message, tag=self.tag)
118            return message.format(**self.payload)
119        except Exception:
120            return self.message + "\n** format error **"
121
122    def to_dict(self) -> dict[str, Any]:
123        """Serialize exception object for API response.
124
125        Returns:
126            dict[str, Any]:
127                Dictionary with keys:
128            - **payload [dict]:** Payload associated with the raise.
129            - **type [str]:** Name of the exception class.
130            - **message_not_fmt [str]:** Message without payload
131                substitution.
132            - **message [str]:** Message formatted with payload data.
133            - **status_code [int]:** HTTP status code for the exception.
134            - **translate [bool]:** Whether the message should be
135                translated.
136            - **tag [str]:** Tag used to disambiguate translation
137                context.
138            - **parallel [bool]:** Whether the error was from parallel
139                work.
140        """
141        message_fmt = self.format_message()
142        rv = {
143            "__error__": 'PumpWoodException',
144            "type": self.__class__.__name__,
145            "payload": self.payload,
146            "message_not_fmt": self.message,
147            "message": message_fmt,
148            "status_code": self.status_code,
149            "was_translated": self.was_translated,
150            "tag": self.tag,
151            "parallel": self.parallel}
152        return rv
153
154
155class PumpWoodDataLoadingException(PumpWoodException):
156    """Problem when loading data at dataloaders and to_load models."""
157
158    pass
159
160
161class PumpWoodDatabaseError(PumpWoodException):
162    """Errors raised by Postgres and not treated by other handlers."""
163
164    pass
165
166
167class PumpWoodUniqueDatabaseError(PumpWoodException):
168    """Unique errors raised by Postgres."""
169
170    pass
171
172
173class PumpWoodDataTransformationException(PumpWoodException):
174    """Problem when transforming model data."""
175
176    pass
177
178
179class PumpWoodWrongParameters(PumpWoodException):
180    """Invalid or missing request parameters."""
181
182    pass
183
184
185class PumpWoodObjectSavingException(PumpWoodException):
186    """Problem when saving object data."""
187
188    pass
189
190
191class PumpWoodObjectDeleteException(PumpWoodException):
192    """Problem when deleting object data."""
193
194    pass
195
196
197class PumpWoodActionArgsException(PumpWoodException):
198    """Missing arguments to perform action."""
199
200    pass
201
202
203class PumpWoodUnauthorized(PumpWoodException):
204    """User is unauthorized to perform the action."""
205
206    status_code = 401
207
208
209class PumpWoodForbidden(PumpWoodException):
210    """Action is not permitted."""
211
212    status_code = 403
213
214
215class PumpWoodObjectDoesNotExist(PumpWoodException):
216    """Object not found in database."""
217
218    status_code = 404
219
220
221class PumpWoodQueryException(PumpWoodException):
222    """Problem when querying data, like wrong fields or operators."""
223
224    pass
225
226
227class PumpWoodIntegrityError(PumpWoodException):
228    """Problem when saving data due to IntegrityError."""
229
230    pass
231
232
233class PumpWoodNotImplementedError(PumpWoodException):
234    """Feature or operation is not implemented."""
235
236    pass
237
238
239class PumpWoodMicroserviceUnavailableError(PumpWoodException):
240    """Microservice is unavailable or was not deployed."""
241
242    pass
243
244
245class PumpWoodMFAError(PumpWoodException):
246    """Problem when using MFA."""
247
248    pass
249
250
251class PumpWoodJSONLoadError(PumpWoodException):
252    """Problem loading JSON data from a request."""
253
254    pass
255
256
257class PumpWoodCacheError(PumpWoodException):
258    """Problem using the PumpWood cache."""
259
260    pass
261
262
263class PumpWoodOtherException(PumpWoodException):
264    """Unhandled or unmapped server error."""
265
266    status_code = 500
267
268    def __init__(self, message: str, payload: dict = None,
269                 status_code: int = None, was_translated: bool = False,
270                 tag: str = '', parallel: bool = False):
271        """Initialize PumpWoodOtherException.
272
273        Args:
274            message (str):
275                Message formatted with payload data using ``{key}``
276                placeholders. Truncated to 1000 characters.
277            payload (dict):
278                Payload data passed as a dictionary. Returned in
279                ``to_dict`` and used to format the message.
280                Defaults to None.
281            was_translated (bool):
282                If the message was translated. Defaults to False.
283            tag (str):
284                Tag used to disambiguate translation context.
285                Defaults to empty string.
286            status_code (int):
287                Change the default status code of the exception.
288                Defaults to None.
289            parallel (bool):
290                Whether the error occurred during parallel work.
291                Defaults to False.
292        """
293        Exception.__init__(self)
294
295        # Initialize payload to avoid mutable default issues
296        if payload is None:
297            payload = {}
298
299        # Limit size of the error, it is expected that other exceptions
300        # may have long text from kong or other between services
301        self.message = message[:1000]
302
303        if status_code is not None:
304            self.status_code = status_code
305        self.payload = payload
306
307        # Other exceptions are never translated are never translated
308        self.was_translated = False
309        self.tag = tag
310        self.parallel = parallel
311
312    def format_message(self) -> str:
313        """Return the message without formatting.
314        
315        Other exceptions are never translated, so the message is not formatted
316        and returned as is.
317        """
318        return self.message
319    
320
321class AirflowMicroServiceException(PumpWoodException):
322    """Exception raised from AirflowMicroService."""
323
324    pass
325
326
327exceptions_dict = {
328    "PumpWoodException": PumpWoodException,
329    "PumpWoodDataLoadingException": PumpWoodDataLoadingException,
330    "PumpWoodDatabaseError": PumpWoodDatabaseError,
331    "PumpWoodDataTransformationException": PumpWoodDataTransformationException,
332    "PumpWoodWrongParameters": PumpWoodWrongParameters,
333    "PumpWoodObjectSavingException": PumpWoodObjectSavingException,
334    "PumpWoodObjectDeleteException": PumpWoodObjectDeleteException,
335    "PumpWoodActionArgsException": PumpWoodActionArgsException,
336    "PumpWoodUnauthorized": PumpWoodUnauthorized,
337    "PumpWoodForbidden": PumpWoodForbidden,
338    "PumpWoodObjectDoesNotExist": PumpWoodObjectDoesNotExist,
339    "PumpWoodQueryException": PumpWoodQueryException,
340    "PumpWoodIntegrityError": PumpWoodIntegrityError,
341    "PumpWoodNotImplementedError": PumpWoodNotImplementedError,
342    "PumpWoodMicroserviceUnavailableError":
343        PumpWoodMicroserviceUnavailableError,
344    "PumpWoodMFAError": PumpWoodMFAError,
345    "PumpWoodJSONLoadError": PumpWoodJSONLoadError,
346    "PumpWoodCacheError": PumpWoodCacheError,
347    "PumpWoodOtherException": PumpWoodOtherException,
348    "AirflowMicroServiceException": AirflowMicroServiceException,
349    "PumpWoodUniqueDatabaseError": PumpWoodUniqueDatabaseError
350}
351"""
352Dictionary mapping exception class names to types.
353
354Used by backends and microservices to re-raise PumpWood exceptions.
355"""
356
357
358def raise_pumpwood_exception(exception_name: str, message: str,
359                             payload: dict = None, status_code: int = None,
360                             translate: bool = False, tag: str = '',
361                             parallel: bool = False):
362    """Raise a PumpWood exception based on its name.
363
364    Args:
365        exception_name (str):
366            Name of the exception to be retrieved and raised.
367        message (str):
368            The error message associated with the exception.
369        payload (dict):
370            A dictionary containing additional data for the exception.
371            Defaults to None.
372        status_code (int):
373            HTTP status code to be returned. Defaults to None.
374        translate (bool):
375            Whether the message should be translated. Defaults to False.
376        tag (str):
377            Tag used to disambiguate translation context.
378            Defaults to empty string.
379        parallel (bool):
380            If the exception happened during parallel processing.
381            Defaults to False.
382
383    Returns:
384        None:
385            This function does not return; it always raises an exception.
386
387    Raises:
388        PumpWoodOtherException:
389            If ``exception_name`` is not found in the registry.
390        PumpWoodException:
391            The specific exception mapped to ``exception_name``.
392    """
393    # Initialize payload to avoid mutable default issues
394    if payload is None:
395        payload = {}
396
397    pumpwood_exception = exceptions_dict.get(exception_name)
398    if pumpwood_exception is None:
399        msg = (
400            "exception_name [{exception_name}] not found in PumpWood "
401            "Exceptions. Check implementation")
402        logger.error(
403            msg.format(exception_name=exception_name))
404        raise PumpWoodOtherException(
405            msg.format(exception_name=exception_name),
406            payload=payload, status_code=status_code,
407            tag=tag, parallel=parallel)
408    else:
409        raise pumpwood_exception(
410            message=message, payload=payload, status_code=status_code,
411            translate=translate, tag=tag, parallel=parallel)
412
413
414def raise_from_dict(exception_dict: dict):
415    """Raise a PumpWood exception from a serialized error dict.
416
417    Accepts output from `PumpWoodException.to_dict()` or compatible
418    payloads using `type` as the exception class name.
419
420    Args:
421        exception_dict (dict):
422            Serialized exception data. Expected keys are `type` (or
423            legacy `exception_name`), `message_not_fmt`, `payload`,
424            `status_code`, `translate`, `tag`, and `parallel`.
425
426    Returns:
427        None:
428            This function does not return as it always raises an
429            exception.
430
431    Raises:
432        PumpWoodOtherException:
433            If the specified exception type is not found in the
434            registry.
435        PumpWoodException:
436            The specific exception mapped to the exception type.
437    """
438    exception_name = exception_dict.get("type") or exception_dict.get(
439        "exception_name")
440    message_not_fmt = exception_dict.get("message_not_fmt")
441    payload = exception_dict.get("payload")
442    status_code = exception_dict.get("status_code")
443    translate = exception_dict.get("translate", False)
444    tag = exception_dict.get("tag", '')
445    parallel = exception_dict.get("parallel", False)
446
447    raise_pumpwood_exception(
448        exception_name=exception_name,
449        message=message_not_fmt, payload=payload, status_code=status_code,
450        translate=translate, tag=tag, parallel=parallel)
class PumpWoodException(builtins.Exception):
 11class PumpWoodException(Exception): # NOQA
 12    """Special exception used in PumpWood systems.
 13
 14    It permits handling raises in applications by serializing responses
 15    with ``to_dict`` and exposing the HTTP status as ``status_code``.
 16    """
 17
 18    status_code: int = 400
 19    """PumpWoodException will return status 400 on Pumpwood backend."""
 20
 21    message: str
 22    """Message associated with raise."""
 23
 24    payload: dict
 25    """Dictionary payload returned by ``to_dict`` and used to format
 26       the message string."""
 27
 28    i8n_object: None
 29    """I8n object used to translate the message."""
 30
 31    was_translated: bool
 32    """If the message was translated."""
 33
 34    tag: str
 35    """Tag used to disambiguate translation context."""
 36
 37    parallel: bool
 38    """If error was raised on a parallel request."""
 39
 40    def __repr__(self):
 41        """@private."""
 42        message_fmt = self.format_message()
 43        template = "{class_name}[status_code={status_code}]: " + \
 44            "{message_fmt}\nerror payload={payload}"
 45        return template.format(
 46            class_name=self.__class__.__name__,
 47            status_code=self.status_code,
 48            message_fmt=message_fmt,
 49            payload=self.payload)
 50
 51    def __str__(self):
 52        """@private."""
 53        message_fmt = self.format_message()
 54        template = "{class_name}[status_code={status_code}]: " + \
 55            "{message_fmt}\nerror payload={payload}"
 56        return template.format(
 57            class_name=self.__class__.__name__,
 58            status_code=self.status_code,
 59            message_fmt=message_fmt,
 60            payload=self.payload)
 61
 62    def __init__(self, message: str, payload: dict = None,
 63                 status_code: int = None, tag: str = '',
 64                 parallel: bool = False, i8n_object: None = None,
 65                 was_translated: bool = False):
 66        """Initialize the PumpWood exception.
 67
 68        Args:
 69            message (str):
 70                Message formatted with payload data using ``{key}``
 71                placeholders.
 72            payload (dict):
 73                Payload data passed as a dictionary. Returned in
 74                ``to_dict`` and used to format the message.
 75                Defaults to None.
 76            status_code (int):
 77                HTTP status code override. Defaults to None.
 78            i8n_object (None):
 79                I8n object used to translate the message.
 80                Defaults to None.
 81            tag (str):
 82                Tag used to disambiguate translation context.
 83                Defaults to empty string.
 84            parallel (bool):
 85                Whether the error occurred during parallel work.
 86                Defaults to False.
 87        """
 88        Exception.__init__(self)
 89
 90        # Initialize payload to avoid mutable default issues
 91        if payload is None:
 92            payload = {}
 93
 94        self.message = message
 95        if status_code is not None:
 96            self.status_code = status_code
 97        self.payload = payload
 98        self.i8n_object = i8n_object
 99        self.was_translated = was_translated
100        self.tag = tag
101        self.parallel = parallel
102
103    def format_message(self) -> str:
104        """Format exception message using payload data.
105
106        Substitute placeholders at exception message with payload.
107
108        Returns:
109            str:
110                Message with placeholders substituted from payload data.
111        """
112        try:
113            # If i8n_object is not None and was_translated is False,
114            # use it to translate the message
115            message = self.message
116            if (self.i8n_object is not None) and (not self.was_translated):
117                message = self.i8n_object.t(
118                    sentence=self.message, tag=self.tag)
119            return message.format(**self.payload)
120        except Exception:
121            return self.message + "\n** format error **"
122
123    def to_dict(self) -> dict[str, Any]:
124        """Serialize exception object for API response.
125
126        Returns:
127            dict[str, Any]:
128                Dictionary with keys:
129            - **payload [dict]:** Payload associated with the raise.
130            - **type [str]:** Name of the exception class.
131            - **message_not_fmt [str]:** Message without payload
132                substitution.
133            - **message [str]:** Message formatted with payload data.
134            - **status_code [int]:** HTTP status code for the exception.
135            - **translate [bool]:** Whether the message should be
136                translated.
137            - **tag [str]:** Tag used to disambiguate translation
138                context.
139            - **parallel [bool]:** Whether the error was from parallel
140                work.
141        """
142        message_fmt = self.format_message()
143        rv = {
144            "__error__": 'PumpWoodException',
145            "type": self.__class__.__name__,
146            "payload": self.payload,
147            "message_not_fmt": self.message,
148            "message": message_fmt,
149            "status_code": self.status_code,
150            "was_translated": self.was_translated,
151            "tag": self.tag,
152            "parallel": self.parallel}
153        return rv

Special exception used in PumpWood systems.

It permits handling raises in applications by serializing responses with to_dict and exposing the HTTP status as status_code.

PumpWoodException( message: str, payload: dict = None, status_code: int = None, tag: str = '', parallel: bool = False, i8n_object: None = None, was_translated: bool = False)
 62    def __init__(self, message: str, payload: dict = None,
 63                 status_code: int = None, tag: str = '',
 64                 parallel: bool = False, i8n_object: None = None,
 65                 was_translated: bool = False):
 66        """Initialize the PumpWood exception.
 67
 68        Args:
 69            message (str):
 70                Message formatted with payload data using ``{key}``
 71                placeholders.
 72            payload (dict):
 73                Payload data passed as a dictionary. Returned in
 74                ``to_dict`` and used to format the message.
 75                Defaults to None.
 76            status_code (int):
 77                HTTP status code override. Defaults to None.
 78            i8n_object (None):
 79                I8n object used to translate the message.
 80                Defaults to None.
 81            tag (str):
 82                Tag used to disambiguate translation context.
 83                Defaults to empty string.
 84            parallel (bool):
 85                Whether the error occurred during parallel work.
 86                Defaults to False.
 87        """
 88        Exception.__init__(self)
 89
 90        # Initialize payload to avoid mutable default issues
 91        if payload is None:
 92            payload = {}
 93
 94        self.message = message
 95        if status_code is not None:
 96            self.status_code = status_code
 97        self.payload = payload
 98        self.i8n_object = i8n_object
 99        self.was_translated = was_translated
100        self.tag = tag
101        self.parallel = parallel

Initialize the PumpWood exception.

Arguments:
  • message (str): Message formatted with payload data using {key} placeholders.
  • payload (dict): Payload data passed as a dictionary. Returned in to_dict and used to format the message. Defaults to None.
  • status_code (int): HTTP status code override. Defaults to None.
  • i8n_object (None): I8n object used to translate the message. Defaults to None.
  • tag (str): Tag used to disambiguate translation context. Defaults to empty string.
  • parallel (bool): Whether the error occurred during parallel work. Defaults to False.
status_code: int = 400

PumpWoodException will return status 400 on Pumpwood backend.

message: str

Message associated with raise.

payload: dict

Dictionary payload returned by to_dict and used to format the message string.

i8n_object: None

I8n object used to translate the message.

was_translated: bool

If the message was translated.

tag: str

Tag used to disambiguate translation context.

parallel: bool

If error was raised on a parallel request.

def format_message(self) -> str:
103    def format_message(self) -> str:
104        """Format exception message using payload data.
105
106        Substitute placeholders at exception message with payload.
107
108        Returns:
109            str:
110                Message with placeholders substituted from payload data.
111        """
112        try:
113            # If i8n_object is not None and was_translated is False,
114            # use it to translate the message
115            message = self.message
116            if (self.i8n_object is not None) and (not self.was_translated):
117                message = self.i8n_object.t(
118                    sentence=self.message, tag=self.tag)
119            return message.format(**self.payload)
120        except Exception:
121            return self.message + "\n** format error **"

Format exception message using payload data.

Substitute placeholders at exception message with payload.

Returns:

str: Message with placeholders substituted from payload data.

def to_dict(self) -> dict[str, typing.Any]:
123    def to_dict(self) -> dict[str, Any]:
124        """Serialize exception object for API response.
125
126        Returns:
127            dict[str, Any]:
128                Dictionary with keys:
129            - **payload [dict]:** Payload associated with the raise.
130            - **type [str]:** Name of the exception class.
131            - **message_not_fmt [str]:** Message without payload
132                substitution.
133            - **message [str]:** Message formatted with payload data.
134            - **status_code [int]:** HTTP status code for the exception.
135            - **translate [bool]:** Whether the message should be
136                translated.
137            - **tag [str]:** Tag used to disambiguate translation
138                context.
139            - **parallel [bool]:** Whether the error was from parallel
140                work.
141        """
142        message_fmt = self.format_message()
143        rv = {
144            "__error__": 'PumpWoodException',
145            "type": self.__class__.__name__,
146            "payload": self.payload,
147            "message_not_fmt": self.message,
148            "message": message_fmt,
149            "status_code": self.status_code,
150            "was_translated": self.was_translated,
151            "tag": self.tag,
152            "parallel": self.parallel}
153        return rv

Serialize exception object for API response.

Returns:

dict[str, Any]: Dictionary with keys:

  • payload [dict]: Payload associated with the raise.
  • type [str]: Name of the exception class.
  • message_not_fmt [str]: Message without payload substitution.
  • message [str]: Message formatted with payload data.
  • status_code [int]: HTTP status code for the exception.
  • translate [bool]: Whether the message should be translated.
  • tag [str]: Tag used to disambiguate translation context.
  • parallel [bool]: Whether the error was from parallel work.
class PumpWoodDataLoadingException(PumpWoodException):
156class PumpWoodDataLoadingException(PumpWoodException):
157    """Problem when loading data at dataloaders and to_load models."""
158
159    pass

Problem when loading data at dataloaders and to_load models.

class PumpWoodDatabaseError(PumpWoodException):
162class PumpWoodDatabaseError(PumpWoodException):
163    """Errors raised by Postgres and not treated by other handlers."""
164
165    pass

Errors raised by Postgres and not treated by other handlers.

class PumpWoodUniqueDatabaseError(PumpWoodException):
168class PumpWoodUniqueDatabaseError(PumpWoodException):
169    """Unique errors raised by Postgres."""
170
171    pass

Unique errors raised by Postgres.

class PumpWoodDataTransformationException(PumpWoodException):
174class PumpWoodDataTransformationException(PumpWoodException):
175    """Problem when transforming model data."""
176
177    pass

Problem when transforming model data.

class PumpWoodWrongParameters(PumpWoodException):
180class PumpWoodWrongParameters(PumpWoodException):
181    """Invalid or missing request parameters."""
182
183    pass

Invalid or missing request parameters.

class PumpWoodObjectSavingException(PumpWoodException):
186class PumpWoodObjectSavingException(PumpWoodException):
187    """Problem when saving object data."""
188
189    pass

Problem when saving object data.

class PumpWoodObjectDeleteException(PumpWoodException):
192class PumpWoodObjectDeleteException(PumpWoodException):
193    """Problem when deleting object data."""
194
195    pass

Problem when deleting object data.

class PumpWoodActionArgsException(PumpWoodException):
198class PumpWoodActionArgsException(PumpWoodException):
199    """Missing arguments to perform action."""
200
201    pass

Missing arguments to perform action.

class PumpWoodUnauthorized(PumpWoodException):
204class PumpWoodUnauthorized(PumpWoodException):
205    """User is unauthorized to perform the action."""
206
207    status_code = 401

User is unauthorized to perform the action.

status_code = 401

PumpWoodException will return status 400 on Pumpwood backend.

class PumpWoodForbidden(PumpWoodException):
210class PumpWoodForbidden(PumpWoodException):
211    """Action is not permitted."""
212
213    status_code = 403

Action is not permitted.

status_code = 403

PumpWoodException will return status 400 on Pumpwood backend.

class PumpWoodObjectDoesNotExist(PumpWoodException):
216class PumpWoodObjectDoesNotExist(PumpWoodException):
217    """Object not found in database."""
218
219    status_code = 404

Object not found in database.

status_code = 404

PumpWoodException will return status 400 on Pumpwood backend.

class PumpWoodQueryException(PumpWoodException):
222class PumpWoodQueryException(PumpWoodException):
223    """Problem when querying data, like wrong fields or operators."""
224
225    pass

Problem when querying data, like wrong fields or operators.

class PumpWoodIntegrityError(PumpWoodException):
228class PumpWoodIntegrityError(PumpWoodException):
229    """Problem when saving data due to IntegrityError."""
230
231    pass

Problem when saving data due to IntegrityError.

class PumpWoodNotImplementedError(PumpWoodException):
234class PumpWoodNotImplementedError(PumpWoodException):
235    """Feature or operation is not implemented."""
236
237    pass

Feature or operation is not implemented.

class PumpWoodMicroserviceUnavailableError(PumpWoodException):
240class PumpWoodMicroserviceUnavailableError(PumpWoodException):
241    """Microservice is unavailable or was not deployed."""
242
243    pass

Microservice is unavailable or was not deployed.

class PumpWoodMFAError(PumpWoodException):
246class PumpWoodMFAError(PumpWoodException):
247    """Problem when using MFA."""
248
249    pass

Problem when using MFA.

class PumpWoodJSONLoadError(PumpWoodException):
252class PumpWoodJSONLoadError(PumpWoodException):
253    """Problem loading JSON data from a request."""
254
255    pass

Problem loading JSON data from a request.

class PumpWoodCacheError(PumpWoodException):
258class PumpWoodCacheError(PumpWoodException):
259    """Problem using the PumpWood cache."""
260
261    pass

Problem using the PumpWood cache.

class PumpWoodOtherException(PumpWoodException):
264class PumpWoodOtherException(PumpWoodException):
265    """Unhandled or unmapped server error."""
266
267    status_code = 500
268
269    def __init__(self, message: str, payload: dict = None,
270                 status_code: int = None, was_translated: bool = False,
271                 tag: str = '', parallel: bool = False):
272        """Initialize PumpWoodOtherException.
273
274        Args:
275            message (str):
276                Message formatted with payload data using ``{key}``
277                placeholders. Truncated to 1000 characters.
278            payload (dict):
279                Payload data passed as a dictionary. Returned in
280                ``to_dict`` and used to format the message.
281                Defaults to None.
282            was_translated (bool):
283                If the message was translated. Defaults to False.
284            tag (str):
285                Tag used to disambiguate translation context.
286                Defaults to empty string.
287            status_code (int):
288                Change the default status code of the exception.
289                Defaults to None.
290            parallel (bool):
291                Whether the error occurred during parallel work.
292                Defaults to False.
293        """
294        Exception.__init__(self)
295
296        # Initialize payload to avoid mutable default issues
297        if payload is None:
298            payload = {}
299
300        # Limit size of the error, it is expected that other exceptions
301        # may have long text from kong or other between services
302        self.message = message[:1000]
303
304        if status_code is not None:
305            self.status_code = status_code
306        self.payload = payload
307
308        # Other exceptions are never translated are never translated
309        self.was_translated = False
310        self.tag = tag
311        self.parallel = parallel
312
313    def format_message(self) -> str:
314        """Return the message without formatting.
315        
316        Other exceptions are never translated, so the message is not formatted
317        and returned as is.
318        """
319        return self.message

Unhandled or unmapped server error.

PumpWoodOtherException( message: str, payload: dict = None, status_code: int = None, was_translated: bool = False, tag: str = '', parallel: bool = False)
269    def __init__(self, message: str, payload: dict = None,
270                 status_code: int = None, was_translated: bool = False,
271                 tag: str = '', parallel: bool = False):
272        """Initialize PumpWoodOtherException.
273
274        Args:
275            message (str):
276                Message formatted with payload data using ``{key}``
277                placeholders. Truncated to 1000 characters.
278            payload (dict):
279                Payload data passed as a dictionary. Returned in
280                ``to_dict`` and used to format the message.
281                Defaults to None.
282            was_translated (bool):
283                If the message was translated. Defaults to False.
284            tag (str):
285                Tag used to disambiguate translation context.
286                Defaults to empty string.
287            status_code (int):
288                Change the default status code of the exception.
289                Defaults to None.
290            parallel (bool):
291                Whether the error occurred during parallel work.
292                Defaults to False.
293        """
294        Exception.__init__(self)
295
296        # Initialize payload to avoid mutable default issues
297        if payload is None:
298            payload = {}
299
300        # Limit size of the error, it is expected that other exceptions
301        # may have long text from kong or other between services
302        self.message = message[:1000]
303
304        if status_code is not None:
305            self.status_code = status_code
306        self.payload = payload
307
308        # Other exceptions are never translated are never translated
309        self.was_translated = False
310        self.tag = tag
311        self.parallel = parallel

Initialize PumpWoodOtherException.

Arguments:
  • message (str): Message formatted with payload data using {key} placeholders. Truncated to 1000 characters.
  • payload (dict): Payload data passed as a dictionary. Returned in to_dict and used to format the message. Defaults to None.
  • was_translated (bool): If the message was translated. Defaults to False.
  • tag (str): Tag used to disambiguate translation context. Defaults to empty string.
  • status_code (int): Change the default status code of the exception. Defaults to None.
  • parallel (bool): Whether the error occurred during parallel work. Defaults to False.
status_code = 500

PumpWoodException will return status 400 on Pumpwood backend.

message

Message associated with raise.

payload

Dictionary payload returned by to_dict and used to format the message string.

was_translated

If the message was translated.

tag

Tag used to disambiguate translation context.

parallel

If error was raised on a parallel request.

def format_message(self) -> str:
313    def format_message(self) -> str:
314        """Return the message without formatting.
315        
316        Other exceptions are never translated, so the message is not formatted
317        and returned as is.
318        """
319        return self.message

Return the message without formatting.

Other exceptions are never translated, so the message is not formatted and returned as is.

class AirflowMicroServiceException(PumpWoodException):
322class AirflowMicroServiceException(PumpWoodException):
323    """Exception raised from AirflowMicroService."""
324
325    pass

Exception raised from AirflowMicroService.

exceptions_dict = {'PumpWoodException': <class 'PumpWoodException'>, 'PumpWoodDataLoadingException': <class 'PumpWoodDataLoadingException'>, 'PumpWoodDatabaseError': <class 'PumpWoodDatabaseError'>, 'PumpWoodDataTransformationException': <class 'PumpWoodDataTransformationException'>, 'PumpWoodWrongParameters': <class 'PumpWoodWrongParameters'>, 'PumpWoodObjectSavingException': <class 'PumpWoodObjectSavingException'>, 'PumpWoodObjectDeleteException': <class 'PumpWoodObjectDeleteException'>, 'PumpWoodActionArgsException': <class 'PumpWoodActionArgsException'>, 'PumpWoodUnauthorized': <class 'PumpWoodUnauthorized'>, 'PumpWoodForbidden': <class 'PumpWoodForbidden'>, 'PumpWoodObjectDoesNotExist': <class 'PumpWoodObjectDoesNotExist'>, 'PumpWoodQueryException': <class 'PumpWoodQueryException'>, 'PumpWoodIntegrityError': <class 'PumpWoodIntegrityError'>, 'PumpWoodNotImplementedError': <class 'PumpWoodNotImplementedError'>, 'PumpWoodMicroserviceUnavailableError': <class 'PumpWoodMicroserviceUnavailableError'>, 'PumpWoodMFAError': <class 'PumpWoodMFAError'>, 'PumpWoodJSONLoadError': <class 'PumpWoodJSONLoadError'>, 'PumpWoodCacheError': <class 'PumpWoodCacheError'>, 'PumpWoodOtherException': <class 'PumpWoodOtherException'>, 'AirflowMicroServiceException': <class 'AirflowMicroServiceException'>, 'PumpWoodUniqueDatabaseError': <class 'PumpWoodUniqueDatabaseError'>}

Dictionary mapping exception class names to types.

Used by backends and microservices to re-raise PumpWood exceptions.

def raise_pumpwood_exception( exception_name: str, message: str, payload: dict = None, status_code: int = None, translate: bool = False, tag: str = '', parallel: bool = False):
359def raise_pumpwood_exception(exception_name: str, message: str,
360                             payload: dict = None, status_code: int = None,
361                             translate: bool = False, tag: str = '',
362                             parallel: bool = False):
363    """Raise a PumpWood exception based on its name.
364
365    Args:
366        exception_name (str):
367            Name of the exception to be retrieved and raised.
368        message (str):
369            The error message associated with the exception.
370        payload (dict):
371            A dictionary containing additional data for the exception.
372            Defaults to None.
373        status_code (int):
374            HTTP status code to be returned. Defaults to None.
375        translate (bool):
376            Whether the message should be translated. Defaults to False.
377        tag (str):
378            Tag used to disambiguate translation context.
379            Defaults to empty string.
380        parallel (bool):
381            If the exception happened during parallel processing.
382            Defaults to False.
383
384    Returns:
385        None:
386            This function does not return; it always raises an exception.
387
388    Raises:
389        PumpWoodOtherException:
390            If ``exception_name`` is not found in the registry.
391        PumpWoodException:
392            The specific exception mapped to ``exception_name``.
393    """
394    # Initialize payload to avoid mutable default issues
395    if payload is None:
396        payload = {}
397
398    pumpwood_exception = exceptions_dict.get(exception_name)
399    if pumpwood_exception is None:
400        msg = (
401            "exception_name [{exception_name}] not found in PumpWood "
402            "Exceptions. Check implementation")
403        logger.error(
404            msg.format(exception_name=exception_name))
405        raise PumpWoodOtherException(
406            msg.format(exception_name=exception_name),
407            payload=payload, status_code=status_code,
408            tag=tag, parallel=parallel)
409    else:
410        raise pumpwood_exception(
411            message=message, payload=payload, status_code=status_code,
412            translate=translate, tag=tag, parallel=parallel)

Raise a PumpWood exception based on its name.

Arguments:
  • exception_name (str): Name of the exception to be retrieved and raised.
  • message (str): The error message associated with the exception.
  • payload (dict): A dictionary containing additional data for the exception. Defaults to None.
  • status_code (int): HTTP status code to be returned. Defaults to None.
  • translate (bool): Whether the message should be translated. Defaults to False.
  • tag (str): Tag used to disambiguate translation context. Defaults to empty string.
  • parallel (bool): If the exception happened during parallel processing. Defaults to False.
Returns:

None: This function does not return; it always raises an exception.

Raises:
  • PumpWoodOtherException: If exception_name is not found in the registry.
  • PumpWoodException: The specific exception mapped to exception_name.
def raise_from_dict(exception_dict: dict):
415def raise_from_dict(exception_dict: dict):
416    """Raise a PumpWood exception from a serialized error dict.
417
418    Accepts output from `PumpWoodException.to_dict()` or compatible
419    payloads using `type` as the exception class name.
420
421    Args:
422        exception_dict (dict):
423            Serialized exception data. Expected keys are `type` (or
424            legacy `exception_name`), `message_not_fmt`, `payload`,
425            `status_code`, `translate`, `tag`, and `parallel`.
426
427    Returns:
428        None:
429            This function does not return as it always raises an
430            exception.
431
432    Raises:
433        PumpWoodOtherException:
434            If the specified exception type is not found in the
435            registry.
436        PumpWoodException:
437            The specific exception mapped to the exception type.
438    """
439    exception_name = exception_dict.get("type") or exception_dict.get(
440        "exception_name")
441    message_not_fmt = exception_dict.get("message_not_fmt")
442    payload = exception_dict.get("payload")
443    status_code = exception_dict.get("status_code")
444    translate = exception_dict.get("translate", False)
445    tag = exception_dict.get("tag", '')
446    parallel = exception_dict.get("parallel", False)
447
448    raise_pumpwood_exception(
449        exception_name=exception_name,
450        message=message_not_fmt, payload=payload, status_code=status_code,
451        translate=translate, tag=tag, parallel=parallel)

Raise a PumpWood exception from a serialized error dict.

Accepts output from PumpWoodException.to_dict() or compatible payloads using type as the exception class name.

Arguments:
  • exception_dict (dict): Serialized exception data. Expected keys are type (or legacy exception_name), message_not_fmt, payload, status_code, translate, tag, and parallel.
Returns:

None: This function does not return as it always raises an exception.

Raises:
  • PumpWoodOtherException: If the specified exception type is not found in the registry.
  • PumpWoodException: The specific exception mapped to the exception type.