Mercurial > libervia-backend
comparison sat_frontends/jp/loops.py @ 3043:3df611adb598
jp: handle dbus bridge with asyncio:
D-Bus bridge is now working again, using the new AsyncIO version. To make it work, the
GLib loop is run in a separated thread.
Loops have been moved to the `loops` module.
author | Goffi <goffi@goffi.org> |
---|---|
date | Tue, 01 Oct 2019 22:49:10 +0200 |
parents | |
children | 9d0df638c8b4 |
comparison
equal
deleted
inserted
replaced
3042:964abd07dc03 | 3043:3df611adb598 |
---|---|
1 #!/usr/bin/env python3 | |
2 | |
3 # jp: a SAT command line tool | |
4 # Copyright (C) 2009-2019 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 import sys | |
20 import asyncio | |
21 import logging as log | |
22 from sat.core.i18n import _ | |
23 from sat_frontends.jp.constants import Const as C | |
24 | |
25 log.basicConfig(level=log.WARNING, | |
26 format='[%(name)s] %(message)s') | |
27 | |
28 USER_INTER_MSG = _("User interruption: good bye") | |
29 | |
30 | |
31 class QuitException(BaseException): | |
32 """Quitting is requested | |
33 | |
34 This is used to stop execution when host.quit() is called | |
35 """ | |
36 | |
37 | |
38 def getJPLoop(bridge_name): | |
39 if 'dbus' in bridge_name: | |
40 import signal | |
41 import threading | |
42 from gi.repository import GLib | |
43 | |
44 class JPLoop: | |
45 | |
46 def run(self, jp, args, namespace): | |
47 signal.signal(signal.SIGINT, self._on_sigint) | |
48 self._glib_loop = GLib.MainLoop() | |
49 threading.Thread(target=self._glib_loop.run).start() | |
50 loop = asyncio.get_event_loop() | |
51 loop.run_until_complete(jp.main(args=args, namespace=namespace)) | |
52 loop.run_forever() | |
53 | |
54 def quit(self, exit_code): | |
55 loop = asyncio.get_event_loop() | |
56 loop.stop() | |
57 self._glib_loop.quit() | |
58 sys.exit(exit_code) | |
59 | |
60 def call_later(self, delay, callback, *args): | |
61 """call a callback repeatedly | |
62 | |
63 @param delay(int): delay between calls in s | |
64 @param callback(callable): method to call | |
65 if the callback return True, the call will continue | |
66 else the calls will stop | |
67 @param *args: args of the callbac | |
68 """ | |
69 loop = asyncio.get_event_loop() | |
70 loop.call_later(delay, callback, *args) | |
71 | |
72 def _on_sigint(self, sig_number, stack_frame): | |
73 """Called on keyboard interruption | |
74 | |
75 Print user interruption message, set exit code and stop reactor | |
76 """ | |
77 print("\r" + USER_INTER_MSG) | |
78 self.quit(C.EXIT_USER_CANCELLED) | |
79 else: | |
80 import signal | |
81 from twisted.internet import asyncioreactor | |
82 asyncioreactor.install() | |
83 from twisted.internet import reactor, defer | |
84 | |
85 class JPLoop: | |
86 | |
87 def __init__(self): | |
88 # exit code must be set when using quit, so if it's not set | |
89 # something got wrong and we must report it | |
90 self._exit_code = C.EXIT_INTERNAL_ERROR | |
91 | |
92 def run(self, jp, *args): | |
93 self.jp = jp | |
94 signal.signal(signal.SIGINT, self._on_sigint) | |
95 defer.ensureDeferred(self._start(jp, *args)) | |
96 try: | |
97 reactor.run(installSignalHandlers=False) | |
98 except SystemExit as e: | |
99 self._exit_code = e.code | |
100 sys.exit(self._exit_code) | |
101 | |
102 async def _start(self, jp, *args): | |
103 fut = asyncio.ensure_future(jp.main(*args)) | |
104 try: | |
105 await defer.Deferred.fromFuture(fut) | |
106 except BaseException: | |
107 import traceback | |
108 traceback.print_exc() | |
109 jp.quit(1) | |
110 | |
111 def quit(self, exit_code): | |
112 self._exit_code = exit_code | |
113 reactor.stop() | |
114 | |
115 def _timeout_cb(self, args, callback, delay): | |
116 try: | |
117 ret = callback(*args) | |
118 # FIXME: temporary hack to avoid traceback when using XMLUI | |
119 # to be removed once create_task is not used anymore in | |
120 # xmlui_manager (i.e. once sat_frontends.tools.xmlui fully supports | |
121 # async syntax) | |
122 except QuitException: | |
123 return | |
124 if ret: | |
125 reactor.callLater(delay, self._timeout_cb, args, callback, delay) | |
126 | |
127 def call_later(self, delay, callback, *args): | |
128 reactor.callLater(delay, self._timeout_cb, args, callback, delay) | |
129 | |
130 def _on_sigint(self, sig_number, stack_frame): | |
131 """Called on keyboard interruption | |
132 | |
133 Print user interruption message, set exit code and stop reactor | |
134 """ | |
135 print("\r" + USER_INTER_MSG) | |
136 self._exit_code = C.EXIT_USER_CANCELLED | |
137 reactor.callFromThread(reactor.stop) | |
138 | |
139 | |
140 if bridge_name == "embedded": | |
141 raise NotImplementedError | |
142 # from sat.core import sat_main | |
143 # sat = sat_main.SAT() | |
144 | |
145 return JPLoop |