Mercurial > libervia-backend
comparison libervia/backend/bridge/dbus_bridge.py @ 4071:4b842c1fb686
refactoring: renamed `sat` package to `libervia.backend`
author | Goffi <goffi@goffi.org> |
---|---|
date | Fri, 02 Jun 2023 11:49:51 +0200 |
parents | sat/bridge/dbus_bridge.py@2594e1951cf7 |
children | 02f0adc745c6 |
comparison
equal
deleted
inserted
replaced
4070:d10748475025 | 4071:4b842c1fb686 |
---|---|
1 #!/usr/bin/env python3 | |
2 | |
3 # Libervia communication bridge | |
4 # Copyright (C) 2009-2021 Jérôme Poisson (goffi@goffi.org) | |
5 | |
6 # This program is free software: you can redistribute it and/or modify | |
7 # it under the terms of the GNU Affero General Public License as published by | |
8 # the Free Software Foundation, either version 3 of the License, or | |
9 # (at your option) any later version. | |
10 | |
11 # This program is distributed in the hope that it will be useful, | |
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 # GNU Affero General Public License for more details. | |
15 | |
16 # You should have received a copy of the GNU Affero General Public License | |
17 # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
18 | |
19 from types import MethodType | |
20 from functools import partialmethod | |
21 from twisted.internet import defer, reactor | |
22 from libervia.backend.core.i18n import _ | |
23 from libervia.backend.core.log import getLogger | |
24 from libervia.backend.core.exceptions import BridgeInitError | |
25 from libervia.backend.tools import config | |
26 from txdbus import client, objects, error | |
27 from txdbus.interface import DBusInterface, Method, Signal | |
28 | |
29 | |
30 log = getLogger(__name__) | |
31 | |
32 # Interface prefix | |
33 const_INT_PREFIX = config.config_get( | |
34 config.parse_main_conf(), | |
35 "", | |
36 "bridge_dbus_int_prefix", | |
37 "org.libervia.Libervia") | |
38 const_ERROR_PREFIX = const_INT_PREFIX + ".error" | |
39 const_OBJ_PATH = "/org/libervia/Libervia/bridge" | |
40 const_CORE_SUFFIX = ".core" | |
41 const_PLUGIN_SUFFIX = ".plugin" | |
42 | |
43 | |
44 class ParseError(Exception): | |
45 pass | |
46 | |
47 | |
48 class DBusException(Exception): | |
49 pass | |
50 | |
51 | |
52 class MethodNotRegistered(DBusException): | |
53 dbusErrorName = const_ERROR_PREFIX + ".MethodNotRegistered" | |
54 | |
55 | |
56 class GenericException(DBusException): | |
57 def __init__(self, twisted_error): | |
58 """ | |
59 | |
60 @param twisted_error (Failure): instance of twisted Failure | |
61 error message is used to store a repr of message and condition in a tuple, | |
62 so it can be evaluated by the frontend bridge. | |
63 """ | |
64 try: | |
65 # twisted_error.value is a class | |
66 class_ = twisted_error.value().__class__ | |
67 except TypeError: | |
68 # twisted_error.value is an instance | |
69 class_ = twisted_error.value.__class__ | |
70 data = twisted_error.getErrorMessage() | |
71 try: | |
72 data = (data, twisted_error.value.condition) | |
73 except AttributeError: | |
74 data = (data,) | |
75 else: | |
76 data = (str(twisted_error),) | |
77 self.dbusErrorName = ".".join( | |
78 (const_ERROR_PREFIX, class_.__module__, class_.__name__) | |
79 ) | |
80 super(GenericException, self).__init__(repr(data)) | |
81 | |
82 @classmethod | |
83 def create_and_raise(cls, exc): | |
84 raise cls(exc) | |
85 | |
86 | |
87 class DBusObject(objects.DBusObject): | |
88 | |
89 core_iface = DBusInterface( | |
90 const_INT_PREFIX + const_CORE_SUFFIX, | |
91 Method('action_launch', arguments='sss', returns='s'), | |
92 Method('actions_get', arguments='s', returns='a(ssi)'), | |
93 Method('config_get', arguments='ss', returns='s'), | |
94 Method('connect', arguments='ssa{ss}', returns='b'), | |
95 Method('contact_add', arguments='ss', returns=''), | |
96 Method('contact_del', arguments='ss', returns=''), | |
97 Method('contact_get', arguments='ss', returns='(a{ss}as)'), | |
98 Method('contact_update', arguments='ssass', returns=''), | |
99 Method('contacts_get', arguments='s', returns='a(sa{ss}as)'), | |
100 Method('contacts_get_from_group', arguments='ss', returns='as'), | |
101 Method('devices_infos_get', arguments='ss', returns='s'), | |
102 Method('disco_find_by_features', arguments='asa(ss)bbbbbs', returns='(a{sa(sss)}a{sa(sss)}a{sa(sss)})'), | |
103 Method('disco_infos', arguments='ssbs', returns='(asa(sss)a{sa(a{ss}as)})'), | |
104 Method('disco_items', arguments='ssbs', returns='a(sss)'), | |
105 Method('disconnect', arguments='s', returns=''), | |
106 Method('encryption_namespace_get', arguments='s', returns='s'), | |
107 Method('encryption_plugins_get', arguments='', returns='s'), | |
108 Method('encryption_trust_ui_get', arguments='sss', returns='s'), | |
109 Method('entities_data_get', arguments='asass', returns='a{sa{ss}}'), | |
110 Method('entity_data_get', arguments='sass', returns='a{ss}'), | |
111 Method('features_get', arguments='s', returns='a{sa{ss}}'), | |
112 Method('history_get', arguments='ssiba{ss}s', returns='a(sdssa{ss}a{ss}ss)'), | |
113 Method('image_check', arguments='s', returns='s'), | |
114 Method('image_convert', arguments='ssss', returns='s'), | |
115 Method('image_generate_preview', arguments='ss', returns='s'), | |
116 Method('image_resize', arguments='sii', returns='s'), | |
117 Method('is_connected', arguments='s', returns='b'), | |
118 Method('main_resource_get', arguments='ss', returns='s'), | |
119 Method('menu_help_get', arguments='ss', returns='s'), | |
120 Method('menu_launch', arguments='sasa{ss}is', returns='a{ss}'), | |
121 Method('menus_get', arguments='si', returns='a(ssasasa{ss})'), | |
122 Method('message_encryption_get', arguments='ss', returns='s'), | |
123 Method('message_encryption_start', arguments='ssbs', returns=''), | |
124 Method('message_encryption_stop', arguments='ss', returns=''), | |
125 Method('message_send', arguments='sa{ss}a{ss}sss', returns=''), | |
126 Method('namespaces_get', arguments='', returns='a{ss}'), | |
127 Method('param_get_a', arguments='ssss', returns='s'), | |
128 Method('param_get_a_async', arguments='sssis', returns='s'), | |
129 Method('param_set', arguments='sssis', returns=''), | |
130 Method('param_ui_get', arguments='isss', returns='s'), | |
131 Method('params_categories_get', arguments='', returns='as'), | |
132 Method('params_register_app', arguments='sis', returns=''), | |
133 Method('params_template_load', arguments='s', returns='b'), | |
134 Method('params_template_save', arguments='s', returns='b'), | |
135 Method('params_values_from_category_get_async', arguments='sisss', returns='a{ss}'), | |
136 Method('presence_set', arguments='ssa{ss}s', returns=''), | |
137 Method('presence_statuses_get', arguments='s', returns='a{sa{s(sia{ss})}}'), | |
138 Method('private_data_delete', arguments='sss', returns=''), | |
139 Method('private_data_get', arguments='sss', returns='s'), | |
140 Method('private_data_set', arguments='ssss', returns=''), | |
141 Method('profile_create', arguments='sss', returns=''), | |
142 Method('profile_delete_async', arguments='s', returns=''), | |
143 Method('profile_is_session_started', arguments='s', returns='b'), | |
144 Method('profile_name_get', arguments='s', returns='s'), | |
145 Method('profile_set_default', arguments='s', returns=''), | |
146 Method('profile_start_session', arguments='ss', returns='b'), | |
147 Method('profiles_list_get', arguments='bb', returns='as'), | |
148 Method('progress_get', arguments='ss', returns='a{ss}'), | |
149 Method('progress_get_all', arguments='s', returns='a{sa{sa{ss}}}'), | |
150 Method('progress_get_all_metadata', arguments='s', returns='a{sa{sa{ss}}}'), | |
151 Method('ready_get', arguments='', returns=''), | |
152 Method('roster_resync', arguments='s', returns=''), | |
153 Method('session_infos_get', arguments='s', returns='a{ss}'), | |
154 Method('sub_waiting_get', arguments='s', returns='a{ss}'), | |
155 Method('subscription', arguments='sss', returns=''), | |
156 Method('version_get', arguments='', returns='s'), | |
157 Signal('_debug', 'sa{ss}s'), | |
158 Signal('action_new', 'ssis'), | |
159 Signal('connected', 'ss'), | |
160 Signal('contact_deleted', 'ss'), | |
161 Signal('contact_new', 'sa{ss}ass'), | |
162 Signal('disconnected', 's'), | |
163 Signal('entity_data_updated', 'ssss'), | |
164 Signal('message_encryption_started', 'sss'), | |
165 Signal('message_encryption_stopped', 'sa{ss}s'), | |
166 Signal('message_new', 'sdssa{ss}a{ss}sss'), | |
167 Signal('param_update', 'ssss'), | |
168 Signal('presence_update', 'ssia{ss}s'), | |
169 Signal('progress_error', 'sss'), | |
170 Signal('progress_finished', 'sa{ss}s'), | |
171 Signal('progress_started', 'sa{ss}s'), | |
172 Signal('subscribe', 'sss'), | |
173 ) | |
174 plugin_iface = DBusInterface( | |
175 const_INT_PREFIX + const_PLUGIN_SUFFIX | |
176 ) | |
177 | |
178 dbusInterfaces = [core_iface, plugin_iface] | |
179 | |
180 def __init__(self, path): | |
181 super().__init__(path) | |
182 log.debug("Init DBusObject...") | |
183 self.cb = {} | |
184 | |
185 def register_method(self, name, cb): | |
186 self.cb[name] = cb | |
187 | |
188 def _callback(self, name, *args, **kwargs): | |
189 """Call the callback if it exists, raise an exception else""" | |
190 try: | |
191 cb = self.cb[name] | |
192 except KeyError: | |
193 raise MethodNotRegistered | |
194 else: | |
195 d = defer.maybeDeferred(cb, *args, **kwargs) | |
196 d.addErrback(GenericException.create_and_raise) | |
197 return d | |
198 | |
199 def dbus_action_launch(self, callback_id, data, profile_key="@DEFAULT@"): | |
200 return self._callback("action_launch", callback_id, data, profile_key) | |
201 | |
202 def dbus_actions_get(self, profile_key="@DEFAULT@"): | |
203 return self._callback("actions_get", profile_key) | |
204 | |
205 def dbus_config_get(self, section, name): | |
206 return self._callback("config_get", section, name) | |
207 | |
208 def dbus_connect(self, profile_key="@DEFAULT@", password='', options={}): | |
209 return self._callback("connect", profile_key, password, options) | |
210 | |
211 def dbus_contact_add(self, entity_jid, profile_key="@DEFAULT@"): | |
212 return self._callback("contact_add", entity_jid, profile_key) | |
213 | |
214 def dbus_contact_del(self, entity_jid, profile_key="@DEFAULT@"): | |
215 return self._callback("contact_del", entity_jid, profile_key) | |
216 | |
217 def dbus_contact_get(self, arg_0, profile_key="@DEFAULT@"): | |
218 return self._callback("contact_get", arg_0, profile_key) | |
219 | |
220 def dbus_contact_update(self, entity_jid, name, groups, profile_key="@DEFAULT@"): | |
221 return self._callback("contact_update", entity_jid, name, groups, profile_key) | |
222 | |
223 def dbus_contacts_get(self, profile_key="@DEFAULT@"): | |
224 return self._callback("contacts_get", profile_key) | |
225 | |
226 def dbus_contacts_get_from_group(self, group, profile_key="@DEFAULT@"): | |
227 return self._callback("contacts_get_from_group", group, profile_key) | |
228 | |
229 def dbus_devices_infos_get(self, bare_jid, profile_key): | |
230 return self._callback("devices_infos_get", bare_jid, profile_key) | |
231 | |
232 def dbus_disco_find_by_features(self, namespaces, identities, bare_jid=False, service=True, roster=True, own_jid=True, local_device=False, profile_key="@DEFAULT@"): | |
233 return self._callback("disco_find_by_features", namespaces, identities, bare_jid, service, roster, own_jid, local_device, profile_key) | |
234 | |
235 def dbus_disco_infos(self, entity_jid, node=u'', use_cache=True, profile_key="@DEFAULT@"): | |
236 return self._callback("disco_infos", entity_jid, node, use_cache, profile_key) | |
237 | |
238 def dbus_disco_items(self, entity_jid, node=u'', use_cache=True, profile_key="@DEFAULT@"): | |
239 return self._callback("disco_items", entity_jid, node, use_cache, profile_key) | |
240 | |
241 def dbus_disconnect(self, profile_key="@DEFAULT@"): | |
242 return self._callback("disconnect", profile_key) | |
243 | |
244 def dbus_encryption_namespace_get(self, arg_0): | |
245 return self._callback("encryption_namespace_get", arg_0) | |
246 | |
247 def dbus_encryption_plugins_get(self, ): | |
248 return self._callback("encryption_plugins_get", ) | |
249 | |
250 def dbus_encryption_trust_ui_get(self, to_jid, namespace, profile_key): | |
251 return self._callback("encryption_trust_ui_get", to_jid, namespace, profile_key) | |
252 | |
253 def dbus_entities_data_get(self, jids, keys, profile): | |
254 return self._callback("entities_data_get", jids, keys, profile) | |
255 | |
256 def dbus_entity_data_get(self, jid, keys, profile): | |
257 return self._callback("entity_data_get", jid, keys, profile) | |
258 | |
259 def dbus_features_get(self, profile_key): | |
260 return self._callback("features_get", profile_key) | |
261 | |
262 def dbus_history_get(self, from_jid, to_jid, limit, between=True, filters='', profile="@NONE@"): | |
263 return self._callback("history_get", from_jid, to_jid, limit, between, filters, profile) | |
264 | |
265 def dbus_image_check(self, arg_0): | |
266 return self._callback("image_check", arg_0) | |
267 | |
268 def dbus_image_convert(self, source, dest, arg_2, extra): | |
269 return self._callback("image_convert", source, dest, arg_2, extra) | |
270 | |
271 def dbus_image_generate_preview(self, image_path, profile_key): | |
272 return self._callback("image_generate_preview", image_path, profile_key) | |
273 | |
274 def dbus_image_resize(self, image_path, width, height): | |
275 return self._callback("image_resize", image_path, width, height) | |
276 | |
277 def dbus_is_connected(self, profile_key="@DEFAULT@"): | |
278 return self._callback("is_connected", profile_key) | |
279 | |
280 def dbus_main_resource_get(self, contact_jid, profile_key="@DEFAULT@"): | |
281 return self._callback("main_resource_get", contact_jid, profile_key) | |
282 | |
283 def dbus_menu_help_get(self, menu_id, language): | |
284 return self._callback("menu_help_get", menu_id, language) | |
285 | |
286 def dbus_menu_launch(self, menu_type, path, data, security_limit, profile_key): | |
287 return self._callback("menu_launch", menu_type, path, data, security_limit, profile_key) | |
288 | |
289 def dbus_menus_get(self, language, security_limit): | |
290 return self._callback("menus_get", language, security_limit) | |
291 | |
292 def dbus_message_encryption_get(self, to_jid, profile_key): | |
293 return self._callback("message_encryption_get", to_jid, profile_key) | |
294 | |
295 def dbus_message_encryption_start(self, to_jid, namespace='', replace=False, profile_key="@NONE@"): | |
296 return self._callback("message_encryption_start", to_jid, namespace, replace, profile_key) | |
297 | |
298 def dbus_message_encryption_stop(self, to_jid, profile_key): | |
299 return self._callback("message_encryption_stop", to_jid, profile_key) | |
300 | |
301 def dbus_message_send(self, to_jid, message, subject={}, mess_type="auto", extra={}, profile_key="@NONE@"): | |
302 return self._callback("message_send", to_jid, message, subject, mess_type, extra, profile_key) | |
303 | |
304 def dbus_namespaces_get(self, ): | |
305 return self._callback("namespaces_get", ) | |
306 | |
307 def dbus_param_get_a(self, name, category, attribute="value", profile_key="@DEFAULT@"): | |
308 return self._callback("param_get_a", name, category, attribute, profile_key) | |
309 | |
310 def dbus_param_get_a_async(self, name, category, attribute="value", security_limit=-1, profile_key="@DEFAULT@"): | |
311 return self._callback("param_get_a_async", name, category, attribute, security_limit, profile_key) | |
312 | |
313 def dbus_param_set(self, name, value, category, security_limit=-1, profile_key="@DEFAULT@"): | |
314 return self._callback("param_set", name, value, category, security_limit, profile_key) | |
315 | |
316 def dbus_param_ui_get(self, security_limit=-1, app='', extra='', profile_key="@DEFAULT@"): | |
317 return self._callback("param_ui_get", security_limit, app, extra, profile_key) | |
318 | |
319 def dbus_params_categories_get(self, ): | |
320 return self._callback("params_categories_get", ) | |
321 | |
322 def dbus_params_register_app(self, xml, security_limit=-1, app=''): | |
323 return self._callback("params_register_app", xml, security_limit, app) | |
324 | |
325 def dbus_params_template_load(self, filename): | |
326 return self._callback("params_template_load", filename) | |
327 | |
328 def dbus_params_template_save(self, filename): | |
329 return self._callback("params_template_save", filename) | |
330 | |
331 def dbus_params_values_from_category_get_async(self, category, security_limit=-1, app="", extra="", profile_key="@DEFAULT@"): | |
332 return self._callback("params_values_from_category_get_async", category, security_limit, app, extra, profile_key) | |
333 | |
334 def dbus_presence_set(self, to_jid='', show='', statuses={}, profile_key="@DEFAULT@"): | |
335 return self._callback("presence_set", to_jid, show, statuses, profile_key) | |
336 | |
337 def dbus_presence_statuses_get(self, profile_key="@DEFAULT@"): | |
338 return self._callback("presence_statuses_get", profile_key) | |
339 | |
340 def dbus_private_data_delete(self, namespace, key, arg_2): | |
341 return self._callback("private_data_delete", namespace, key, arg_2) | |
342 | |
343 def dbus_private_data_get(self, namespace, key, profile_key): | |
344 return self._callback("private_data_get", namespace, key, profile_key) | |
345 | |
346 def dbus_private_data_set(self, namespace, key, data, profile_key): | |
347 return self._callback("private_data_set", namespace, key, data, profile_key) | |
348 | |
349 def dbus_profile_create(self, profile, password='', component=''): | |
350 return self._callback("profile_create", profile, password, component) | |
351 | |
352 def dbus_profile_delete_async(self, profile): | |
353 return self._callback("profile_delete_async", profile) | |
354 | |
355 def dbus_profile_is_session_started(self, profile_key="@DEFAULT@"): | |
356 return self._callback("profile_is_session_started", profile_key) | |
357 | |
358 def dbus_profile_name_get(self, profile_key="@DEFAULT@"): | |
359 return self._callback("profile_name_get", profile_key) | |
360 | |
361 def dbus_profile_set_default(self, profile): | |
362 return self._callback("profile_set_default", profile) | |
363 | |
364 def dbus_profile_start_session(self, password='', profile_key="@DEFAULT@"): | |
365 return self._callback("profile_start_session", password, profile_key) | |
366 | |
367 def dbus_profiles_list_get(self, clients=True, components=False): | |
368 return self._callback("profiles_list_get", clients, components) | |
369 | |
370 def dbus_progress_get(self, id, profile): | |
371 return self._callback("progress_get", id, profile) | |
372 | |
373 def dbus_progress_get_all(self, profile): | |
374 return self._callback("progress_get_all", profile) | |
375 | |
376 def dbus_progress_get_all_metadata(self, profile): | |
377 return self._callback("progress_get_all_metadata", profile) | |
378 | |
379 def dbus_ready_get(self, ): | |
380 return self._callback("ready_get", ) | |
381 | |
382 def dbus_roster_resync(self, profile_key="@DEFAULT@"): | |
383 return self._callback("roster_resync", profile_key) | |
384 | |
385 def dbus_session_infos_get(self, profile_key): | |
386 return self._callback("session_infos_get", profile_key) | |
387 | |
388 def dbus_sub_waiting_get(self, profile_key="@DEFAULT@"): | |
389 return self._callback("sub_waiting_get", profile_key) | |
390 | |
391 def dbus_subscription(self, sub_type, entity, profile_key="@DEFAULT@"): | |
392 return self._callback("subscription", sub_type, entity, profile_key) | |
393 | |
394 def dbus_version_get(self, ): | |
395 return self._callback("version_get", ) | |
396 | |
397 | |
398 class bridge: | |
399 | |
400 def __init__(self): | |
401 log.info("Init DBus...") | |
402 self._obj = DBusObject(const_OBJ_PATH) | |
403 | |
404 async def post_init(self): | |
405 try: | |
406 conn = await client.connect(reactor) | |
407 except error.DBusException as e: | |
408 if e.errName == "org.freedesktop.DBus.Error.NotSupported": | |
409 log.error( | |
410 _( | |
411 "D-Bus is not launched, please see README to see instructions on " | |
412 "how to launch it" | |
413 ) | |
414 ) | |
415 raise BridgeInitError(str(e)) | |
416 | |
417 conn.exportObject(self._obj) | |
418 await conn.requestBusName(const_INT_PREFIX) | |
419 | |
420 def _debug(self, action, params, profile): | |
421 self._obj.emitSignal("_debug", action, params, profile) | |
422 | |
423 def action_new(self, action_data, id, security_limit, profile): | |
424 self._obj.emitSignal("action_new", action_data, id, security_limit, profile) | |
425 | |
426 def connected(self, jid_s, profile): | |
427 self._obj.emitSignal("connected", jid_s, profile) | |
428 | |
429 def contact_deleted(self, entity_jid, profile): | |
430 self._obj.emitSignal("contact_deleted", entity_jid, profile) | |
431 | |
432 def contact_new(self, contact_jid, attributes, groups, profile): | |
433 self._obj.emitSignal("contact_new", contact_jid, attributes, groups, profile) | |
434 | |
435 def disconnected(self, profile): | |
436 self._obj.emitSignal("disconnected", profile) | |
437 | |
438 def entity_data_updated(self, jid, name, value, profile): | |
439 self._obj.emitSignal("entity_data_updated", jid, name, value, profile) | |
440 | |
441 def message_encryption_started(self, to_jid, encryption_data, profile_key): | |
442 self._obj.emitSignal("message_encryption_started", to_jid, encryption_data, profile_key) | |
443 | |
444 def message_encryption_stopped(self, to_jid, encryption_data, profile_key): | |
445 self._obj.emitSignal("message_encryption_stopped", to_jid, encryption_data, profile_key) | |
446 | |
447 def message_new(self, uid, timestamp, from_jid, to_jid, message, subject, mess_type, extra, profile): | |
448 self._obj.emitSignal("message_new", uid, timestamp, from_jid, to_jid, message, subject, mess_type, extra, profile) | |
449 | |
450 def param_update(self, name, value, category, profile): | |
451 self._obj.emitSignal("param_update", name, value, category, profile) | |
452 | |
453 def presence_update(self, entity_jid, show, priority, statuses, profile): | |
454 self._obj.emitSignal("presence_update", entity_jid, show, priority, statuses, profile) | |
455 | |
456 def progress_error(self, id, error, profile): | |
457 self._obj.emitSignal("progress_error", id, error, profile) | |
458 | |
459 def progress_finished(self, id, metadata, profile): | |
460 self._obj.emitSignal("progress_finished", id, metadata, profile) | |
461 | |
462 def progress_started(self, id, metadata, profile): | |
463 self._obj.emitSignal("progress_started", id, metadata, profile) | |
464 | |
465 def subscribe(self, sub_type, entity_jid, profile): | |
466 self._obj.emitSignal("subscribe", sub_type, entity_jid, profile) | |
467 | |
468 def register_method(self, name, callback): | |
469 log.debug(f"registering DBus bridge method [{name}]") | |
470 self._obj.register_method(name, callback) | |
471 | |
472 def emit_signal(self, name, *args): | |
473 self._obj.emitSignal(name, *args) | |
474 | |
475 def add_method( | |
476 self, name, int_suffix, in_sign, out_sign, method, async_=False, doc={} | |
477 ): | |
478 """Dynamically add a method to D-Bus bridge""" | |
479 # FIXME: doc parameter is kept only temporary, the time to remove it from calls | |
480 log.debug(f"Adding method {name!r} to D-Bus bridge") | |
481 self._obj.plugin_iface.addMethod( | |
482 Method(name, arguments=in_sign, returns=out_sign) | |
483 ) | |
484 # we have to create a method here instead of using partialmethod, because txdbus | |
485 # uses __func__ which doesn't work with partialmethod | |
486 def caller(self_, *args, **kwargs): | |
487 return self_._callback(name, *args, **kwargs) | |
488 setattr(self._obj, f"dbus_{name}", MethodType(caller, self._obj)) | |
489 self.register_method(name, method) | |
490 | |
491 def add_signal(self, name, int_suffix, signature, doc={}): | |
492 """Dynamically add a signal to D-Bus bridge""" | |
493 log.debug(f"Adding signal {name!r} to D-Bus bridge") | |
494 self._obj.plugin_iface.addSignal(Signal(name, signature)) | |
495 setattr(bridge, name, partialmethod(bridge.emit_signal, name)) |