changeset 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 4bc408b549cd
children ad88808591ef
files frontends/src/bridge/bridge_frontend.py frontends/src/jp/base.py frontends/src/jp/constants.py frontends/src/primitivus/primitivus src/bridge/bridge_constructor/constructors/embedded/embedded_template.py
diffstat 5 files changed, 114 insertions(+), 29 deletions(-) [+]
line wrap: on
line diff
--- a/frontends/src/bridge/bridge_frontend.py	Sun Dec 18 16:28:46 2016 +0100
+++ b/frontends/src/bridge/bridge_frontend.py	Sun Dec 18 16:28:51 2016 +0100
@@ -19,8 +19,6 @@
 
 
 class BridgeFrontend(object):
-    def __init__(self):
-        print "Bridge frontend initialization"
 
     def register(self, functionName, handler):
         raise NotImplementedError
--- a/frontends/src/jp/base.py	Sun Dec 18 16:28:46 2016 +0100
+++ b/frontends/src/jp/base.py	Sun Dec 18 16:28:51 2016 +0100
@@ -29,11 +29,11 @@
 import locale
 import os.path
 import argparse
-from gi.repository import GLib
 from glob import iglob
 from importlib import import_module
 from sat_frontends.tools.jid import JID
-from sat_frontends.bridge.dbus_bridge import Bridge
+from sat.tools import config
+from sat.tools.common import dynamic_import
 from sat.core import exceptions
 import sat_frontends.jp
 from sat_frontends.jp.constants import Const as C
@@ -41,6 +41,66 @@
 import shlex
 from collections import OrderedDict
 
+## bridge handling
+# we get bridge name from conf and initialise the right class accordingly
+main_config = config.parseMainConf()
+bridge_name = config.getConfig(main_config, '', 'bridge', 'dbus')
+
+
+# TODO: move loops handling in a separated module
+if 'dbus' in bridge_name:
+    from gi.repository import GLib
+
+
+    class JPLoop(object):
+
+        def __init__(self):
+            self.loop = GLib.MainLoop()
+
+        def run(self):
+            self.loop.run()
+
+        def quit(self):
+            self.loop.quit()
+
+        def call_later(self, delay, callback, *args):
+            """call a callback repeatedly
+
+            @param delay(int): delay between calls in ms
+            @param callback(callable): method to call
+                if the callback return True, the call will continue
+                else the calls will stop
+            @param *args: args of the callbac
+            """
+            GLib.timeout_add(delay, callback, *args)
+
+else:
+    print u"can't start jp: only D-Bus bridge is currently handled"
+    sys.exit(C.EXIT_ERROR)
+    # FIXME: twisted loop can be used when jp can handle fully async bridges
+    # from twisted.internet import reactor
+
+    # class JPLoop(object):
+
+    #     def run(self):
+    #         reactor.run()
+
+    #     def quit(self):
+    #         reactor.stop()
+
+    #     def _timeout_cb(self, args, callback, delay):
+    #         ret = callback(*args)
+    #         if ret:
+    #             reactor.callLater(delay, self._timeout_cb, args, callback, delay)
+
+    #     def call_later(self, delay, callback, *args):
+    #         delay = float(delay) / 1000
+    #         reactor.callLater(delay, self._timeout_cb, args, callback, delay)
+
+if bridge_name == "embedded":
+    from sat.core import sat_main
+    sat = sat_main.SAT()
+
 if sys.version_info < (2, 7, 3):
     # XXX: shlex.split only handle unicode since python 2.7.3
     # this is a workaround for older versions
@@ -52,8 +112,8 @@
 try:
     import progressbar
 except ImportError:
-    log.info (_('ProgressBar not available, please download it at http://pypi.python.org/pypi/progressbar'))
-    log.info (_('Progress bar deactivated\n--\n'))
+    log.info (_(u'ProgressBar not available, please download it at http://pypi.python.org/pypi/progressbar'))
+    log.info (_(u'Progress bar deactivated\n--\n'))
     progressbar=None
 
 #consts
@@ -95,18 +155,19 @@
         @attribute progress_failure(callable): method to call when progress failed
             by default display a message
         """
-        try:
-            self.bridge = Bridge()
-        except exceptions.BridgeExceptionNoService:
-            print(_(u"Can't connect to SàT backend, are you sure it's launched ?"))
-            sys.exit(1)
-        except exceptions.BridgeInitError:
-            print(_(u"Can't init bridge"))
+        # FIXME: need_loop should be removed, everything must be async in bridge so
+        #        loop will always be needed
+        bridge_module = dynamic_import.bridge(bridge_name, 'sat_frontends.bridge')
+        if bridge_module is None:
+            log.error(u"Can't import {} bridge".format(bridge_name))
             sys.exit(1)
 
+        self.bridge = bridge_module.Bridge()
+        self.bridge.bridgeConnect(callback=self._bridgeCb, errback=self._bridgeEb)
+
+    def _bridgeCb(self):
         self.parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter,
                                               description=DESCRIPTION)
-
         self._make_parents()
         self.add_parser_options()
         self.subparsers = self.parser.add_subparsers(title=_('Available commands'), dest='subparser_name')
@@ -122,6 +183,15 @@
         for type_ in C.OUTPUT_TYPES:
             self._outputs[type_] = OrderedDict()
 
+    def _bridgeEb(self, failure):
+        if isinstance(failure, exceptions.BridgeExceptionNoService):
+            print(_(u"Can't connect to SàT backend, are you sure it's launched ?"))
+        elif isinstance(failure, exceptions.BridgeInitError):
+            print(_(u"Can't init bridge"))
+        else:
+            print(_(u"Error while initialising bridge: {}".format(failure)))
+        sys.exit(C.EXIT_BRIDGE_ERROR)
+
     @property
     def version(self):
         return self.bridge.getVersion()
@@ -287,7 +357,7 @@
             log.info(_("User interruption: good bye"))
 
     def _start_loop(self):
-        self.loop = GLib.MainLoop()
+        self.loop = JPLoop()
         self.loop.run()
 
     def stop_loop(self):
@@ -304,7 +374,7 @@
         assert self._need_loop
         # XXX: python-dbus will show a traceback if we exit in a signal handler
         # so we use this little timeout trick to avoid it
-        GLib.timeout_add(0, self.quit, errcode)
+        self.loop.call_later(0, self.quit, errcode)
 
     def quit(self, errcode=0):
         # first the onQuitCallbacks
@@ -437,7 +507,8 @@
 
     def __init__(self, host, name, use_profile=True, use_output=False,
                        need_connect=None, help=None, **kwargs):
-        """ Initialise CommandBase
+        """Initialise CommandBase
+
         @param host: Jp instance
         @param name(unicode): name of the new command
         @param use_profile(bool): if True, add profile selection/connection commands
@@ -453,7 +524,6 @@
                 progress* signals will be handled
             - use_verbose(bool): if True, add verbosity option
         @attribute need_loop(bool): to set by commands when loop is needed
-
         """
         self.need_loop = False # to be set by commands when loop is needed
         try: # If we have subcommands, host is a CommandBase and we need to use host.host
@@ -536,7 +606,7 @@
         else:
             if self.host.watch_progress and uid == self.progress_id:
                 self.onProgressStarted(metadata)
-                GLib.timeout_add(PROGRESS_DELAY, self.progressUpdate)
+                self.loop.call_later(PROGRESS_DELAY, self.progressUpdate)
 
     def progressFinishedHandler(self, uid, metadata, profile):
         if profile != self.profile:
--- a/frontends/src/jp/constants.py	Sun Dec 18 16:28:46 2016 +0100
+++ b/frontends/src/jp/constants.py	Sun Dec 18 16:28:51 2016 +0100
@@ -34,6 +34,7 @@
     EXIT_OK = 0
     EXIT_ERROR = 1 # generic error, when nothing else match
     EXIT_BAD_ARG = 2 # arguments given by user are bad
+    EXIT_BRIDGE_ERROR = 3 # can't connect to bridge
     EXIT_NOT_FOUND = 16 # an item required by a command was not found
     EXIT_FILE_NOT_EXE = 126 # a file to be executed was found, but it was not an executable utility (cf. man 1 exit)
     EXIT_CMD_NOT_FOUND = 127 # a utility to be executed was not found (cf. man 1 exit)
--- a/frontends/src/primitivus/primitivus	Sun Dec 18 16:28:46 2016 +0100
+++ b/frontends/src/primitivus/primitivus	Sun Dec 18 16:28:51 2016 +0100
@@ -28,7 +28,6 @@
 import urwid
 from urwid.util import is_wide_char
 from urwid_satext import sat_widgets
-from sat_frontends.bridge.dbus_bridge import Bridge
 from sat_frontends.quick_frontend.quick_app import QuickApp
 from sat_frontends.quick_frontend import quick_utils
 from sat_frontends.quick_frontend import quick_chat
@@ -41,9 +40,17 @@
 from sat_frontends.primitivus.keys import action_key_map as a_key
 from sat_frontends.primitivus import config
 from sat_frontends.tools.misc import InputHistory
+from sat.tools.common import dynamic_import
 from sat_frontends.tools import jid
 import signal
 import sys
+## bridge handling
+# we get bridge name from conf and initialise the right class accordingly
+main_config = sat_config.parseMainConf()
+bridge_name = sat_config.getConfig(main_config, '', 'bridge', 'dbus')
+if 'dbus' not in bridge_name:
+    print(u"only D-Bus bridge is currently supported")
+    sys.exit(3)
 
 
 class EditBar(sat_widgets.ModalEdit):
@@ -276,11 +283,18 @@
     AVATAR_HANDLER = False
 
     def __init__(self):
-        QuickApp.__init__(self, create_bridge=Bridge, xmlui=xmlui, check_options=quick_utils.check_options)
-
+        bridge_module = dynamic_import.bridge(bridge_name, 'sat_frontends.bridge')
+        if bridge_module is None:
+            log.error(u"Can't import {} bridge".format(bridge_name))
+            sys.exit(3)
+        else:
+            log.debug(u"Loading {} bridge".format(bridge_name))
+        QuickApp.__init__(self, bridge_factory=bridge_module.Bridge, xmlui=xmlui, check_options=quick_utils.check_options, connect_bridge=False)
         ## main loop setup ##
-        self.main_widget = ProfileManager(self)
-        self.loop = urwid.MainLoop(self.main_widget, C.PALETTE, event_loop=urwid.GLibEventLoop(), input_filter=self.inputFilter, unhandled_input=self.keyHandler)
+        event_loop = urwid.GLibEventLoop if 'dbus' in bridge_name else urwid.TwistedEventLoop
+        self.loop = urwid.MainLoop(urwid.SolidFill(), C.PALETTE, event_loop=event_loop(), input_filter=self.inputFilter, unhandled_input=self.keyHandler)
+
+    def onBridgeConnected(self):
 
         ##misc setup##
         self._visible_widgets = set()
@@ -302,6 +316,9 @@
             sys.stdout.write("\033[?2004h")
             self._bracketed_mode_set = True
 
+        self.loop.widget = self.main_widget = ProfileManager(self)
+        self.postInit()
+
     @property
     def visible_widgets(self):
         return self._visible_widgets
@@ -343,8 +360,7 @@
             pass
 
     def start(self):
-        self.i = 0
-        self.loop.set_alarm_in(0,lambda a,b: self.postInit())
+        self.connectBridge()
         self.loop.run()
 
     def postInit(self):
@@ -813,5 +829,5 @@
         else:
             log.warning(u"No ContactList widget found for profile {}".format(profile))
 
-sat = PrimitivusApp()
-sat.start()
+primitivus = PrimitivusApp()
+primitivus.start()
--- a/src/bridge/bridge_constructor/constructors/embedded/embedded_template.py	Sun Dec 18 16:28:46 2016 +0100
+++ b/src/bridge/bridge_constructor/constructors/embedded/embedded_template.py	Sun Dec 18 16:28:51 2016 +0100
@@ -24,7 +24,7 @@
 
 class _Bridge(object):
     def __init__(self):
-        log.info(u"Init embedded bridge...")
+        log.debug(u"Init embedded bridge...")
         self._methods_cbs = {}
         self._signals_cbs = {
             "core": {},