Skip to content

Commit ff32314

Browse files
authored
Merge pull request #198 from samschott/get-real-current-notifications
Get real current notifications on macOS
2 parents cff82b5 + 07b8fcd commit ff32314

File tree

3 files changed

+62
-2
lines changed

3 files changed

+62
-2
lines changed

src/desktop_notifier/backends/macos.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
foundation = load_library("Foundation")
3434
uns = load_library("UserNotifications")
3535

36+
UNNotification = ObjCClass("UNNotification")
3637
UNUserNotificationCenter = ObjCClass("UNUserNotificationCenter")
3738
UNMutableNotificationContent = ObjCClass("UNMutableNotificationContent")
3839
UNNotificationRequest = ObjCClass("UNNotificationRequest")
@@ -195,6 +196,28 @@ def handler(settings: objc_id) -> None:
195196

196197
return authorized
197198

199+
async def get_current_notifications(self) -> list[str]:
200+
future: Future[list[UNNotification]] = Future() # type:ignore[valid-type]
201+
202+
def handler(notifications: objc_id) -> None:
203+
notifications = py_from_ns(notifications)
204+
for notification in notifications:
205+
notification.retain()
206+
future.set_result(notifications)
207+
208+
self.nc.getDeliveredNotificationsWithCompletionHandler(handler)
209+
210+
notifications = await asyncio.wrap_future(future)
211+
identifiers = [
212+
str(n.request.identifier) # type:ignore[attr-defined]
213+
for n in notifications
214+
]
215+
216+
for notification in notifications:
217+
notification.autorelease() # type:ignore[attr-defined]
218+
219+
return identifiers
220+
198221
async def _send(self, notification: Notification) -> None:
199222
"""
200223
Uses UNUserNotificationCenter to schedule a notification.

tests/test_api.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
import asyncio
12
import sys
3+
import time
24
from pathlib import Path
35

46
import pytest
@@ -15,6 +17,19 @@
1517
)
1618

1719

20+
async def wait_for_notifications(
21+
notifier: DesktopNotifier, notification_count: int = 1, timeout_sec: float = 2.0
22+
) -> None:
23+
t0 = time.monotonic()
24+
25+
while time.monotonic() - t0 < timeout_sec:
26+
if len(await notifier.get_current_notifications()) == notification_count:
27+
return
28+
await asyncio.sleep(0.2)
29+
30+
raise TimeoutError("Timed out while waiting for notifications")
31+
32+
1833
@pytest.mark.asyncio
1934
async def test_request_authorisation(notifier: DesktopNotifier) -> None:
2035
"""
@@ -48,6 +63,7 @@ async def test_send(notifier: DesktopNotifier) -> None:
4863
thread="test_notifications",
4964
timeout=5,
5065
)
66+
await wait_for_notifications(notifier)
5167
assert notification in await notifier.get_current_notifications()
5268

5369

@@ -125,7 +141,9 @@ async def test_clear(notifier: DesktopNotifier) -> None:
125141
title="Julius Caesar",
126142
message="Et tu, Brute?",
127143
)
144+
await wait_for_notifications(notifier, 2)
128145
current_notifications = await notifier.get_current_notifications()
146+
129147
assert n0 in current_notifications
130148
assert n1 in current_notifications
131149

@@ -143,8 +161,9 @@ async def test_clear_all(notifier: DesktopNotifier) -> None:
143161
title="Julius Caesar",
144162
message="Et tu, Brute?",
145163
)
146-
164+
await wait_for_notifications(notifier, 2)
147165
current_notifications = await notifier.get_current_notifications()
166+
148167
assert n0 in current_notifications
149168
assert n1 in current_notifications
150169

tests/test_sync_api.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import sys
2+
import time
23

34
import pytest
45

@@ -11,6 +12,19 @@
1112
)
1213

1314

15+
def wait_for_notifications(
16+
notifier: DesktopNotifierSync, notification_count: int = 1, timeout_sec: float = 2.0
17+
) -> None:
18+
t0 = time.monotonic()
19+
20+
while time.monotonic() - t0 < timeout_sec:
21+
if len(notifier.get_current_notifications()) == notification_count:
22+
return
23+
time.sleep(0.2)
24+
25+
raise TimeoutError("Timed out while waiting for notifications")
26+
27+
1428
def test_send(notifier_sync: DesktopNotifierSync) -> None:
1529
notification = notifier_sync.send(
1630
title="Julius Caesar",
@@ -33,6 +47,7 @@ def test_send(notifier_sync: DesktopNotifierSync) -> None:
3347
thread="test_notifications",
3448
timeout=5,
3549
)
50+
wait_for_notifications(notifier_sync)
3651
assert notification in notifier_sync.get_current_notifications()
3752

3853

@@ -49,7 +64,9 @@ def test_clear(notifier_sync: DesktopNotifierSync) -> None:
4964
title="Julius Caesar",
5065
message="Et tu, Brute?",
5166
)
67+
wait_for_notifications(notifier_sync, 2)
5268
current_notifications = notifier_sync.get_current_notifications()
69+
5370
assert n0 in current_notifications
5471
assert n1 in current_notifications
5572

@@ -66,8 +83,9 @@ def test_clear_all(notifier_sync: DesktopNotifierSync) -> None:
6683
title="Julius Caesar",
6784
message="Et tu, Brute?",
6885
)
69-
86+
wait_for_notifications(notifier_sync, 2)
7087
current_notifications = notifier_sync.get_current_notifications()
88+
7189
assert n0 in current_notifications
7290
assert n1 in current_notifications
7391

0 commit comments

Comments
 (0)