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 '