ai-station/.venv/lib/python3.12/site-packages/textual/notifications.py

121 lines
3.6 KiB
Python

"""Provides classes for holding and managing notifications."""
from __future__ import annotations
from dataclasses import dataclass, field
from time import time
from typing import Iterator
from uuid import uuid4
from rich.repr import Result
from typing_extensions import Literal, Self, TypeAlias
from textual.message import Message
SeverityLevel: TypeAlias = Literal["information", "warning", "error"]
"""The severity level for a notification."""
@dataclass
class Notify(Message, bubble=False):
"""Message to show a notification."""
notification: Notification
@dataclass
class Notification:
"""Holds the details of a notification."""
message: str
"""The message for the notification."""
title: str = ""
"""The title for the notification."""
severity: SeverityLevel = "information"
"""The severity level for the notification."""
timeout: float = 5
"""The timeout (in seconds) for the notification."""
markup: bool = False
"""Render the notification message as content markup?"""
raised_at: float = field(default_factory=time)
"""The time when the notification was raised (in Unix time)."""
identity: str = field(default_factory=lambda: str(uuid4()))
"""The unique identity of the notification."""
@property
def time_left(self) -> float:
"""The time left until this notification expires"""
return (self.raised_at + self.timeout) - time()
@property
def has_expired(self) -> bool:
"""Has the notification expired?"""
return self.time_left <= 0
def __rich_repr__(self) -> Result:
yield "message", self.message
yield "title", self.title, ""
yield "severity", self.severity
yield "raised_it", self.raised_at
yield "identity", self.identity
yield "time_left", self.time_left
yield "has_expired", self.has_expired
class Notifications:
"""Class for managing a collection of notifications."""
def __init__(self) -> None:
"""Initialise the notification collection."""
self._notifications: dict[str, Notification] = {}
def _reap(self) -> Self:
"""Remove any expired notifications from the notification collection."""
for notification in list(self._notifications.values()):
if notification.has_expired:
del self._notifications[notification.identity]
return self
def add(self, notification: Notification) -> Self:
"""Add the given notification to the collection of managed notifications.
Args:
notification: The notification to add.
Returns:
Self.
"""
self._reap()._notifications[notification.identity] = notification
return self
def clear(self) -> Self:
"""Clear all the notifications."""
self._notifications.clear()
return self
def __len__(self) -> int:
"""The number of notifications."""
return len(self._reap()._notifications)
def __iter__(self) -> Iterator[Notification]:
return iter(self._reap()._notifications.values())
def __contains__(self, notification: Notification) -> bool:
return notification.identity in self._notifications
def __delitem__(self, notification: Notification) -> None:
try:
del self._reap()._notifications[notification.identity]
except KeyError:
# An attempt to remove a notification we don't know about is a
# no-op. What matters here is that the notification is forgotten
# about, and it looks like a caller has tried to be
# belt-and-braces. We're fine with this.
pass