CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-desktop-notifier

Python library for cross-platform desktop notifications

Pending
Overview
Eval results
Files

resources.mddocs/

Resource Management

Classes for managing icons, sounds, and file attachments with support for both file paths, URIs, and system-named resources across different platforms.

Capabilities

Icon

Icon resource class supporting file paths, URIs, and system-named icons with automatic format conversion and platform compatibility.

@dataclass(frozen=True)
class Icon(Resource):
    path: Path | None = None
    """Local file system path to icon image file"""
    
    uri: str | None = None
    """URI reference to icon resource (file://, http://, etc.)"""
    
    name: str | None = None
    """System icon name (platform-specific named icons)"""
    
    def as_uri(self) -> str:
        """
        Convert resource to URI string format.
        
        Returns:
        str: URI representation of the icon resource
        
        Raises:
        AttributeError: If no path, URI, or name is provided
        """
    
    def as_path(self) -> Path:
        """
        Convert resource to Path object.
        
        Note: URI scheme information is lost during conversion.
        
        Returns:
        Path: Path representation of the icon resource
        
        Raises:
        AttributeError: If no path or URI is provided
        """
    
    def as_name(self) -> str:
        """
        Get system icon name.
        
        Returns:
        str: System icon name
        
        Raises:
        AttributeError: If no name is provided
        """
    
    def is_named(self) -> bool:
        """
        Check if icon was initialized with system name.
        
        Returns:
        bool: True if using named system icon
        """
    
    def is_file(self) -> bool:
        """
        Check if icon was initialized with file path or URI.
        
        Returns:
        bool: True if using file-based icon
        """

Sound

Sound resource class for notification audio with same interface as Icon, supporting file-based and system-named sounds.

@dataclass(frozen=True)
class Sound(Resource):
    path: Path | None = None
    """Local file system path to sound file"""
    
    uri: str | None = None
    """URI reference to sound resource"""
    
    name: str | None = None
    """System sound name (e.g., 'default', 'Tink', 'Blow')"""
    
    def as_uri(self) -> str:
        """Convert sound resource to URI string format"""
    
    def as_path(self) -> Path:
        """Convert sound resource to Path object"""
    
    def as_name(self) -> str:
        """Get system sound name"""
    
    def is_named(self) -> bool:
        """Check if using named system sound"""
    
    def is_file(self) -> bool:
        """Check if using file-based sound"""

Attachment

File attachment class for notification previews, limited to file paths and URIs (no system-named attachments).

@dataclass(frozen=True)
class Attachment(FileResource):
    path: Path | None = None
    """Local file system path to attachment file"""
    
    uri: str | None = None
    """URI reference to attachment file"""
    
    def as_uri(self) -> str:
        """
        Convert attachment to URI string format.
        
        Returns:
        str: URI representation of the attachment
        """
    
    def as_path(self) -> Path:
        """
        Convert attachment to Path object.
        
        Returns:
        Path: Path representation of the attachment
        """

Resource Base Classes

Base classes providing common resource management functionality.

@dataclass(frozen=True)
class FileResource:
    """Base class for file-based resources (path or URI only)"""
    
    path: Path | None = None
    """Local file system path"""
    
    uri: str | None = None
    """URI reference to resource"""
    
    def __post_init__(self) -> None:
        """Validates that only one field is set and at least one is provided"""
    
    def as_uri(self) -> str:
        """Convert to URI string format"""
    
    def as_path(self) -> Path:
        """Convert to Path object"""

@dataclass(frozen=True) 
class Resource(FileResource):
    """Base class for resources supporting both files and system names"""
    
    name: str | None = None
    """System resource name"""
    
    def as_name(self) -> str:
        """Get system resource name"""
    
    def is_named(self) -> bool:
        """Check if using system name"""
    
    def is_file(self) -> bool:
        """Check if using file path or URI"""

Default Resources

Pre-configured resource constants for common use cases.

DEFAULT_ICON: Icon
"""Default Python icon included with the package"""

DEFAULT_SOUND: Sound  
"""Default system notification sound (platform-appropriate)"""

Usage Examples

File-based Icons

from desktop_notifier import Icon, DesktopNotifier
from pathlib import Path

# Icon from local file path
app_icon = Icon(path=Path("/path/to/app-icon.png"))

# Icon from URI (local or remote)
web_icon = Icon(uri="https://example.com/icon.png")
file_uri_icon = Icon(uri="file:///absolute/path/to/icon.png")

# Use with notifier
notifier = DesktopNotifier(app_name="My App", app_icon=app_icon)

# Or per-notification
await notifier.send(
    title="Custom Icon",
    message="This notification has a custom icon",
    icon=Icon(path=Path("/path/to/notification-icon.png"))
)

System Named Icons

from desktop_notifier import Icon

# Common system icons (platform-dependent availability)
info_icon = Icon(name="info")
warning_icon = Icon(name="warning") 
error_icon = Icon(name="error")
mail_icon = Icon(name="mail-unread")

# Check what type of icon
print(f"Using named icon: {info_icon.is_named()}")  # True
print(f"Using file icon: {info_icon.is_file()}")    # False

# Linux-specific system icons
linux_icons = [
    Icon(name="dialog-information"),
    Icon(name="dialog-warning"),
    Icon(name="dialog-error"),
    Icon(name="emblem-important"),
    Icon(name="call-start"),
    Icon(name="call-stop")
]

Sound Resources

from desktop_notifier import Sound, DEFAULT_SOUND

# System named sounds
default_sound = DEFAULT_SOUND  # Sound(name="default")
custom_system_sound = Sound(name="Tink")  # macOS system sound
alert_sound = Sound(name="Blow")          # Another macOS sound

# File-based sounds
custom_sound = Sound(path=Path("/path/to/notification.wav"))
remote_sound = Sound(uri="https://example.com/chime.mp3")

# Use with notifications
await notifier.send(
    title="Sound Test",
    message="This plays a custom sound",
    sound=custom_sound
)

# Platform-specific sound names
if platform.system() == "Darwin":  # macOS
    sounds = [
        Sound(name="Basso"),
        Sound(name="Blow"),
        Sound(name="Bottle"),
        Sound(name="Frog"),
        Sound(name="Funk"),
        Sound(name="Glass"),
        Sound(name="Hero"),
        Sound(name="Morse"),
        Sound(name="Ping"),
        Sound(name="Pop"),
        Sound(name="Purr"),
        Sound(name="Sosumi"),
        Sound(name="Submarine"),
        Sound(name="Tink")
    ]
elif platform.system() == "Windows":
    sounds = [
        Sound(name="ms-winsoundevent:Notification.Default"),
        Sound(name="ms-winsoundevent:Notification.IM"),
        Sound(name="ms-winsoundevent:Notification.Mail"),
        Sound(name="ms-winsoundevent:Notification.Reminder"),
        Sound(name="ms-winsoundevent:Notification.SMS")
    ]

File Attachments

from desktop_notifier import Attachment

# Image attachments for preview
image_attachment = Attachment(path=Path("/photos/vacation.jpg"))
screenshot = Attachment(uri="file:///tmp/screenshot.png")

# Document attachments  
pdf_doc = Attachment(path=Path("/documents/report.pdf"))
text_file = Attachment(uri="file:///logs/error.log")

# Use with notifications
await notifier.send(
    title="Photo Shared",
    message="New vacation photos are ready",
    attachment=image_attachment,
    on_clicked=lambda: print("User wants to view photos")
)

# Platform support varies for attachment types:
# - macOS: Most image formats, PDFs, some video formats
# - Windows: Images (PNG, JPG, GIF), some video formats
# - Linux: Depends on notification daemon implementation

Resource Conversion and Validation

from desktop_notifier import Icon, Sound, Attachment
from pathlib import Path

# Resource conversion utilities
icon = Icon(path=Path("/app/icon.png"))

# Convert between formats
icon_uri = icon.as_uri()        # "file:///app/icon.png"  
icon_path = icon.as_path()      # Path("/app/icon.png")

# Type checking
print(f"Is file-based: {icon.is_file()}")    # True
print(f"Is named: {icon.is_named()}")        # False

# System named resource
system_icon = Icon(name="dialog-information")
print(f"System name: {system_icon.as_name()}")  # "dialog-information"

# Validation - only one field can be set
try:
    invalid_icon = Icon(path=Path("/icon.png"), name="warning")
    # Raises RuntimeError: "Only a single field can be set"
except RuntimeError as e:
    print(f"Validation error: {e}")

try:
    empty_icon = Icon()
    # Raises RuntimeError: "Either of ['path', 'uri', 'name'] must be set"  
except RuntimeError as e:
    print(f"Validation error: {e}")

Dynamic Resource Selection

import platform
from desktop_notifier import Icon, Sound
from pathlib import Path

def get_platform_appropriate_icon(icon_type: str) -> Icon:
    """Select best icon based on platform capabilities"""
    if platform.system() == "Linux":
        # Use freedesktop.org standard icon names
        icon_map = {
            "info": "dialog-information",
            "warning": "dialog-warning", 
            "error": "dialog-error"
        }
        return Icon(name=icon_map.get(icon_type, "dialog-information"))
    
    elif platform.system() in ["Darwin", "Windows"]:
        # Use custom app icons on macOS/Windows
        icon_path = Path(f"assets/icons/{icon_type}.png")
        if icon_path.exists():
            return Icon(path=icon_path)
        else:
            return Icon(name=icon_type)  # Fallback to system
    
    else:
        # Fallback for other platforms
        return Icon(name=icon_type)

def get_platform_appropriate_sound(sound_type: str) -> Sound:
    """Select best sound based on platform"""
    if platform.system() == "Darwin":
        sound_map = {
            "success": "Tink",
            "warning": "Basso", 
            "error": "Sosumi",
            "message": "Ping"
        }
        return Sound(name=sound_map.get(sound_type, "default"))
    
    elif platform.system() == "Windows":
        sound_map = {
            "success": "ms-winsoundevent:Notification.Default",
            "message": "ms-winsoundevent:Notification.IM"
        }
        return Sound(name=sound_map.get(sound_type, "default"))
    
    else:
        return Sound(name="default")

# Usage
info_icon = get_platform_appropriate_icon("info")
success_sound = get_platform_appropriate_sound("success")

await notifier.send(
    title="Platform Optimized",
    message="This uses the best icon and sound for your OS",
    icon=info_icon,
    sound=success_sound
)

Install with Tessl CLI

npx tessl i tessl/pypi-desktop-notifier

docs

core-api.md

enums-constants.md

index.md

notification-components.md

resources.md

tile.json