Coverage for dataexcept/exceptions/notification.py: 88%
26 statements
« prev ^ index » next coverage.py v7.15.4, created at 2026-09-03 20:46 +0000
« prev ^ index » next coverage.py v7.15.4, created at 2026-09-03 20:46 +0000
1# notification.py
2from ..redaction import redact_url
3from .base import JobError
6class NotificationError(JobError):
7 """Base exception for notification failures."""
9 def __init__(
10 self,
11 channel: str,
12 original_exception: Exception | None = None,
13 message: str | None = None,
14 ):
15 self.channel = channel
16 self.original_exception = original_exception
17 msg = message or f"Notification via '{channel}' failed"
18 if message is None and original_exception: 18 ↛ 19line 18 didn't jump to line 19 because the condition on line 18 was never true
19 msg += f": {original_exception}"
20 super().__init__(msg)
23class EmailError(NotificationError):
24 """Raised when sending an email fails."""
26 def __init__(
27 self,
28 recipient: str,
29 subject: str,
30 original_exception: Exception | None = None,
31 ):
32 self.recipient = recipient
33 self.subject = subject
34 # original_exception is set by NotificationError.__init__ below.
35 msg = f"Email to '{recipient}' with subject '{subject}' failed"
36 if original_exception: 36 ↛ 37line 36 didn't jump to line 37 because the condition on line 36 was never true
37 msg += f": {original_exception}"
38 super().__init__("email", original_exception, message=msg)
41class WebhookError(NotificationError):
42 """Raised when a webhook POST fails."""
44 # Slack, Discord and others put the secret in the webhook path, so keeping
45 # the path would defeat the redaction. This also applies to the scrubbing
46 # of the whole message, which is how a wrapped HTTP exception quoting the
47 # original URL gets its path dropped too.
48 _keep_url_path = False
50 def __init__(self, url: str, original_exception: Exception | None = None):
51 self.url = redact_url(url, keep_path=False)
52 # original_exception is set by NotificationError.__init__ below.
53 msg = f"Webhook to URL '{self.url}' failed"
54 if original_exception:
55 msg += f": {original_exception}"
56 super().__init__("webhook", original_exception, message=msg)