Skip to content

Commit 6783884

Browse files
committed
Add shutdown method to gracefully quit internal tasks
1 parent 1119915 commit 6783884

File tree

5 files changed

+47
-11
lines changed

5 files changed

+47
-11
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66

77
## [Unreleased]
88

9+
### Added
10+
- A new method `shutdown` to gracefully quit internal tasks
11+
912
## [1.2.0] - 15th of October, 2024
1013

1114
### Changed

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
The MIT License
22

3-
Copyright (c) 2024 Tim Henkes (Syndace)
3+
Copyright (c) 2025 Tim Henkes (Syndace)
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

omemo/project.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"name" : "OMEMO",
55
"description" : "A Python implementation of the OMEMO Multi-End Message and Object Encryption protocol.",
66
"url" : "https://github.com/Syndace/python-omemo",
7-
"year" : "2024",
7+
"year" : "2025",
88
"author" : "Tim Henkes (Syndace)",
99
"author_email" : "[email protected]",
1010
"categories" : [

omemo/session_manager.py

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
import itertools
55
import logging
66
import secrets
7-
from typing import Dict, FrozenSet, List, NamedTuple, Optional, Set, Tuple, Type, TypeVar, Union, cast
7+
from typing import Awaitable, Dict, FrozenSet, List, NamedTuple, Optional, Set, Tuple, Type, TypeVar, Union, \
8+
cast
89
from typing_extensions import assert_never
910

1011
try:
@@ -247,6 +248,8 @@ def __init__(self) -> None:
247248
self.__pre_key_refill_threshold: int
248249
self.__identity_key_pair: IdentityKeyPair
249250
self.__synchronizing: bool
251+
self.__async_framework: AsyncFramework
252+
self.__signed_pre_key_management_task: Awaitable[None]
250253

251254
@classmethod
252255
async def create(
@@ -349,6 +352,7 @@ async def create(
349352
self.__pre_key_refill_threshold = pre_key_refill_threshold
350353
self.__identity_key_pair = await IdentityKeyPair.get(storage)
351354
self.__synchronizing = True
355+
self.__async_framework = async_framework
352356

353357
try:
354358
self.__own_device_id = (await self.__storage.load_primitive("/own_device_id", int)).from_just()
@@ -473,16 +477,15 @@ async def create(
473477
await self.purge_backend(namespace)
474478

475479
# Start signed pre key rotation management "in the background"
480+
signed_pre_key_management_coro = self.__manage_signed_pre_key_rotation(
481+
signed_pre_key_rotation_period,
482+
async_framework
483+
)
484+
476485
if async_framework is AsyncFramework.ASYNCIO:
477-
asyncio.ensure_future(self.__manage_signed_pre_key_rotation(
478-
signed_pre_key_rotation_period,
479-
async_framework
480-
))
486+
self.__signed_pre_key_management_task = asyncio.ensure_future(signed_pre_key_management_coro)
481487
elif async_framework is AsyncFramework.TWISTED:
482-
defer.ensureDeferred(self.__manage_signed_pre_key_rotation(
483-
signed_pre_key_rotation_period,
484-
async_framework
485-
))
488+
self.__signed_pre_key_management_task = defer.ensureDeferred(signed_pre_key_management_coro)
486489
else:
487490
assert_never(async_framework)
488491

@@ -492,6 +495,30 @@ async def create(
492495

493496
return self
494497

498+
async def shutdown(self) -> None:
499+
"""
500+
Gracefully quit internal tasks.
501+
"""
502+
503+
if self.__async_framework is AsyncFramework.ASYNCIO:
504+
asyncio_task = cast(asyncio.Future[None], self.__signed_pre_key_management_task)
505+
try:
506+
asyncio_task.cancel()
507+
await asyncio_task
508+
except asyncio.CancelledError:
509+
pass
510+
511+
elif self.__async_framework is AsyncFramework.TWISTED:
512+
twisted_task = cast(defer.Deferred[None], self.__signed_pre_key_management_task)
513+
try:
514+
twisted_task.cancel()
515+
await twisted_task
516+
except defer.CancelledError:
517+
pass
518+
519+
else:
520+
assert_never(self.__async_framework)
521+
495522
async def purge_backend(self, namespace: str) -> None:
496523
"""
497524
Purge a backend, removing both the online data (bundle, device list entry) and the offline data that

tests/test_session_manager.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,10 @@ async def test_regression0() -> None:
169169
assert plaintext == b"Hello back, Alice!"
170170

171171

172+
await alice_session_manager.shutdown()
173+
await bob_session_manager.shutdown()
174+
175+
172176
async def test_oldmemo_migration() -> None:
173177
"""
174178
Tests the migration of the legacy storage format, which is provided by python-oldmemo.
@@ -217,3 +221,5 @@ async def test_oldmemo_migration() -> None:
217221
plaintext, _, _ = await session_manager.decrypt(message)
218222

219223
assert plaintext == b"This is a test message"
224+
225+
await session_manager.shutdown()

0 commit comments

Comments
 (0)