Mercurial > libervia-backend
comparison sat_frontends/jp/base.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 | fee60f17ebac |
children | d9f328374473 |
comparison
equal
deleted
inserted
replaced
3042:964abd07dc03 | 3043:3df611adb598 |
---|---|
1 #!/usr/bin/env python2 | 1 #!/usr/bin/env python3 |
2 # -*- coding: utf-8 -*- | |
3 | 2 |
4 # jp: a SAT command line tool | 3 # jp: a SAT command line tool |
5 # Copyright (C) 2009-2019 Jérôme Poisson (goffi@goffi.org) | 4 # Copyright (C) 2009-2019 Jérôme Poisson (goffi@goffi.org) |
6 | 5 |
7 # This program is free software: you can redistribute it and/or modify | 6 # This program is free software: you can redistribute it and/or modify |
39 from sat.tools.common import dynamic_import | 38 from sat.tools.common import dynamic_import |
40 from sat.tools.common import uri | 39 from sat.tools.common import uri |
41 from sat.tools.common import date_utils | 40 from sat.tools.common import date_utils |
42 from sat.core import exceptions | 41 from sat.core import exceptions |
43 import sat_frontends.jp | 42 import sat_frontends.jp |
43 from sat_frontends.jp.loops import QuitException, getJPLoop | |
44 from sat_frontends.jp.constants import Const as C | 44 from sat_frontends.jp.constants import Const as C |
45 from sat_frontends.tools import misc | 45 from sat_frontends.tools import misc |
46 import xml.etree.ElementTree as ET # FIXME: used temporarily to manage XMLUI | 46 import xml.etree.ElementTree as ET # FIXME: used temporarily to manage XMLUI |
47 from collections import OrderedDict | 47 from collections import OrderedDict |
48 | 48 |
49 ## bridge handling | 49 ## bridge handling |
50 # we get bridge name from conf and initialise the right class accordingly | 50 # we get bridge name from conf and initialise the right class accordingly |
51 main_config = config.parseMainConf() | 51 main_config = config.parseMainConf() |
52 bridge_name = config.getConfig(main_config, '', 'bridge', 'dbus') | 52 bridge_name = config.getConfig(main_config, '', 'bridge', 'dbus') |
53 USER_INTER_MSG = _("User interruption: good bye") | 53 JPLoop = getJPLoop(bridge_name) |
54 | 54 |
55 | |
56 class QuitException(BaseException): | |
57 """Quitting is requested | |
58 | |
59 This is used to stop execution when host.quit() is called | |
60 """ | |
61 | |
62 | |
63 # TODO: move loops handling in a separated module | |
64 if 'dbus' in bridge_name: | |
65 from gi.repository import GLib | |
66 | |
67 | |
68 class JPLoop(object): | |
69 | |
70 def __init__(self): | |
71 self.loop = GLib.MainLoop() | |
72 | |
73 def run(self): | |
74 self.loop.run() | |
75 | |
76 def quit(self, exit_code): | |
77 self.loop.quit() | |
78 sys.exit(exit_code) | |
79 | |
80 def call_later(self, delay, callback, *args): | |
81 """call a callback repeatedly | |
82 | |
83 @param delay(int): delay between calls in ms | |
84 @param callback(callable): method to call | |
85 if the callback return True, the call will continue | |
86 else the calls will stop | |
87 @param *args: args of the callbac | |
88 """ | |
89 GLib.timeout_add(delay, callback, *args) | |
90 | |
91 else: | |
92 import signal | |
93 from twisted.internet import asyncioreactor | |
94 asyncioreactor.install() | |
95 from twisted.internet import reactor, defer | |
96 | |
97 class JPLoop(object): | |
98 | |
99 def __init__(self): | |
100 # exit code must be set when using quit, so if it's not set | |
101 # something got wrong and we must report it | |
102 self._exit_code = C.EXIT_INTERNAL_ERROR | |
103 | |
104 def run(self, jp, *args): | |
105 self.jp = jp | |
106 signal.signal(signal.SIGINT, self._on_sigint) | |
107 defer.ensureDeferred(self._start(jp, *args)) | |
108 try: | |
109 reactor.run(installSignalHandlers=False) | |
110 except SystemExit as e: | |
111 self._exit_code = e.code | |
112 sys.exit(self._exit_code) | |
113 | |
114 async def _start(self, jp, *args): | |
115 fut = asyncio.ensure_future(jp.main(*args)) | |
116 try: | |
117 await defer.Deferred.fromFuture(fut) | |
118 except BaseException: | |
119 import traceback | |
120 traceback.print_exc() | |
121 jp.quit(1) | |
122 | |
123 def quit(self, exit_code): | |
124 self._exit_code = exit_code | |
125 reactor.stop() | |
126 | |
127 def _timeout_cb(self, args, callback, delay): | |
128 try: | |
129 ret = callback(*args) | |
130 # FIXME: temporary hack to avoid traceback when using XMLUI | |
131 # to be removed once create_task is not used anymore in | |
132 # xmlui_manager (i.e. once sat_frontends.tools.xmlui fully supports | |
133 # async syntax) | |
134 except QuitException: | |
135 return | |
136 if ret: | |
137 reactor.callLater(delay, self._timeout_cb, args, callback, delay) | |
138 | |
139 def call_later(self, delay, callback, *args): | |
140 delay = float(delay) / 1000 | |
141 reactor.callLater(delay, self._timeout_cb, args, callback, delay) | |
142 | |
143 def _on_sigint(self, sig_number, stack_frame): | |
144 """Called on keyboard interruption | |
145 | |
146 Print user interruption message, set exit code and stop reactor | |
147 """ | |
148 print("\r" + USER_INTER_MSG) | |
149 self._exit_code = C.EXIT_USER_CANCELLED | |
150 reactor.callFromThread(reactor.stop) | |
151 | |
152 | |
153 if bridge_name == "embedded": | |
154 from sat.core import sat_main | |
155 sat = sat_main.SAT() | |
156 | 55 |
157 try: | 56 try: |
158 import progressbar | 57 import progressbar |
159 except ImportError: | 58 except ImportError: |
160 msg = (_('ProgressBar not available, please download it at ' | 59 msg = (_('ProgressBar not available, please download it at ' |