- commit
- d241b8a53cab1f54563072f953803756c32cbf02
- parent
- 37911ea6ed293cd987fad904b07ab4b027857604
- Author
- Tobias Bengfort <tobias.bengfort@posteo.de>
- Date
- 2026-04-12 16:45
add subscribe_signals()
Diffstat
| A | tests/test_signals.py | 21 | +++++++++++++++++++++ |
| M | xiio/__init__.py | 1 | + |
| A | xiio/signals.py | 41 | +++++++++++++++++++++++++++++++++++++++++ |
3 files changed, 63 insertions, 0 deletions
diff --git a/tests/test_signals.py b/tests/test_signals.py
@@ -0,0 +1,21 @@ -1 1 import os -1 2 import signal -1 3 -1 4 import xiio -1 5 from tests.utils import XiioTestCase -1 6 -1 7 -1 8 class TestSubscribeSignals(XiioTestCase): -1 9 async def test_subscribe_signals(self): -1 10 result = [] -1 11 -1 12 async def send_signals(): -1 13 await xiio.sleep(0.1) -1 14 os.kill(os.getpid(), signal.Signals.SIGUSR1) -1 15 -1 16 async with xiio.TaskGroup() as tg: -1 17 async with xiio.subscribe_signals(signal.Signals.SIGUSR1) as signal_fd: -1 18 tg.add_task(send_signals()) -1 19 with self.assert_duration(0.1): -1 20 result = await xiio.read(signal_fd, 1) -1 21 self.assertEqual(result[0], signal.Signals.SIGUSR1)
diff --git a/xiio/__init__.py b/xiio/__init__.py
@@ -9,3 +9,4 @@ from .multiplex import TaskGroup # noqa 9 9 from .multiplex import gather # noqa 10 10 from .multiplex import timeout # noqa 11 11 from .subprocess import run_process # noqa -1 12 from .signals import subscribe_signals # noqa
diff --git a/xiio/signals.py b/xiio/signals.py
@@ -0,0 +1,41 @@
-1 1 """
-1 2 Signals can only be registered in the main thread and there can only be a
-1 3 single wakeup FD. As a consequence, `subscribe_signals()` should only be used
-1 4 once, early on in the application.
-1 5 """
-1 6
-1 7 import contextlib
-1 8 import signal
-1 9 import socket
-1 10 from collections.abc import AsyncGenerator
-1 11 from collections.abc import Generator
-1 12
-1 13
-1 14 def noop(*args):
-1 15 pass
-1 16
-1 17
-1 18 @contextlib.contextmanager
-1 19 def signal_fd() -> Generator[int, None, None]:
-1 20 r, w = socket.socketpair()
-1 21 r.setblocking(False) # noqa
-1 22 w.setblocking(False) # noqa
-1 23
-1 24 old = signal.set_wakeup_fd(w.fileno())
-1 25 try:
-1 26 yield r.fileno()
-1 27 finally:
-1 28 signal.set_wakeup_fd(old)
-1 29 r.close()
-1 30 w.close()
-1 31
-1 32
-1 33 @contextlib.asynccontextmanager
-1 34 async def subscribe_signals(*sigs: int) -> AsyncGenerator[int, None]:
-1 35 with signal_fd() as fd:
-1 36 old_sigs = {sig: signal.signal(sig, noop) for sig in sigs}
-1 37 try:
-1 38 yield fd
-1 39 finally:
-1 40 for sig, old in old_sigs.items():
-1 41 signal.signal(sig, old)