comparison frontends/src/jp/base.py @ 2098:e0066920a661

primitivus, jp: dynamic bridge + fixed D-Bus bridge: Primitivus and jp can now load the bridge specified in sat.conf, but they are only working with D-Bus so far (need to change all sync calls to async). Exit code 3 is used for Bridge init error.
author Goffi <goffi@goffi.org>
date Sun, 18 Dec 2016 16:28:51 +0100
parents 4633cfcbcccb
children dc5d214f0a3b
comparison
equal deleted inserted replaced
2097:4bc408b549cd 2098:e0066920a661
27 27
28 import sys 28 import sys
29 import locale 29 import locale
30 import os.path 30 import os.path
31 import argparse 31 import argparse
32 from gi.repository import GLib
33 from glob import iglob 32 from glob import iglob
34 from importlib import import_module 33 from importlib import import_module
35 from sat_frontends.tools.jid import JID 34 from sat_frontends.tools.jid import JID
36 from sat_frontends.bridge.dbus_bridge import Bridge 35 from sat.tools import config
36 from sat.tools.common import dynamic_import
37 from sat.core import exceptions 37 from sat.core import exceptions
38 import sat_frontends.jp 38 import sat_frontends.jp
39 from sat_frontends.jp.constants import Const as C 39 from sat_frontends.jp.constants import Const as C
40 import xml.etree.ElementTree as ET # FIXME: used temporarily to manage XMLUI 40 import xml.etree.ElementTree as ET # FIXME: used temporarily to manage XMLUI
41 import shlex 41 import shlex
42 from collections import OrderedDict 42 from collections import OrderedDict
43
44 ## bridge handling
45 # we get bridge name from conf and initialise the right class accordingly
46 main_config = config.parseMainConf()
47 bridge_name = config.getConfig(main_config, '', 'bridge', 'dbus')
48
49
50 # TODO: move loops handling in a separated module
51 if 'dbus' in bridge_name:
52 from gi.repository import GLib
53
54
55 class JPLoop(object):
56
57 def __init__(self):
58 self.loop = GLib.MainLoop()
59
60 def run(self):
61 self.loop.run()
62
63 def quit(self):
64 self.loop.quit()
65
66 def call_later(self, delay, callback, *args):
67 """call a callback repeatedly
68
69 @param delay(int): delay between calls in ms
70 @param callback(callable): method to call
71 if the callback return True, the call will continue
72 else the calls will stop
73 @param *args: args of the callbac
74 """
75 GLib.timeout_add(delay, callback, *args)
76
77 else:
78 print u"can't start jp: only D-Bus bridge is currently handled"
79 sys.exit(C.EXIT_ERROR)
80 # FIXME: twisted loop can be used when jp can handle fully async bridges
81 # from twisted.internet import reactor
82
83 # class JPLoop(object):
84
85 # def run(self):
86 # reactor.run()
87
88 # def quit(self):
89 # reactor.stop()
90
91 # def _timeout_cb(self, args, callback, delay):
92 # ret = callback(*args)
93 # if ret:
94 # reactor.callLater(delay, self._timeout_cb, args, callback, delay)
95
96 # def call_later(self, delay, callback, *args):
97 # delay = float(delay) / 1000
98 # reactor.callLater(delay, self._timeout_cb, args, callback, delay)
99
100 if bridge_name == "embedded":
101 from sat.core import sat_main
102 sat = sat_main.SAT()
43 103
44 if sys.version_info < (2, 7, 3): 104 if sys.version_info < (2, 7, 3):
45 # XXX: shlex.split only handle unicode since python 2.7.3 105 # XXX: shlex.split only handle unicode since python 2.7.3
46 # this is a workaround for older versions 106 # this is a workaround for older versions
47 old_split = shlex.split 107 old_split = shlex.split
50 shlex.split = new_split 110 shlex.split = new_split
51 111
52 try: 112 try:
53 import progressbar 113 import progressbar
54 except ImportError: 114 except ImportError:
55 log.info (_('ProgressBar not available, please download it at http://pypi.python.org/pypi/progressbar')) 115 log.info (_(u'ProgressBar not available, please download it at http://pypi.python.org/pypi/progressbar'))
56 log.info (_('Progress bar deactivated\n--\n')) 116 log.info (_(u'Progress bar deactivated\n--\n'))
57 progressbar=None 117 progressbar=None
58 118
59 #consts 119 #consts
60 PROG_NAME = u"jp" 120 PROG_NAME = u"jp"
61 DESCRIPTION = """This software is a command line tool for XMPP. 121 DESCRIPTION = """This software is a command line tool for XMPP.
93 @attribute progress_success(callable): method to call when progress is successfully finished 153 @attribute progress_success(callable): method to call when progress is successfully finished
94 by default display a message 154 by default display a message
95 @attribute progress_failure(callable): method to call when progress failed 155 @attribute progress_failure(callable): method to call when progress failed
96 by default display a message 156 by default display a message
97 """ 157 """
98 try: 158 # FIXME: need_loop should be removed, everything must be async in bridge so
99 self.bridge = Bridge() 159 # loop will always be needed
100 except exceptions.BridgeExceptionNoService: 160 bridge_module = dynamic_import.bridge(bridge_name, 'sat_frontends.bridge')
101 print(_(u"Can't connect to SàT backend, are you sure it's launched ?")) 161 if bridge_module is None:
162 log.error(u"Can't import {} bridge".format(bridge_name))
102 sys.exit(1) 163 sys.exit(1)
103 except exceptions.BridgeInitError: 164
104 print(_(u"Can't init bridge")) 165 self.bridge = bridge_module.Bridge()
105 sys.exit(1) 166 self.bridge.bridgeConnect(callback=self._bridgeCb, errback=self._bridgeEb)
106 167
168 def _bridgeCb(self):
107 self.parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter, 169 self.parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter,
108 description=DESCRIPTION) 170 description=DESCRIPTION)
109
110 self._make_parents() 171 self._make_parents()
111 self.add_parser_options() 172 self.add_parser_options()
112 self.subparsers = self.parser.add_subparsers(title=_('Available commands'), dest='subparser_name') 173 self.subparsers = self.parser.add_subparsers(title=_('Available commands'), dest='subparser_name')
113 self._auto_loop = False # when loop is used for internal reasons 174 self._auto_loop = False # when loop is used for internal reasons
114 self._need_loop = False 175 self._need_loop = False
119 180
120 # outputs 181 # outputs
121 self._outputs = {} 182 self._outputs = {}
122 for type_ in C.OUTPUT_TYPES: 183 for type_ in C.OUTPUT_TYPES:
123 self._outputs[type_] = OrderedDict() 184 self._outputs[type_] = OrderedDict()
185
186 def _bridgeEb(self, failure):
187 if isinstance(failure, exceptions.BridgeExceptionNoService):
188 print(_(u"Can't connect to SàT backend, are you sure it's launched ?"))
189 elif isinstance(failure, exceptions.BridgeInitError):
190 print(_(u"Can't init bridge"))
191 else:
192 print(_(u"Error while initialising bridge: {}".format(failure)))
193 sys.exit(C.EXIT_BRIDGE_ERROR)
124 194
125 @property 195 @property
126 def version(self): 196 def version(self):
127 return self.bridge.getVersion() 197 return self.bridge.getVersion()
128 198
285 self._start_loop() 355 self._start_loop()
286 except KeyboardInterrupt: 356 except KeyboardInterrupt:
287 log.info(_("User interruption: good bye")) 357 log.info(_("User interruption: good bye"))
288 358
289 def _start_loop(self): 359 def _start_loop(self):
290 self.loop = GLib.MainLoop() 360 self.loop = JPLoop()
291 self.loop.run() 361 self.loop.run()
292 362
293 def stop_loop(self): 363 def stop_loop(self):
294 try: 364 try:
295 self.loop.quit() 365 self.loop.quit()
302 /!\: return must be used after calling this method ! 372 /!\: return must be used after calling this method !
303 """ 373 """
304 assert self._need_loop 374 assert self._need_loop
305 # XXX: python-dbus will show a traceback if we exit in a signal handler 375 # XXX: python-dbus will show a traceback if we exit in a signal handler
306 # so we use this little timeout trick to avoid it 376 # so we use this little timeout trick to avoid it
307 GLib.timeout_add(0, self.quit, errcode) 377 self.loop.call_later(0, self.quit, errcode)
308 378
309 def quit(self, errcode=0): 379 def quit(self, errcode=0):
310 # first the onQuitCallbacks 380 # first the onQuitCallbacks
311 try: 381 try:
312 callbacks_list = self._onQuitCallbacks 382 callbacks_list = self._onQuitCallbacks
435 505
436 class CommandBase(object): 506 class CommandBase(object):
437 507
438 def __init__(self, host, name, use_profile=True, use_output=False, 508 def __init__(self, host, name, use_profile=True, use_output=False,
439 need_connect=None, help=None, **kwargs): 509 need_connect=None, help=None, **kwargs):
440 """ Initialise CommandBase 510 """Initialise CommandBase
511
441 @param host: Jp instance 512 @param host: Jp instance
442 @param name(unicode): name of the new command 513 @param name(unicode): name of the new command
443 @param use_profile(bool): if True, add profile selection/connection commands 514 @param use_profile(bool): if True, add profile selection/connection commands
444 @param use_output(bool, dict): if not False, add --output option 515 @param use_output(bool, dict): if not False, add --output option
445 @param need_connect(bool, None): True if profile connection is needed 516 @param need_connect(bool, None): True if profile connection is needed
451 use_* are handled directly, they can be: 522 use_* are handled directly, they can be:
452 - use_progress(bool): if True, add progress bar activation option 523 - use_progress(bool): if True, add progress bar activation option
453 progress* signals will be handled 524 progress* signals will be handled
454 - use_verbose(bool): if True, add verbosity option 525 - use_verbose(bool): if True, add verbosity option
455 @attribute need_loop(bool): to set by commands when loop is needed 526 @attribute need_loop(bool): to set by commands when loop is needed
456
457 """ 527 """
458 self.need_loop = False # to be set by commands when loop is needed 528 self.need_loop = False # to be set by commands when loop is needed
459 try: # If we have subcommands, host is a CommandBase and we need to use host.host 529 try: # If we have subcommands, host is a CommandBase and we need to use host.host
460 self.host = host.host 530 self.host = host.host
461 except AttributeError: 531 except AttributeError:
534 except AttributeError: 604 except AttributeError:
535 self.host.progress_ids_cache = [cache_data] 605 self.host.progress_ids_cache = [cache_data]
536 else: 606 else:
537 if self.host.watch_progress and uid == self.progress_id: 607 if self.host.watch_progress and uid == self.progress_id:
538 self.onProgressStarted(metadata) 608 self.onProgressStarted(metadata)
539 GLib.timeout_add(PROGRESS_DELAY, self.progressUpdate) 609 self.loop.call_later(PROGRESS_DELAY, self.progressUpdate)
540 610
541 def progressFinishedHandler(self, uid, metadata, profile): 611 def progressFinishedHandler(self, uid, metadata, profile):
542 if profile != self.profile: 612 if profile != self.profile:
543 return 613 return
544 if uid == self.progress_id: 614 if uid == self.progress_id: