Mercurial > libervia-backend
comparison libervia/backend/bridge/bridge_constructor/constructors/dbus/dbus_core_template.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/bridge_constructor/constructors/dbus/dbus_core_template.py@524856bd7b19 |
children |
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 ##METHODS_DECLARATIONS_PART## | |
92 ##SIGNALS_DECLARATIONS_PART## | |
93 ) | |
94 plugin_iface = DBusInterface( | |
95 const_INT_PREFIX + const_PLUGIN_SUFFIX | |
96 ) | |
97 | |
98 dbusInterfaces = [core_iface, plugin_iface] | |
99 | |
100 def __init__(self, path): | |
101 super().__init__(path) | |
102 log.debug("Init DBusObject...") | |
103 self.cb = {} | |
104 | |
105 def register_method(self, name, cb): | |
106 self.cb[name] = cb | |
107 | |
108 def _callback(self, name, *args, **kwargs): | |
109 """Call the callback if it exists, raise an exception else""" | |
110 try: | |
111 cb = self.cb[name] | |
112 except KeyError: | |
113 raise MethodNotRegistered | |
114 else: | |
115 d = defer.maybeDeferred(cb, *args, **kwargs) | |
116 d.addErrback(GenericException.create_and_raise) | |
117 return d | |
118 | |
119 ##METHODS_PART## | |
120 | |
121 class bridge: | |
122 | |
123 def __init__(self): | |
124 log.info("Init DBus...") | |
125 self._obj = DBusObject(const_OBJ_PATH) | |
126 | |
127 async def post_init(self): | |
128 try: | |
129 conn = await client.connect(reactor) | |
130 except error.DBusException as e: | |
131 if e.errName == "org.freedesktop.DBus.Error.NotSupported": | |
132 log.error( | |
133 _( | |
134 "D-Bus is not launched, please see README to see instructions on " | |
135 "how to launch it" | |
136 ) | |
137 ) | |
138 raise BridgeInitError(str(e)) | |
139 | |
140 conn.exportObject(self._obj) | |
141 await conn.requestBusName(const_INT_PREFIX) | |
142 | |
143 ##SIGNALS_PART## | |
144 def register_method(self, name, callback): | |
145 log.debug(f"registering DBus bridge method [{name}]") | |
146 self._obj.register_method(name, callback) | |
147 | |
148 def emit_signal(self, name, *args): | |
149 self._obj.emitSignal(name, *args) | |
150 | |
151 def add_method( | |
152 self, name, int_suffix, in_sign, out_sign, method, async_=False, doc={} | |
153 ): | |
154 """Dynamically add a method to D-Bus bridge""" | |
155 # FIXME: doc parameter is kept only temporary, the time to remove it from calls | |
156 log.debug(f"Adding method {name!r} to D-Bus bridge") | |
157 self._obj.plugin_iface.addMethod( | |
158 Method(name, arguments=in_sign, returns=out_sign) | |
159 ) | |
160 # we have to create a method here instead of using partialmethod, because txdbus | |
161 # uses __func__ which doesn't work with partialmethod | |
162 def caller(self_, *args, **kwargs): | |
163 return self_._callback(name, *args, **kwargs) | |
164 setattr(self._obj, f"dbus_{name}", MethodType(caller, self._obj)) | |
165 self.register_method(name, method) | |
166 | |
167 def add_signal(self, name, int_suffix, signature, doc={}): | |
168 """Dynamically add a signal to D-Bus bridge""" | |
169 log.debug(f"Adding signal {name!r} to D-Bus bridge") | |
170 self._obj.plugin_iface.addSignal(Signal(name, signature)) | |
171 setattr(bridge, name, partialmethod(bridge.emit_signal, name)) |