0
# Resource Management
1
2
Classes for managing icons, sounds, and file attachments with support for both file paths, URIs, and system-named resources across different platforms.
3
4
## Capabilities
5
6
### Icon
7
8
Icon resource class supporting file paths, URIs, and system-named icons with automatic format conversion and platform compatibility.
9
10
```python { .api }
11
@dataclass(frozen=True)
12
class Icon(Resource):
13
path: Path | None = None
14
"""Local file system path to icon image file"""
15
16
uri: str | None = None
17
"""URI reference to icon resource (file://, http://, etc.)"""
18
19
name: str | None = None
20
"""System icon name (platform-specific named icons)"""
21
22
def as_uri(self) -> str:
23
"""
24
Convert resource to URI string format.
25
26
Returns:
27
str: URI representation of the icon resource
28
29
Raises:
30
AttributeError: If no path, URI, or name is provided
31
"""
32
33
def as_path(self) -> Path:
34
"""
35
Convert resource to Path object.
36
37
Note: URI scheme information is lost during conversion.
38
39
Returns:
40
Path: Path representation of the icon resource
41
42
Raises:
43
AttributeError: If no path or URI is provided
44
"""
45
46
def as_name(self) -> str:
47
"""
48
Get system icon name.
49
50
Returns:
51
str: System icon name
52
53
Raises:
54
AttributeError: If no name is provided
55
"""
56
57
def is_named(self) -> bool:
58
"""
59
Check if icon was initialized with system name.
60
61
Returns:
62
bool: True if using named system icon
63
"""
64
65
def is_file(self) -> bool:
66
"""
67
Check if icon was initialized with file path or URI.
68
69
Returns:
70
bool: True if using file-based icon
71
"""
72
```
73
74
### Sound
75
76
Sound resource class for notification audio with same interface as Icon, supporting file-based and system-named sounds.
77
78
```python { .api }
79
@dataclass(frozen=True)
80
class Sound(Resource):
81
path: Path | None = None
82
"""Local file system path to sound file"""
83
84
uri: str | None = None
85
"""URI reference to sound resource"""
86
87
name: str | None = None
88
"""System sound name (e.g., 'default', 'Tink', 'Blow')"""
89
90
def as_uri(self) -> str:
91
"""Convert sound resource to URI string format"""
92
93
def as_path(self) -> Path:
94
"""Convert sound resource to Path object"""
95
96
def as_name(self) -> str:
97
"""Get system sound name"""
98
99
def is_named(self) -> bool:
100
"""Check if using named system sound"""
101
102
def is_file(self) -> bool:
103
"""Check if using file-based sound"""
104
```
105
106
### Attachment
107
108
File attachment class for notification previews, limited to file paths and URIs (no system-named attachments).
109
110
```python { .api }
111
@dataclass(frozen=True)
112
class Attachment(FileResource):
113
path: Path | None = None
114
"""Local file system path to attachment file"""
115
116
uri: str | None = None
117
"""URI reference to attachment file"""
118
119
def as_uri(self) -> str:
120
"""
121
Convert attachment to URI string format.
122
123
Returns:
124
str: URI representation of the attachment
125
"""
126
127
def as_path(self) -> Path:
128
"""
129
Convert attachment to Path object.
130
131
Returns:
132
Path: Path representation of the attachment
133
"""
134
```
135
136
### Resource Base Classes
137
138
Base classes providing common resource management functionality.
139
140
```python { .api }
141
@dataclass(frozen=True)
142
class FileResource:
143
"""Base class for file-based resources (path or URI only)"""
144
145
path: Path | None = None
146
"""Local file system path"""
147
148
uri: str | None = None
149
"""URI reference to resource"""
150
151
def __post_init__(self) -> None:
152
"""Validates that only one field is set and at least one is provided"""
153
154
def as_uri(self) -> str:
155
"""Convert to URI string format"""
156
157
def as_path(self) -> Path:
158
"""Convert to Path object"""
159
160
@dataclass(frozen=True)
161
class Resource(FileResource):
162
"""Base class for resources supporting both files and system names"""
163
164
name: str | None = None
165
"""System resource name"""
166
167
def as_name(self) -> str:
168
"""Get system resource name"""
169
170
def is_named(self) -> bool:
171
"""Check if using system name"""
172
173
def is_file(self) -> bool:
174
"""Check if using file path or URI"""
175
```
176
177
### Default Resources
178
179
Pre-configured resource constants for common use cases.
180
181
```python { .api }
182
DEFAULT_ICON: Icon
183
"""Default Python icon included with the package"""
184
185
DEFAULT_SOUND: Sound
186
"""Default system notification sound (platform-appropriate)"""
187
```
188
189
## Usage Examples
190
191
### File-based Icons
192
193
```python
194
from desktop_notifier import Icon, DesktopNotifier
195
from pathlib import Path
196
197
# Icon from local file path
198
app_icon = Icon(path=Path("/path/to/app-icon.png"))
199
200
# Icon from URI (local or remote)
201
web_icon = Icon(uri="https://example.com/icon.png")
202
file_uri_icon = Icon(uri="file:///absolute/path/to/icon.png")
203
204
# Use with notifier
205
notifier = DesktopNotifier(app_name="My App", app_icon=app_icon)
206
207
# Or per-notification
208
await notifier.send(
209
title="Custom Icon",
210
message="This notification has a custom icon",
211
icon=Icon(path=Path("/path/to/notification-icon.png"))
212
)
213
```
214
215
### System Named Icons
216
217
```python
218
from desktop_notifier import Icon
219
220
# Common system icons (platform-dependent availability)
221
info_icon = Icon(name="info")
222
warning_icon = Icon(name="warning")
223
error_icon = Icon(name="error")
224
mail_icon = Icon(name="mail-unread")
225
226
# Check what type of icon
227
print(f"Using named icon: {info_icon.is_named()}") # True
228
print(f"Using file icon: {info_icon.is_file()}") # False
229
230
# Linux-specific system icons
231
linux_icons = [
232
Icon(name="dialog-information"),
233
Icon(name="dialog-warning"),
234
Icon(name="dialog-error"),
235
Icon(name="emblem-important"),
236
Icon(name="call-start"),
237
Icon(name="call-stop")
238
]
239
```
240
241
### Sound Resources
242
243
```python
244
from desktop_notifier import Sound, DEFAULT_SOUND
245
246
# System named sounds
247
default_sound = DEFAULT_SOUND # Sound(name="default")
248
custom_system_sound = Sound(name="Tink") # macOS system sound
249
alert_sound = Sound(name="Blow") # Another macOS sound
250
251
# File-based sounds
252
custom_sound = Sound(path=Path("/path/to/notification.wav"))
253
remote_sound = Sound(uri="https://example.com/chime.mp3")
254
255
# Use with notifications
256
await notifier.send(
257
title="Sound Test",
258
message="This plays a custom sound",
259
sound=custom_sound
260
)
261
262
# Platform-specific sound names
263
if platform.system() == "Darwin": # macOS
264
sounds = [
265
Sound(name="Basso"),
266
Sound(name="Blow"),
267
Sound(name="Bottle"),
268
Sound(name="Frog"),
269
Sound(name="Funk"),
270
Sound(name="Glass"),
271
Sound(name="Hero"),
272
Sound(name="Morse"),
273
Sound(name="Ping"),
274
Sound(name="Pop"),
275
Sound(name="Purr"),
276
Sound(name="Sosumi"),
277
Sound(name="Submarine"),
278
Sound(name="Tink")
279
]
280
elif platform.system() == "Windows":
281
sounds = [
282
Sound(name="ms-winsoundevent:Notification.Default"),
283
Sound(name="ms-winsoundevent:Notification.IM"),
284
Sound(name="ms-winsoundevent:Notification.Mail"),
285
Sound(name="ms-winsoundevent:Notification.Reminder"),
286
Sound(name="ms-winsoundevent:Notification.SMS")
287
]
288
```
289
290
### File Attachments
291
292
```python
293
from desktop_notifier import Attachment
294
295
# Image attachments for preview
296
image_attachment = Attachment(path=Path("/photos/vacation.jpg"))
297
screenshot = Attachment(uri="file:///tmp/screenshot.png")
298
299
# Document attachments
300
pdf_doc = Attachment(path=Path("/documents/report.pdf"))
301
text_file = Attachment(uri="file:///logs/error.log")
302
303
# Use with notifications
304
await notifier.send(
305
title="Photo Shared",
306
message="New vacation photos are ready",
307
attachment=image_attachment,
308
on_clicked=lambda: print("User wants to view photos")
309
)
310
311
# Platform support varies for attachment types:
312
# - macOS: Most image formats, PDFs, some video formats
313
# - Windows: Images (PNG, JPG, GIF), some video formats
314
# - Linux: Depends on notification daemon implementation
315
```
316
317
### Resource Conversion and Validation
318
319
```python
320
from desktop_notifier import Icon, Sound, Attachment
321
from pathlib import Path
322
323
# Resource conversion utilities
324
icon = Icon(path=Path("/app/icon.png"))
325
326
# Convert between formats
327
icon_uri = icon.as_uri() # "file:///app/icon.png"
328
icon_path = icon.as_path() # Path("/app/icon.png")
329
330
# Type checking
331
print(f"Is file-based: {icon.is_file()}") # True
332
print(f"Is named: {icon.is_named()}") # False
333
334
# System named resource
335
system_icon = Icon(name="dialog-information")
336
print(f"System name: {system_icon.as_name()}") # "dialog-information"
337
338
# Validation - only one field can be set
339
try:
340
invalid_icon = Icon(path=Path("/icon.png"), name="warning")
341
# Raises RuntimeError: "Only a single field can be set"
342
except RuntimeError as e:
343
print(f"Validation error: {e}")
344
345
try:
346
empty_icon = Icon()
347
# Raises RuntimeError: "Either of ['path', 'uri', 'name'] must be set"
348
except RuntimeError as e:
349
print(f"Validation error: {e}")
350
```
351
352
### Dynamic Resource Selection
353
354
```python
355
import platform
356
from desktop_notifier import Icon, Sound
357
from pathlib import Path
358
359
def get_platform_appropriate_icon(icon_type: str) -> Icon:
360
"""Select best icon based on platform capabilities"""
361
if platform.system() == "Linux":
362
# Use freedesktop.org standard icon names
363
icon_map = {
364
"info": "dialog-information",
365
"warning": "dialog-warning",
366
"error": "dialog-error"
367
}
368
return Icon(name=icon_map.get(icon_type, "dialog-information"))
369
370
elif platform.system() in ["Darwin", "Windows"]:
371
# Use custom app icons on macOS/Windows
372
icon_path = Path(f"assets/icons/{icon_type}.png")
373
if icon_path.exists():
374
return Icon(path=icon_path)
375
else:
376
return Icon(name=icon_type) # Fallback to system
377
378
else:
379
# Fallback for other platforms
380
return Icon(name=icon_type)
381
382
def get_platform_appropriate_sound(sound_type: str) -> Sound:
383
"""Select best sound based on platform"""
384
if platform.system() == "Darwin":
385
sound_map = {
386
"success": "Tink",
387
"warning": "Basso",
388
"error": "Sosumi",
389
"message": "Ping"
390
}
391
return Sound(name=sound_map.get(sound_type, "default"))
392
393
elif platform.system() == "Windows":
394
sound_map = {
395
"success": "ms-winsoundevent:Notification.Default",
396
"message": "ms-winsoundevent:Notification.IM"
397
}
398
return Sound(name=sound_map.get(sound_type, "default"))
399
400
else:
401
return Sound(name="default")
402
403
# Usage
404
info_icon = get_platform_appropriate_icon("info")
405
success_sound = get_platform_appropriate_sound("success")
406
407
await notifier.send(
408
title="Platform Optimized",
409
message="This uses the best icon and sound for your OS",
410
icon=info_icon,
411
sound=success_sound
412
)
413
```