Mercurial > libervia-backend
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: |