- commit
- cd6b737ca8f9204163d93457271c0b9334762658
- parent
- 10bef3c9d7a55769299413d4ddb6ab51b3a04c63
- Author
- Tobias Bengfort <tobias.bengfort@posteo.de>
- Date
- 2026-02-16 08:55
add subscribe signal
Diffstat
| M | xibus/client.py | 61 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| M | xibus/connection.py | 15 | ++++++++++++++- |
2 files changed, 75 insertions, 1 deletions
diff --git a/xibus/client.py b/xibus/client.py
@@ -1,6 +1,40 @@ -1 1 import contextlib -1 2 1 3 from .schema import Schema 2 4 3 5 -1 6 class SignalQueue: -1 7 def __init__(self, queue, sender, path, iface, signal): -1 8 self.queue = queue -1 9 self.sender = sender -1 10 self.path = path -1 11 self.iface = iface -1 12 self.signal = signal -1 13 -1 14 @property -1 15 def rule(self): -1 16 return ','.join( -1 17 f"{key}='{value}'" -1 18 for key, value in { -1 19 'type': 'signal', -1 20 'sender': self.sender, -1 21 'path': self.path, -1 22 'interface': self.iface, -1 23 'member': self.signal, -1 24 }.items() -1 25 ) -1 26 -1 27 async def __aiter__(self): -1 28 async for msg in self.queue: -1 29 if ( -1 30 msg.sender == self.sender -1 31 and msg.path == self.path -1 32 and msg.iface == self.iface -1 33 and msg.member == self.signal -1 34 ): -1 35 yield msg.body -1 36 -1 37 4 38 class Proxy: 5 39 def __init__(self, client, name, path=None, iface=None): 6 40 self.client = client @@ -9,11 +43,22 @@ class Proxy: 9 43 async def call(self, method, params=(), sig=None): 10 44 return await self.client.call(*self.defaults, method, params, sig) 11 45 -1 46 @contextlib.asynccontextmanager -1 47 async def subscribe_signal(self, signal): -1 48 async with self.client.subscribe_signal(*self.defaults, signal) as queue: -1 49 yield queue -1 50 12 51 13 52 class Client: 14 53 def __init__(self, con): 15 54 self.con = con 16 55 self.introspect_cache = {} -1 56 self.bus = Proxy( -1 57 self, -1 58 'org.freedesktop.DBus', -1 59 '/org/freedesktop/DBus', -1 60 'org.freedesktop.DBus', -1 61 ) 17 62 18 63 async def introspect(self, name, path): 19 64 key = f'{name}{path}' @@ -34,3 +79,19 @@ class Client: 34 79 return result[0] 35 80 elif len(m.returns) > 1: 36 81 return result -1 82 -1 83 @contextlib.asynccontextmanager -1 84 async def subscribe_signal(self, name, path, iface, signal): -1 85 # NOTE: if we register the same match rule twice and then remove one of -1 86 # them, the other still exists on the bus. So we do not need any -1 87 # special handling on our end. -1 88 -1 89 if not name.startswith(':'): -1 90 name = await self.bus.call('GetNameOwner', [name], 's') -1 91 with self.con.signal_queue() as queue: -1 92 sq = SignalQueue(queue, name, path, iface, signal) -1 93 await self.bus.call('AddMatch', [sq.rule], 's') -1 94 try: -1 95 yield sq -1 96 finally: -1 97 await self.bus.call('RemoveMatch', [sq.rule], 's')
diff --git a/xibus/connection.py b/xibus/connection.py
@@ -2,6 +2,7 @@ import asyncio 2 2 import os 3 3 import re 4 4 import socket -1 5 from contextlib import contextmanager 5 6 6 7 from .message import Msg 7 8 from .message import MsgFlag @@ -34,6 +35,7 @@ class Connection: 34 35 self.serial = 0 35 36 self.send_queue = [] 36 37 self.replies = {} -1 38 self.signal_queues = set() 37 39 38 40 if not self.loop: 39 41 self.loop = asyncio.get_running_loop() @@ -57,7 +59,8 @@ class Connection: 57 59 elif msg.type == MsgType.METHOD_CALL: 58 60 raise NotImplementedError 59 61 elif msg.type == MsgType.SIGNAL:60 -1 raise NotImplementedError-1 62 for queue in self.signal_queues: -1 63 queue.put_nowait(msg) 61 64 else: 62 65 raise ValueError(msg) 63 66 @@ -120,6 +123,16 @@ class Connection: 120 123 self.sock.close() 121 124 self.sock = None 122 125 -1 126 @contextmanager -1 127 def signal_queue(self): -1 128 queue = asyncio.Queue() -1 129 self.signal_queues.add(queue) -1 130 try: -1 131 yield iter_queue(queue) -1 132 finally: -1 133 self.signal_queues.remove(queue) -1 134 queue.shutdown() -1 135 123 136 async def call(self, dest, path, iface, method, body, sig, flags=MsgFlag.NONE): 124 137 if not RE_PATH.match(path): 125 138 raise InvalidPathError(path)