diff src/server/server.py @ 1113:cdd389ef97bc

server: code style reformatting using black
author Goffi <goffi@goffi.org>
date Fri, 29 Jun 2018 17:45:26 +0200
parents 7ec1ba86d38d
children
line wrap: on
line diff
--- a/src/server/server.py	Sun Jun 24 22:21:25 2018 +0200
+++ b/src/server/server.py	Fri Jun 29 17:45:26 2018 +0200
@@ -32,8 +32,13 @@
 from txjsonrpc import jsonrpclib
 
 from sat.core.log import getLogger
+
 log = getLogger(__name__)
-from sat_frontends.bridge.dbus_bridge import Bridge, BridgeExceptionNoService, const_TIMEOUT as BRIDGE_TIMEOUT
+from sat_frontends.bridge.dbus_bridge import (
+    Bridge,
+    BridgeExceptionNoService,
+    const_TIMEOUT as BRIDGE_TIMEOUT,
+)
 from sat.core.i18n import _, D_
 from sat.core import exceptions
 from sat.tools import utils
@@ -112,61 +117,85 @@
         self.redirections = {}
         self.inv_redirections = {}  # new URL to old URL map
 
-        if options['url_redirections_dict'] and not options['url_redirections_profile']:
+        if options["url_redirections_dict"] and not options["url_redirections_profile"]:
             # FIXME: url_redirections_profile should not be needed. It is currently used to
             #        redirect to an URL which associate the profile with the service, but this
             #        is not clean, and service should be explicitly specified
-            raise ValueError(u"url_redirections_profile need to be filled if you want to use url_redirections_dict")
+            raise ValueError(
+                u"url_redirections_profile need to be filled if you want to use url_redirections_dict"
+            )
 
-        for old, new_data in options['url_redirections_dict'].iteritems():
+        for old, new_data in options["url_redirections_dict"].iteritems():
             # new_data can be a dictionary or a unicode url
             if isinstance(new_data, dict):
                 # new_data dict must contain either "url", "page" or "path" key (exclusive)
                 # if "path" is used, a file url is constructed with it
-                if len({'path', 'url', 'page'}.intersection(new_data.keys())) != 1:
-                    raise ValueError(u'You must have one and only one of "url", "page" or "path" key in your url_redirections_dict data')
-                if 'url' in new_data:
-                    new = new_data['url']
-                elif 'page' in new_data:
+                if len({"path", "url", "page"}.intersection(new_data.keys())) != 1:
+                    raise ValueError(
+                        u'You must have one and only one of "url", "page" or "path" key in your url_redirections_dict data'
+                    )
+                if "url" in new_data:
+                    new = new_data["url"]
+                elif "page" in new_data:
                     new = new_data
-                    new['type'] = 'page'
-                    new.setdefault('path_args', [])
-                    if not isinstance(new['path_args'], list):
-                        log.error(_(u'"path_args" in redirection of {old} must be a list. Ignoring the redirection'.format(
-                            old = old)))
+                    new["type"] = "page"
+                    new.setdefault("path_args", [])
+                    if not isinstance(new["path_args"], list):
+                        log.error(
+                            _(
+                                u'"path_args" in redirection of {old} must be a list. Ignoring the redirection'.format(
+                                    old=old
+                                )
+                            )
+                        )
                         continue
-                    new.setdefault('query_args', {})
-                    if not isinstance(new['query_args'], dict):
-                        log.error(_(u'"query_args" in redirection of {old} must be a dictionary. Ignoring the redirection'.format(
-                            old = old)))
+                    new.setdefault("query_args", {})
+                    if not isinstance(new["query_args"], dict):
+                        log.error(
+                            _(
+                                u'"query_args" in redirection of {old} must be a dictionary. Ignoring the redirection'.format(
+                                    old=old
+                                )
+                            )
+                        )
                         continue
-                    new['path_args'] = [quote(a) for a in new['path_args']]
+                    new["path_args"] = [quote(a) for a in new["path_args"]]
                     # we keep an inversed dict of page redirection (page/path_args => redirecting URL)
                     # so getURL can return the redirecting URL if the same arguments are used
                     # making the URL consistent
-                    args_hash = tuple(new['path_args'])
-                    LiberviaPage.pages_redirects.setdefault(new_data['page'], {})[args_hash] = old
+                    args_hash = tuple(new["path_args"])
+                    LiberviaPage.pages_redirects.setdefault(new_data["page"], {})[
+                        args_hash
+                    ] = old
 
                     # we need lists in query_args because it will be used
                     # as it in request.path_args
-                    for k,v in new['query_args'].iteritems():
+                    for k, v in new["query_args"].iteritems():
                         if isinstance(v, basestring):
-                            new['query_args'][k] = [v]
-                elif 'path' in new_data:
-                    new = 'file:{}'.format(urllib.quote(new_data['path']))
+                            new["query_args"][k] = [v]
+                elif "path" in new_data:
+                    new = "file:{}".format(urllib.quote(new_data["path"]))
             elif isinstance(new_data, basestring):
                 new = new_data
                 new_data = {}
             else:
-                log.error(_(u"ignoring invalid redirection value: {new_data}").format(new_data=new_data))
+                log.error(
+                    _(u"ignoring invalid redirection value: {new_data}").format(
+                        new_data=new_data
+                    )
+                )
                 continue
 
             # some normalization
             if not old.strip():
                 # root URL special case
-                old = ''
-            elif not old.startswith('/'):
-                log.error(_(u"redirected url must start with '/', got {value}. Ignoring").format(value=old))
+                old = ""
+            elif not old.startswith("/"):
+                log.error(
+                    _(
+                        u"redirected url must start with '/', got {value}. Ignoring"
+                    ).format(value=old)
+                )
                 continue
             else:
                 old = self._normalizeURL(old)
@@ -176,73 +205,105 @@
                 # which ared use dynamically when the request is done
                 self.redirections[old] = new
                 if not old:
-                    if new[u'type'] == u'page':
-                        log.info(_(u"Root URL redirected to page {name}").format(name=new[u'page']))
+                    if new[u"type"] == u"page":
+                        log.info(
+                            _(u"Root URL redirected to page {name}").format(
+                                name=new[u"page"]
+                            )
+                        )
                 else:
-                    if new[u'type'] == u'page':
-                        page = LiberviaPage.getPageByName(new[u'page'])
-                        url = page.getURL(*new.get(u'path_args', []))
+                    if new[u"type"] == u"page":
+                        page = LiberviaPage.getPageByName(new[u"page"])
+                        url = page.getURL(*new.get(u"path_args", []))
                         self.inv_redirections[url] = old
                 continue
 
             # at this point we have a redirection URL in new, we can parse it
-            new_url = urlparse.urlsplit(new.encode('utf-8'))
+            new_url = urlparse.urlsplit(new.encode("utf-8"))
 
             # we handle the known URL schemes
-            if new_url.scheme == 'xmpp':
+            if new_url.scheme == "xmpp":
                 location = LiberviaPage.getPagePathFromURI(new)
                 if location is None:
-                    log.warning(_(u"ignoring redirection, no page found to handle this URI: {uri}").format(uri=new))
+                    log.warning(
+                        _(
+                            u"ignoring redirection, no page found to handle this URI: {uri}"
+                        ).format(uri=new)
+                    )
                     continue
                 request_data = self._getRequestData(location)
                 if old:
                     self.inv_redirections[location] = old
 
-            elif new_url.scheme in ('', 'http', 'https'):
+            elif new_url.scheme in ("", "http", "https"):
                 # direct redirection
                 if new_url.netloc:
-                    raise NotImplementedError(u"netloc ({netloc}) is not implemented yet for url_redirections_dict, it is not possible to redirect to an external website".format(
-                        netloc = new_url.netloc))
-                location = urlparse.urlunsplit(('', '', new_url.path, new_url.query, new_url.fragment)).decode('utf-8')
+                    raise NotImplementedError(
+                        u"netloc ({netloc}) is not implemented yet for url_redirections_dict, it is not possible to redirect to an external website".format(
+                            netloc=new_url.netloc
+                        )
+                    )
+                location = urlparse.urlunsplit(
+                    ("", "", new_url.path, new_url.query, new_url.fragment)
+                ).decode("utf-8")
                 request_data = self._getRequestData(location)
                 if old:
                     self.inv_redirections[location] = old
 
-            elif new_url.scheme in ('file'):
+            elif new_url.scheme in ("file"):
                 # file or directory
                 if new_url.netloc:
-                    raise NotImplementedError(u"netloc ({netloc}) is not implemented for url redirection to file system, it is not possible to redirect to an external host".format(
-                        netloc = new_url.netloc))
+                    raise NotImplementedError(
+                        u"netloc ({netloc}) is not implemented for url redirection to file system, it is not possible to redirect to an external host".format(
+                            netloc=new_url.netloc
+                        )
+                    )
                 path = urllib.unquote(new_url.path)
                 if not os.path.isabs(path):
-                    raise ValueError(u'file redirection must have an absolute path: e.g. file:/path/to/my/file')
+                    raise ValueError(
+                        u"file redirection must have an absolute path: e.g. file:/path/to/my/file"
+                    )
                 # for file redirection, we directly put child here
-                segments, dummy, last_segment = old.rpartition('/')
-                url_segments = segments.split('/') if segments else []
+                segments, dummy, last_segment = old.rpartition("/")
+                url_segments = segments.split("/") if segments else []
                 current = self
                 for segment in url_segments:
                     resource = web_resource.NoResource()
                     current.putChild(segment, resource)
                     current = resource
-                resource_class = ProtectedFile if new_data.get('protected',True) else static.File
+                resource_class = (
+                    ProtectedFile if new_data.get("protected", True) else static.File
+                )
                 current.putChild(last_segment, resource_class(path))
-                log.info(u"Added redirection from /{old} to file system path {path}".format(old=old.decode('utf-8'), path=path.decode('utf-8')))
-                continue # we don't want to use redirection system, so we continue here
+                log.info(
+                    u"Added redirection from /{old} to file system path {path}".format(
+                        old=old.decode("utf-8"), path=path.decode("utf-8")
+                    )
+                )
+                continue  # we don't want to use redirection system, so we continue here
 
             else:
-                raise NotImplementedError(u"{scheme}: scheme is not managed for url_redirections_dict".format(scheme=new_url.scheme))
+                raise NotImplementedError(
+                    u"{scheme}: scheme is not managed for url_redirections_dict".format(
+                        scheme=new_url.scheme
+                    )
+                )
 
             self.redirections[old] = request_data
             if not old:
-                log.info(_(u"Root URL redirected to {uri}").format(uri=request_data[1].decode('utf-8')))
+                log.info(
+                    _(u"Root URL redirected to {uri}").format(
+                        uri=request_data[1].decode("utf-8")
+                    )
+                )
 
         # no need to keep url_redirections*, they will not be used anymore
-        del options['url_redirections_dict']
-        del options['url_redirections_profile']
+        del options["url_redirections_dict"]
+        del options["url_redirections_profile"]
 
         # the default root URL, if not redirected
-        if not '' in self.redirections:
-            self.redirections[''] = self._getRequestData(C.LIBERVIA_MAIN_PAGE)
+        if not "" in self.redirections:
+            self.redirections[""] = self._getRequestData(C.LIBERVIA_MAIN_PAGE)
 
     def _normalizeURL(self, url, lower=True):
         """Return URL normalized for self.redirections dict
@@ -253,7 +314,7 @@
         """
         if lower:
             url = url.lower()
-        return '/'.join((p for p in url.encode('utf-8').split('/') if p))
+        return "/".join((p for p in url.encode("utf-8").split("/") if p))
 
     def _getRequestData(self, uri):
         """Return data needed to redirect request
@@ -265,10 +326,10 @@
             path as in Request.path
             args as in Request.args
         """
-        uri = uri.encode('utf-8')
+        uri = uri.encode("utf-8")
         # XXX: we reuse code from twisted.web.http.py here
         #      as we need to have the same behaviour
-        x = uri.split(b'?', 1)
+        x = uri.split(b"?", 1)
 
         if len(x) == 1:
             path = uri
@@ -279,7 +340,12 @@
 
         # XXX: splitted path case must not be changed, as it may be significant
         #      (e.g. for blog items)
-        return self._normalizeURL(path.decode('utf-8'), lower=False).split('/'), uri, path, args
+        return (
+            self._normalizeURL(path.decode("utf-8"), lower=False).split("/"),
+            uri,
+            path,
+            args,
+        )
 
     def _redirect(self, request, request_data):
         """Redirect an URL by rewritting request
@@ -298,40 +364,48 @@
             try:
                 dummy, uri, dummy, dummy = request_data
             except ValueError:
-                uri = u''
-            log.error(D_(u"recursive redirection, please fix this URL:\n{old} ==> {new}").format(
-                old=request.uri.decode('utf-8'),
-                new=uri.decode('utf-8'),
-                ))
+                uri = u""
+            log.error(
+                D_(
+                    u"recursive redirection, please fix this URL:\n{old} ==> {new}"
+                ).format(old=request.uri.decode("utf-8"), new=uri.decode("utf-8"))
+            )
             return web_resource.NoResource()
 
-        request._redirected = True # here to avoid recursive redirections
+        request._redirected = True  # here to avoid recursive redirections
 
         if isinstance(request_data, dict):
-            if request_data['type'] == 'page':
+            if request_data["type"] == "page":
                 try:
-                    page = LiberviaPage.getPageByName(request_data['page'])
+                    page = LiberviaPage.getPageByName(request_data["page"])
                 except KeyError:
-                    log.error(_(u"Can't find page named \"{name}\" requested in redirection").format(
-                        name = request_data['page']))
+                    log.error(
+                        _(
+                            u'Can\'t find page named "{name}" requested in redirection'
+                        ).format(name=request_data["page"])
+                    )
                     return web_resource.NoResource()
-                request.postpath = request_data['path_args'][:] + request.postpath
+                request.postpath = request_data["path_args"][:] + request.postpath
 
                 try:
-                    request.args.update(request_data['query_args'])
+                    request.args.update(request_data["query_args"])
                 except (TypeError, ValueError):
-                    log.error(_(u"Invalid args in redirection: {query_args}").format(
-                        query_args=request_data['query_args']))
+                    log.error(
+                        _(u"Invalid args in redirection: {query_args}").format(
+                            query_args=request_data["query_args"]
+                        )
+                    )
                     return web_resource.NoResource()
                 return page
             else:
-                raise exceptions.InternalError(u'unknown request_data type')
+                raise exceptions.InternalError(u"unknown request_data type")
         else:
             path_list, uri, path, args = request_data
-            log.debug(u"Redirecting URL {old} to {new}".format(
-                old=request.uri.decode('utf-8'),
-                new=uri.decode('utf-8'),
-                ))
+            log.debug(
+                u"Redirecting URL {old} to {new}".format(
+                    old=request.uri.decode("utf-8"), new=uri.decode("utf-8")
+                )
+            )
             # we change the request to reflect the new url
             request.postpath = path_list[1:] + request.postpath
             request.args = args
@@ -342,8 +416,8 @@
     def getChildWithDefault(self, name, request):
         # XXX: this method is overriden only for root url
         #      which is the only ones who need to be handled before other children
-        if name == '' and not request.postpath:
-            return self._redirect(request, self.redirections[''])
+        if name == "" and not request.postpath:
+            return self._redirect(request, self.redirections[""])
         return super(LiberviaRootResource, self).getChildWithDefault(name, request)
 
     def getChild(self, name, request):
@@ -354,7 +428,7 @@
             # XXX: we want redirections to happen only if everything else failed
             path_elt = request.prepath + request.postpath
             for idx in xrange(len(path_elt), 0, -1):
-                test_url = '/'.join(path_elt[:idx]).lower()
+                test_url = "/".join(path_elt[:idx]).lower()
                 if test_url in self.redirections:
                     request_data = self.redirections[test_url]
                     request.postpath = path_elt[idx:]
@@ -365,7 +439,9 @@
     def createSimilarFile(self, path):
         # XXX: this method need to be overriden to avoid recreating a LiberviaRootResource
 
-        f = LiberviaRootResource.__base__(path, self.defaultType, self.ignoredExts, self.registry)
+        f = LiberviaRootResource.__base__(
+            path, self.defaultType, self.ignoredExts, self.registry
+        )
         # refactoring by steps, here - constructor should almost certainly take these
         f.processors = self.processors
         f.indexNames = self.indexNames[:]
@@ -374,7 +450,6 @@
 
 
 class JSONRPCMethodManager(jsonrpc.JSONRPC):
-
     def __init__(self, sat_host):
         jsonrpc.JSONRPC.__init__(self)
         self.sat_host = sat_host
@@ -384,7 +459,6 @@
 
 
 class MethodHandler(JSONRPCMethodManager):
-
     def __init__(self, sat_host):
         JSONRPCMethodManager.__init__(self, sat_host)
 
@@ -392,10 +466,14 @@
         self.session = request.getSession()
         profile = session_iface.ISATSession(self.session).profile
         if not profile:
-            #user is not identified, we return a jsonrpc fault
+            # user is not identified, we return a jsonrpc fault
             parsed = jsonrpclib.loads(request.content.read())
-            fault = jsonrpclib.Fault(C.ERRNUM_LIBERVIA, C.NOT_ALLOWED)  # FIXME: define some standard error codes for libervia
-            return jsonrpc.JSONRPC._cbRender(self, fault, request, parsed.get('id'), parsed.get('jsonrpc'))  # pylint: disable=E1103
+            fault = jsonrpclib.Fault(
+                C.ERRNUM_LIBERVIA, C.NOT_ALLOWED
+            )  # FIXME: define some standard error codes for libervia
+            return jsonrpc.JSONRPC._cbRender(
+                self, fault, request, parsed.get("id"), parsed.get("jsonrpc")
+            )  # pylint: disable=E1103
         return jsonrpc.JSONRPC.render(self, request)
 
     @defer.inlineCallbacks
@@ -456,12 +534,16 @@
         @param status: any string to describe your status
         """
         profile = session_iface.ISATSession(self.session).profile
-        return self.sat_host.bridgeCall("setPresence", '', presence, {'': status}, profile)
+        return self.sat_host.bridgeCall(
+            "setPresence", "", presence, {"": status}, profile
+        )
 
     def jsonrpc_messageSend(self, to_jid, msg, subject, type_, extra={}):
         """send message"""
         profile = session_iface.ISATSession(self.session).profile
-        return self.asyncBridgeCall("messageSend", to_jid, msg, subject, type_, extra, profile)
+        return self.asyncBridgeCall(
+            "messageSend", to_jid, msg, subject, type_, extra, profile
+        )
 
     ## PubSub ##
 
@@ -530,7 +612,9 @@
         @return: a deferred couple with the list of items and metadatas.
         """
         profile = session_iface.ISATSession(self.session).profile
-        return self.asyncBridgeCall("mbGet", service_jid, node, max_items, item_ids, extra, profile)
+        return self.asyncBridgeCall(
+            "mbGet", service_jid, node, max_items, item_ids, extra, profile
+        )
 
     def jsonrpc_mbGetFromMany(self, publishers_type, publishers, max_items, extra):
         """Get many blog nodes at once
@@ -542,7 +626,9 @@
         @return (str): RT Deferred session id
         """
         profile = session_iface.ISATSession(self.session).profile
-        return self.sat_host.bridgeCall("mbGetFromMany", publishers_type, publishers, max_items, extra, profile)
+        return self.sat_host.bridgeCall(
+            "mbGetFromMany", publishers_type, publishers, max_items, extra, profile
+        )
 
     def jsonrpc_mbGetFromManyRTResult(self, rt_session):
         """Get results from RealTime mbGetFromMany session
@@ -552,7 +638,15 @@
         profile = session_iface.ISATSession(self.session).profile
         return self.asyncBridgeCall("mbGetFromManyRTResult", rt_session, profile)
 
-    def jsonrpc_mbGetFromManyWithComments(self, publishers_type, publishers, max_items, max_comments, rsm_dict, rsm_comments_dict):
+    def jsonrpc_mbGetFromManyWithComments(
+        self,
+        publishers_type,
+        publishers,
+        max_items,
+        max_comments,
+        rsm_dict,
+        rsm_comments_dict,
+    ):
         """Helper method to get the microblogs and their comments in one shot
 
         @param publishers_type (str): type of the list of publishers (one of "GROUP" or "JID" or "ALL")
@@ -565,7 +659,16 @@
         @return (str): RT Deferred session id
         """
         profile = session_iface.ISATSession(self.session).profile
-        return self.sat_host.bridgeCall("mbGetFromManyWithComments", publishers_type, publishers, max_items, max_comments, rsm_dict, rsm_comments_dict, profile)
+        return self.sat_host.bridgeCall(
+            "mbGetFromManyWithComments",
+            publishers_type,
+            publishers,
+            max_items,
+            max_comments,
+            rsm_dict,
+            rsm_comments_dict,
+            profile,
+        )
 
     def jsonrpc_mbGetFromManyWithCommentsRTResult(self, rt_session):
         """Get results from RealTime mbGetFromManyWithComments session
@@ -573,8 +676,9 @@
         @param rt_session (str): RT Deferred session id
         """
         profile = session_iface.ISATSession(self.session).profile
-        return self.asyncBridgeCall("mbGetFromManyWithCommentsRTResult", rt_session, profile)
-
+        return self.asyncBridgeCall(
+            "mbGetFromManyWithCommentsRTResult", rt_session, profile
+        )
 
     # def jsonrpc_sendMblog(self, type_, dest, text, extra={}):
     #     """ Send microblog message
@@ -684,26 +788,49 @@
         profile = session_iface.ISATSession(self.session).profile
         return self.sat_host.bridgeCall("getPresenceStatuses", profile)
 
-    def jsonrpc_historyGet(self, from_jid, to_jid, size, between, search=''):
+    def jsonrpc_historyGet(self, from_jid, to_jid, size, between, search=""):
         """Return history for the from_jid/to_jid couple"""
         sat_session = session_iface.ISATSession(self.session)
         profile = sat_session.profile
         sat_jid = sat_session.jid
         if not sat_jid:
-            raise exceptions.InternalError('session jid should be set')
-        if jid.JID(from_jid).userhost() != sat_jid.userhost() and jid.JID(to_jid).userhost() != sat_jid.userhost():
-            log.error(u"Trying to get history from a different jid (given (browser): {}, real (backend): {}), maybe a hack attempt ?".format(from_jid, sat_jid))
+            raise exceptions.InternalError("session jid should be set")
+        if (
+            jid.JID(from_jid).userhost() != sat_jid.userhost()
+            and jid.JID(to_jid).userhost() != sat_jid.userhost()
+        ):
+            log.error(
+                u"Trying to get history from a different jid (given (browser): {}, real (backend): {}), maybe a hack attempt ?".format(
+                    from_jid, sat_jid
+                )
+            )
             return {}
-        d = self.asyncBridgeCall("historyGet", from_jid, to_jid, size, between, search, profile)
+        d = self.asyncBridgeCall(
+            "historyGet", from_jid, to_jid, size, between, search, profile
+        )
 
         def show(result_dbus):
             result = []
             for line in result_dbus:
-                #XXX: we have to do this stupid thing because Python D-Bus use its own types instead of standard types
+                # XXX: we have to do this stupid thing because Python D-Bus use its own types instead of standard types
                 #     and txJsonRPC doesn't accept D-Bus types, resulting in a empty query
-                uuid, timestamp, from_jid, to_jid, message, subject, mess_type, extra = line
-                result.append((unicode(uuid), float(timestamp), unicode(from_jid), unicode(to_jid), dict(message), dict(subject), unicode(mess_type), dict(extra)))
+                uuid, timestamp, from_jid, to_jid, message, subject, mess_type, extra = (
+                    line
+                )
+                result.append(
+                    (
+                        unicode(uuid),
+                        float(timestamp),
+                        unicode(from_jid),
+                        unicode(to_jid),
+                        dict(message),
+                        dict(subject),
+                        unicode(mess_type),
+                        dict(extra),
+                    )
+                )
             return result
+
         d.addCallback(show)
         return d
 
@@ -726,7 +853,9 @@
         profile = session_iface.ISATSession(self.session).profile
         room_id = room_jid.split("@")[0]
         service = room_jid.split("@")[1]
-        return self.sat_host.bridgeCall("inviteMUC", contact_jid, service, room_id, {}, profile)
+        return self.sat_host.bridgeCall(
+            "inviteMUC", contact_jid, service, room_id, {}, profile
+        )
 
     def jsonrpc_mucLeave(self, room_jid):
         """Quit a Multi-User Chat room"""
@@ -734,7 +863,7 @@
         try:
             room_jid = jid.JID(room_jid)
         except:
-            log.warning('Invalid room jid')
+            log.warning("Invalid room jid")
             return
         return self.sat_host.bridgeCall("mucLeave", room_jid.userhost(), profile)
 
@@ -755,13 +884,18 @@
         @param room_jid (unicode): room JID or empty string to generate a unique name
         """
         profile = session_iface.ISATSession(self.session).profile
-        return self.sat_host.bridgeCall("tarotGameLaunch", other_players, room_jid, profile)
+        return self.sat_host.bridgeCall(
+            "tarotGameLaunch", other_players, room_jid, profile
+        )
 
     def jsonrpc_getTarotCardsPaths(self):
         """Give the path of all the tarot cards"""
         _join = os.path.join
-        _media_dir = _join(self.sat_host.media_dir, '')
-        return map(lambda x: _join(C.MEDIA_DIR, x[len(_media_dir):]), glob.glob(_join(_media_dir, C.CARDS_DIR, '*_*.png')))
+        _media_dir = _join(self.sat_host.media_dir, "")
+        return map(
+            lambda x: _join(C.MEDIA_DIR, x[len(_media_dir) :]),
+            glob.glob(_join(_media_dir, C.CARDS_DIR, "*_*.png")),
+        )
 
     def jsonrpc_tarotGameReady(self, player, referee):
         """Tell to the server that we are ready to start the game"""
@@ -771,7 +905,9 @@
     def jsonrpc_tarotGamePlayCards(self, player_nick, referee, cards):
         """Tell to the server the cards we want to put on the table"""
         profile = session_iface.ISATSession(self.session).profile
-        return self.sat_host.bridgeCall("tarotGamePlayCards", player_nick, referee, cards, profile)
+        return self.sat_host.bridgeCall(
+            "tarotGamePlayCards", player_nick, referee, cards, profile
+        )
 
     def jsonrpc_launchRadioCollective(self, invited, room_jid=""):
         """Create a room, invite people, and start a radio collective.
@@ -789,7 +925,9 @@
         @param keys: name of data we want (list)
         @return: requested data"""
         if not C.ALLOWED_ENTITY_DATA.issuperset(keys):
-            raise exceptions.PermissionError("Trying to access unallowed data (hack attempt ?)")
+            raise exceptions.PermissionError(
+                "Trying to access unallowed data (hack attempt ?)"
+            )
         profile = session_iface.ISATSession(self.session).profile
         try:
             return self.sat_host.bridgeCall("getEntitiesData", jids, keys, profile)
@@ -803,7 +941,9 @@
         @param keys: name of data we want (list)
         @return: requested data"""
         if not C.ALLOWED_ENTITY_DATA.issuperset(keys):
-            raise exceptions.PermissionError("Trying to access unallowed data (hack attempt ?)")
+            raise exceptions.PermissionError(
+                "Trying to access unallowed data (hack attempt ?)"
+            )
         profile = session_iface.ISATSession(self.session).profile
         try:
             return self.sat_host.bridgeCall("getEntityData", jid, keys, profile)
@@ -822,7 +962,9 @@
         session_data = session_iface.ISATSession(self.session)
         profile = session_data.profile
         # profile_uuid = session_data.uuid
-        avatar = yield self.asyncBridgeCall("avatarGet", entity, cache_only, hash_only, profile)
+        avatar = yield self.asyncBridgeCall(
+            "avatarGet", entity, cache_only, hash_only, profile
+        )
         if hash_only:
             defer.returnValue(avatar)
         else:
@@ -847,18 +989,29 @@
         if category == "Connection":
             # we need to manage the followings params here, else SECURITY_LIMIT would block them
             if param == "JabberID":
-                return self.asyncBridgeCall("asyncGetParamA", param, category, attribute, profile_key=profile)
+                return self.asyncBridgeCall(
+                    "asyncGetParamA", param, category, attribute, profile_key=profile
+                )
             elif param == "autoconnect":
                 return defer.succeed(C.BOOL_TRUE)
-        d = self.asyncBridgeCall("asyncGetParamA", param, category, attribute, C.SECURITY_LIMIT, profile_key=profile)
+        d = self.asyncBridgeCall(
+            "asyncGetParamA",
+            param,
+            category,
+            attribute,
+            C.SECURITY_LIMIT,
+            profile_key=profile,
+        )
         return d
 
     def jsonrpc_setParam(self, name, value, category):
         profile = session_iface.ISATSession(self.session).profile
-        return self.sat_host.bridgeCall("setParam", name, value, category, C.SECURITY_LIMIT, profile)
+        return self.sat_host.bridgeCall(
+            "setParam", name, value, category, C.SECURITY_LIMIT, profile
+        )
 
     def jsonrpc_launchAction(self, callback_id, data):
-        #FIXME: any action can be launched, this can be a huge security issue if callback_id can be guessed
+        # FIXME: any action can be launched, this can be a huge security issue if callback_id can be guessed
         #       a security system with authorised callback_id must be implemented, similar to the one for authorised params
         profile = session_iface.ISATSession(self.session).profile
         d = self.asyncBridgeCall("launchAction", callback_id, data, profile)
@@ -876,7 +1029,9 @@
         d = self.asyncBridgeCall("getNewAccountDomain")
         return d
 
-    def jsonrpc_syntaxConvert(self, text, syntax_from=C.SYNTAX_XHTML, syntax_to=C.SYNTAX_CURRENT):
+    def jsonrpc_syntaxConvert(
+        self, text, syntax_from=C.SYNTAX_XHTML, syntax_to=C.SYNTAX_CURRENT
+    ):
         """ Convert a text between two syntaxes
         @param text: text to convert
         @param syntax_from: source syntax (e.g. "markdown")
@@ -884,7 +1039,9 @@
         @param safe: clean resulting XHTML to avoid malicious code if True (forced here)
         @return: converted text """
         profile = session_iface.ISATSession(self.session).profile
-        return self.sat_host.bridgeCall("syntaxConvert", text, syntax_from, syntax_to, True, profile)
+        return self.sat_host.bridgeCall(
+            "syntaxConvert", text, syntax_from, syntax_to, True, profile
+        )
 
     def jsonrpc_getLastResource(self, jid_s):
         """Get the last active resource of that contact."""
@@ -902,11 +1059,10 @@
         return self.sat_host.bridgeCall("skipOTR", profile)
 
     def jsonrpc_namespacesGet(self):
-        return self.sat_host.bridgeCall('namespacesGet')
+        return self.sat_host.bridgeCall("namespacesGet")
 
 
 class WaitingRequests(dict):
-
     def setRequest(self, request, profile, register_with_ext_jid=False):
         """Add the given profile to the waiting list.
 
@@ -963,18 +1119,22 @@
            - except login, every method is jsonrpc
            - user doesn't need to be authentified for explicitely listed methods, but must be for all others
         """
-        if request.postpath == ['login']:
+        if request.postpath == ["login"]:
             return self.loginOrRegister(request)
         _session = request.getSession()
         parsed = jsonrpclib.loads(request.content.read())
         method = parsed.get("method")  # pylint: disable=E1103
-        if  method not in ['getSessionMetadata', 'registerParams', 'menusGet']:
-            #if we don't call these methods, we need to be identified
+        if method not in ["getSessionMetadata", "registerParams", "menusGet"]:
+            # if we don't call these methods, we need to be identified
             profile = session_iface.ISATSession(_session).profile
             if not profile:
-                #user is not identified, we return a jsonrpc fault
-                fault = jsonrpclib.Fault(C.ERRNUM_LIBERVIA, C.NOT_ALLOWED)  # FIXME: define some standard error codes for libervia
-                return jsonrpc.JSONRPC._cbRender(self, fault, request, parsed.get('id'), parsed.get('jsonrpc'))  # pylint: disable=E1103
+                # user is not identified, we return a jsonrpc fault
+                fault = jsonrpclib.Fault(
+                    C.ERRNUM_LIBERVIA, C.NOT_ALLOWED
+                )  # FIXME: define some standard error codes for libervia
+                return jsonrpc.JSONRPC._cbRender(
+                    self, fault, request, parsed.get("id"), parsed.get("jsonrpc")
+                )  # pylint: disable=E1103
         self.request = request
         return jsonrpc.JSONRPC.render(self, request)
 
@@ -987,24 +1147,24 @@
             - a return value from self._loginAccount or self._registerNewAccount
         """
         try:
-            submit_type = request.args['submit_type'][0]
+            submit_type = request.args["submit_type"][0]
         except KeyError:
             return C.BAD_REQUEST
 
-        if submit_type == 'register':
+        if submit_type == "register":
             self._registerNewAccount(request)
             return server.NOT_DONE_YET
-        elif submit_type == 'login':
+        elif submit_type == "login":
             self._loginAccount(request)
             return server.NOT_DONE_YET
-        return Exception('Unknown submit type')
+        return Exception("Unknown submit type")
 
     @defer.inlineCallbacks
     def _registerNewAccount(self, request):
         try:
-            login = request.args['register_login'][0]
-            password = request.args['register_password'][0]
-            email = request.args['email'][0]
+            login = request.args["register_login"][0]
+            password = request.args["register_password"][0]
+            email = request.args["email"][0]
         except KeyError:
             request.write(C.BAD_REQUEST)
             request.finish()
@@ -1030,8 +1190,8 @@
         @param request: request of the register form
         """
         try:
-            login = request.args['login'][0]
-            password = request.args['login_password'][0]
+            login = request.args["login"][0]
+            password = request.args["login_password"][0]
         except KeyError:
             request.write(C.BAD_REQUEST)
             request.finish()
@@ -1041,9 +1201,11 @@
 
         try:
             status = yield self.sat_host.connect(request, login, password)
-        except (exceptions.DataError,
-                exceptions.ProfileUnknownError,
-                exceptions.PermissionError):
+        except (
+            exceptions.DataError,
+            exceptions.ProfileUnknownError,
+            exceptions.PermissionError,
+        ):
             request.write(C.PROFILE_AUTH_ERROR)
             request.finish()
             return
@@ -1084,7 +1246,9 @@
         _session = self.request.getSession()
         profile = session_iface.ISATSession(_session).profile
         if self.waiting_profiles.getRequest(profile):
-            raise jsonrpclib.Fault(1, C.ALREADY_WAITING)  # FIXME: define some standard error codes for libervia
+            raise jsonrpclib.Fault(
+                1, C.ALREADY_WAITING
+            )  # FIXME: define some standard error codes for libervia
         self.waiting_profiles.setRequest(self.request, profile)
         self.sat_host.bridgeCall("connect", profile)
         return server.NOT_DONE_YET
@@ -1121,35 +1285,54 @@
     def jsonrpc_menusGet(self):
         """Return the parameters XML for profile"""
         # XXX: we put this method in Register because we get menus before being logged
-        return self.sat_host.bridgeCall("menusGet", '', C.SECURITY_LIMIT)
+        return self.sat_host.bridgeCall("menusGet", "", C.SECURITY_LIMIT)
 
     def _getSecurityWarning(self):
         """@return: a security warning message, or None if the connection is secure"""
-        if self.request.URLPath().scheme == 'https' or not self.sat_host.options['security_warning']:
+        if (
+            self.request.URLPath().scheme == "https"
+            or not self.sat_host.options["security_warning"]
+        ):
             return None
-        text = "<p>" + D_("You are about to connect to an unsecure service.") + "</p><p>&nbsp;</p><p>"
+        text = (
+            "<p>"
+            + D_("You are about to connect to an unsecure service.")
+            + "</p><p>&nbsp;</p><p>"
+        )
 
-        if self.sat_host.options['connection_type'] == 'both':
-            new_port = (':%s' % self.sat_host.options['port_https_ext']) if self.sat_host.options['port_https_ext'] != HTTPS_PORT else ''
-            url = "https://%s" % self.request.URLPath().netloc.replace(':%s' % self.sat_host.options['port'], new_port)
-            text += D_('Please read our %(faq_prefix)ssecurity notice%(faq_suffix)s regarding HTTPS') % {'faq_prefix': '<a href="http://salut-a-toi.org/faq.html#https" target="#">', 'faq_suffix': '</a>'}
-            text += "</p><p>" + D_('and use the secure version of this website:')
-            text += '</p><p>&nbsp;</p><p align="center"><a href="%(url)s">%(url)s</a>' % {'url': url}
+        if self.sat_host.options["connection_type"] == "both":
+            new_port = (
+                (":%s" % self.sat_host.options["port_https_ext"])
+                if self.sat_host.options["port_https_ext"] != HTTPS_PORT
+                else ""
+            )
+            url = "https://%s" % self.request.URLPath().netloc.replace(
+                ":%s" % self.sat_host.options["port"], new_port
+            )
+            text += D_(
+                "Please read our %(faq_prefix)ssecurity notice%(faq_suffix)s regarding HTTPS"
+            ) % {
+                "faq_prefix": '<a href="http://salut-a-toi.org/faq.html#https" target="#">',
+                "faq_suffix": "</a>",
+            }
+            text += "</p><p>" + D_("and use the secure version of this website:")
+            text += '</p><p>&nbsp;</p><p align="center"><a href="%(url)s">%(url)s</a>' % {
+                "url": url
+            }
         else:
-            text += D_('You should ask your administrator to turn on HTTPS.')
+            text += D_("You should ask your administrator to turn on HTTPS.")
 
         return text + "</p><p>&nbsp;</p>"
 
 
 class SignalHandler(jsonrpc.JSONRPC):
-
     def __init__(self, sat_host):
         web_resource.Resource.__init__(self)
         self.register = None
         self.sat_host = sat_host
         self._last_service_prof_disconnect = time.time()
-        self.signalDeferred = {} # dict of deferred (key: profile, value: Deferred)
-                                 # which manages the long polling HTTP request with signals
+        self.signalDeferred = {}  # dict of deferred (key: profile, value: Deferred)
+        # which manages the long polling HTTP request with signals
         self.queue = {}
 
     def plugRegister(self, register):
@@ -1164,7 +1347,7 @@
             if self.queue[profile]:
                 return self.queue[profile].pop(0)
             else:
-                #the queue is empty, we delete the profile from queue
+                # the queue is empty, we delete the profile from queue
                 del self.queue[profile]
         _session.lock()  # we don't want the session to expire as long as this connection is active
 
@@ -1177,9 +1360,9 @@
                     try:
                         _session.expire()
                     except KeyError:
-                        # FIXME: happen if session is ended using login page
+                        #  FIXME: happen if session is ended using login page
                         #        when pyjamas page is also launched
-                        log.warning(u'session is already expired')
+                        log.warning(u"session is already expired")
             except IndexError:
                 log.error("Deferred result should be a tuple with fonction name first")
 
@@ -1190,6 +1373,7 @@
     def getGenericCb(self, function_name):
         """Return a generic function which send all params to signalDeferred.callback
         function must have profile as last argument"""
+
         def genericCb(*args):
             profile = args[-1]
             if not profile in self.sat_host.prof_connected:
@@ -1198,10 +1382,11 @@
             try:
                 signal_callback = self.signalDeferred[profile].callback
             except KeyError:
-                self.queue.setdefault(profile,[]).append(signal_data)
+                self.queue.setdefault(profile, []).append(signal_data)
             else:
                 signal_callback(signal_data)
                 del self.signalDeferred[profile]
+
         return genericCb
 
     def actionNewHandler(self, action_data, action_id, security_limit, profile):
@@ -1219,13 +1404,17 @@
         #        raise an exception if it's not OK
         #        and read value in sat.conf
         if security_limit >= C.SECURITY_LIMIT:
-            log.debug(u"Ignoring action  {action_id}, blocked by security limit".format(action_id=action_id))
+            log.debug(
+                u"Ignoring action  {action_id}, blocked by security limit".format(
+                    action_id=action_id
+                )
+            )
             return
         signal_data = ("actionNew", (action_data, action_id, security_limit))
         try:
             signal_callback = self.signalDeferred[profile].callback
         except KeyError:
-            self.queue.setdefault(profile,[]).append(signal_data)
+            self.queue.setdefault(profile, []).append(signal_data)
         else:
             signal_callback(signal_data)
             del self.signalDeferred[profile]
@@ -1252,20 +1441,36 @@
             # and display an error message
             disconnect_delta = time.time() - self._last_service_prof_disconnect
             if disconnect_delta < 15:
-                log.error(_(u'Service profile disconnected twice in a short time, please check connection'))
+                log.error(
+                    _(
+                        u"Service profile disconnected twice in a short time, please check connection"
+                    )
+                )
             else:
-                log.info(_(u"Service profile has been disconnected, but we need it! Reconnecting it..."))
-                d = self.sat_host.bridgeCall("connect", profile,
-                    self.sat_host.options['passphrase'],
-                    {},
+                log.info(
+                    _(
+                        u"Service profile has been disconnected, but we need it! Reconnecting it..."
                     )
-                d.addErrback(lambda failure_: log.error(_(u"Can't reconnect service profile, please check connection: {reason}").format(
-                    reason=failure_)))
+                )
+                d = self.sat_host.bridgeCall(
+                    "connect", profile, self.sat_host.options["passphrase"], {}
+                )
+                d.addErrback(
+                    lambda failure_: log.error(
+                        _(
+                            u"Can't reconnect service profile, please check connection: {reason}"
+                        ).format(reason=failure_)
+                    )
+                )
             self._last_service_prof_disconnect = time.time()
             return
 
         if not profile in self.sat_host.prof_connected:
-            log.info(_(u"'disconnected' signal received for a not connected profile ({profile})").format(profile=profile))
+            log.info(
+                _(
+                    u"'disconnected' signal received for a not connected profile ({profile})"
+                ).format(profile=profile)
+            )
             return
         self.sat_host.prof_connected.remove(profile)
         if profile in self.signalDeferred:
@@ -1284,9 +1489,13 @@
         parsed = jsonrpclib.loads(request.content.read())
         profile = session_iface.ISATSession(_session).profile
         if not profile:
-            #user is not identified, we return a jsonrpc fault
-            fault = jsonrpclib.Fault(C.ERRNUM_LIBERVIA, C.NOT_ALLOWED)  # FIXME: define some standard error codes for libervia
-            return jsonrpc.JSONRPC._cbRender(self, fault, request, parsed.get('id'), parsed.get('jsonrpc'))  # pylint: disable=E1103
+            # user is not identified, we return a jsonrpc fault
+            fault = jsonrpclib.Fault(
+                C.ERRNUM_LIBERVIA, C.NOT_ALLOWED
+            )  # FIXME: define some standard error codes for libervia
+            return jsonrpc.JSONRPC._cbRender(
+                self, fault, request, parsed.get("id"), parsed.get("jsonrpc")
+            )  # pylint: disable=E1103
         self.request = request
         return jsonrpc.JSONRPC.render(self, request)
 
@@ -1294,8 +1503,9 @@
 class UploadManager(web_resource.Resource):
     """This class manage the upload of a file
     It redirect the stream to SàT core backend"""
+
     isLeaf = True
-    NAME = 'path'  # name use by the FileUpload
+    NAME = "path"  # name use by the FileUpload
 
     def __init__(self, sat_host):
         self.sat_host = sat_host
@@ -1327,12 +1537,12 @@
         """
         filename = self._getFileName(request)
         filepath = os.path.join(self.upload_dir, filename)
-        #FIXME: the uploaded file is fully loaded in memory at form parsing time so far
+        # FIXME: the uploaded file is fully loaded in memory at form parsing time so far
         #       (see twisted.web.http.Request.requestReceived). A custom requestReceived should
         #       be written in the futur. In addition, it is not yet possible to get progression informations
         #       (see http://twistedmatrix.com/trac/ticket/288)
 
-        with open(filepath, 'w') as f:
+        with open(filepath, "w") as f:
             f.write(request.args[self.NAME][0])
 
         def finish(d):
@@ -1343,17 +1553,22 @@
             # a DBusException instance --> extract the data from the backtrace?
             request.finish()
 
-        d = JSONRPCMethodManager(self.sat_host).asyncBridgeCall(*self._fileWritten(request, filepath))
+        d = JSONRPCMethodManager(self.sat_host).asyncBridgeCall(
+            *self._fileWritten(request, filepath)
+        )
         d.addCallbacks(lambda d: finish(d), lambda failure: finish(failure))
         return server.NOT_DONE_YET
 
 
 class UploadManagerRadioCol(UploadManager):
-    NAME = 'song'
+    NAME = "song"
 
     def _getFileName(self, request):
-        extension = os.path.splitext(request.args['filename'][0])[1]
-        return "%s%s" % (str(uuid.uuid4()), extension)  # XXX: chromium doesn't seem to play song without the .ogg extension, even with audio/ogg mime-type
+        extension = os.path.splitext(request.args["filename"][0])[1]
+        return "%s%s" % (
+            str(uuid.uuid4()),
+            extension,
+        )  # XXX: chromium doesn't seem to play song without the .ogg extension, even with audio/ogg mime-type
 
     def _fileWritten(self, request, filepath):
         """Called once the file is actually written on disk
@@ -1363,11 +1578,11 @@
         to be called followed by its arguments.
         """
         profile = session_iface.ISATSession(request.getSession()).profile
-        return ("radiocolSongAdded", request.args['referee'][0], filepath, profile)
+        return ("radiocolSongAdded", request.args["referee"][0], filepath, profile)
 
 
 class UploadManagerAvatar(UploadManager):
-    NAME = 'avatar_path'
+    NAME = "avatar_path"
 
     def _getFileName(self, request):
         return str(uuid.uuid4())
@@ -1391,24 +1606,26 @@
         self.initialised = defer.Deferred()
         self.waiting_profiles = WaitingRequests()  # FIXME: should be removed
 
-        if self.options['base_url_ext']:
-            self.base_url_ext = self.options.pop('base_url_ext')
-            if self.base_url_ext[-1] != '/':
-                self.base_url_ext += '/'
+        if self.options["base_url_ext"]:
+            self.base_url_ext = self.options.pop("base_url_ext")
+            if self.base_url_ext[-1] != "/":
+                self.base_url_ext += "/"
             self.base_url_ext_data = urlparse.urlsplit(self.base_url_ext)
         else:
             self.base_url_ext = None
             # we split empty string anyway so we can do things like
             # scheme = self.base_url_ext_data.scheme or 'https'
-            self.base_url_ext_data = urlparse.urlsplit('')
+            self.base_url_ext_data = urlparse.urlsplit("")
 
-        if not self.options['port_https_ext']:
-            self.options['port_https_ext'] = self.options['port_https']
-        if self.options['data_dir'] == DATA_DIR_DEFAULT:
-            coerceDataDir(self.options['data_dir'])  # this is not done when using the default value
+        if not self.options["port_https_ext"]:
+            self.options["port_https_ext"] = self.options["port_https"]
+        if self.options["data_dir"] == DATA_DIR_DEFAULT:
+            coerceDataDir(
+                self.options["data_dir"]
+            )  # this is not done when using the default value
 
-        self.html_dir = os.path.join(self.options['data_dir'], C.HTML_DIR)
-        self.themes_dir = os.path.join(self.options['data_dir'], C.THEMES_DIR)
+        self.html_dir = os.path.join(self.options["data_dir"], C.HTML_DIR)
+        self.themes_dir = os.path.join(self.options["data_dir"], C.THEMES_DIR)
 
         self._cleanup = []
 
@@ -1436,63 +1653,100 @@
         _register = Register(self)
         _upload_radiocol = UploadManagerRadioCol(self)
         _upload_avatar = UploadManagerAvatar(self)
-        d = self.bridgeCall('namespacesGet')
+        d = self.bridgeCall("namespacesGet")
         d.addCallback(self._namespacesGetCb)
         d.addErrback(self._namespacesGetEb)
         self.signal_handler.plugRegister(_register)
         self.bridge.register_signal("connected", self.signal_handler.connected)
         self.bridge.register_signal("disconnected", self.signal_handler.disconnected)
-        #core
-        for signal_name in ['presenceUpdate', 'messageNew', 'subscribe', 'contactDeleted',
-                            'newContact', 'entityDataUpdated', 'paramUpdate']:
-            self.bridge.register_signal(signal_name, self.signal_handler.getGenericCb(signal_name))
+        # core
+        for signal_name in [
+            "presenceUpdate",
+            "messageNew",
+            "subscribe",
+            "contactDeleted",
+            "newContact",
+            "entityDataUpdated",
+            "paramUpdate",
+        ]:
+            self.bridge.register_signal(
+                signal_name, self.signal_handler.getGenericCb(signal_name)
+            )
         # XXX: actionNew is handled separately because the handler must manage security_limit
-        self.bridge.register_signal('actionNew', self.signal_handler.actionNewHandler)
-        #plugins
-        for signal_name in ['psEvent', 'mucRoomJoined', 'tarotGameStarted', 'tarotGameNew', 'tarotGameChooseContrat',
-                            'tarotGameShowCards', 'tarotGameInvalidCards', 'tarotGameCardsPlayed', 'tarotGameYourTurn', 'tarotGameScore', 'tarotGamePlayers',
-                            'radiocolStarted', 'radiocolPreload', 'radiocolPlay', 'radiocolNoUpload', 'radiocolUploadOk', 'radiocolSongRejected', 'radiocolPlayers',
-                            'mucRoomLeft', 'mucRoomUserChangedNick', 'chatStateReceived']:
-            self.bridge.register_signal(signal_name, self.signal_handler.getGenericCb(signal_name), "plugin")
-        self.media_dir = self.bridge.getConfig('', 'media_dir')
-        self.local_dir = self.bridge.getConfig('', 'local_dir')
-        self.cache_root_dir = os.path.join(
-            self.local_dir,
-            C.CACHE_DIR)
+        self.bridge.register_signal("actionNew", self.signal_handler.actionNewHandler)
+        # plugins
+        for signal_name in [
+            "psEvent",
+            "mucRoomJoined",
+            "tarotGameStarted",
+            "tarotGameNew",
+            "tarotGameChooseContrat",
+            "tarotGameShowCards",
+            "tarotGameInvalidCards",
+            "tarotGameCardsPlayed",
+            "tarotGameYourTurn",
+            "tarotGameScore",
+            "tarotGamePlayers",
+            "radiocolStarted",
+            "radiocolPreload",
+            "radiocolPlay",
+            "radiocolNoUpload",
+            "radiocolUploadOk",
+            "radiocolSongRejected",
+            "radiocolPlayers",
+            "mucRoomLeft",
+            "mucRoomUserChangedNick",
+            "chatStateReceived",
+        ]:
+            self.bridge.register_signal(
+                signal_name, self.signal_handler.getGenericCb(signal_name), "plugin"
+            )
+        self.media_dir = self.bridge.getConfig("", "media_dir")
+        self.local_dir = self.bridge.getConfig("", "local_dir")
+        self.cache_root_dir = os.path.join(self.local_dir, C.CACHE_DIR)
 
         # JSON APIs
-        self.putChild('json_signal_api', self.signal_handler)
-        self.putChild('json_api', MethodHandler(self))
-        self.putChild('register_api', _register)
+        self.putChild("json_signal_api", self.signal_handler)
+        self.putChild("json_api", MethodHandler(self))
+        self.putChild("register_api", _register)
 
         # files upload
-        self.putChild('upload_radiocol', _upload_radiocol)
-        self.putChild('upload_avatar', _upload_avatar)
+        self.putChild("upload_radiocol", _upload_radiocol)
+        self.putChild("upload_avatar", _upload_avatar)
 
         # static pages
-        self.putChild('blog_legacy', MicroBlog(self))
+        self.putChild("blog_legacy", MicroBlog(self))
         self.putChild(C.THEMES_URL, ProtectedFile(self.themes_dir))
 
         # websocket
-        if self.options['connection_type'] in ('https', 'both'):
+        if self.options["connection_type"] in ("https", "both"):
             wss = websockets.LiberviaPageWSProtocol.getResource(self, secure=True)
-            self.putChild('wss', wss)
-        if self.options['connection_type'] in ('http', 'both'):
+            self.putChild("wss", wss)
+        if self.options["connection_type"] in ("http", "both"):
             ws = websockets.LiberviaPageWSProtocol.getResource(self, secure=False)
-            self.putChild('ws', ws)
+            self.putChild("ws", ws)
 
-        # Libervia pages
+        #  Libervia pages
         LiberviaPage.importPages(self)
-        LiberviaPage.setMenu(self.options['menu_json'])
+        LiberviaPage.setMenu(self.options["menu_json"])
         ## following signal is needed for cache handling in Libervia pages
-        self.bridge.register_signal("psEventRaw", partial(LiberviaPage.onNodeEvent, self), "plugin")
-        self.bridge.register_signal("messageNew", partial(LiberviaPage.onSignal, self, "messageNew"))
+        self.bridge.register_signal(
+            "psEventRaw", partial(LiberviaPage.onNodeEvent, self), "plugin"
+        )
+        self.bridge.register_signal(
+            "messageNew", partial(LiberviaPage.onSignal, self, "messageNew")
+        )
 
-        # Progress handling
-        self.bridge.register_signal("progressStarted", partial(ProgressHandler._signal, "started"))
-        self.bridge.register_signal("progressFinished", partial(ProgressHandler._signal, "finished"))
-        self.bridge.register_signal("progressError", partial(ProgressHandler._signal, "error"))
-
+        #  Progress handling
+        self.bridge.register_signal(
+            "progressStarted", partial(ProgressHandler._signal, "started")
+        )
+        self.bridge.register_signal(
+            "progressFinished", partial(ProgressHandler._signal, "finished")
+        )
+        self.bridge.register_signal(
+            "progressError", partial(ProgressHandler._signal, "error")
+        )
 
         # media dirs
         # FIXME: get rid of dirname and "/" in C.XXX_DIR
@@ -1501,20 +1755,25 @@
         self.putChild(C.CACHE_DIR, self.cache_resource)
 
         # special
-        self.putChild('radiocol', ProtectedFile(_upload_radiocol.getTmpDir(), defaultType="audio/ogg"))  # FIXME: We cheat for PoC because we know we are on the same host, so we use directly upload dir
+        self.putChild(
+            "radiocol",
+            ProtectedFile(_upload_radiocol.getTmpDir(), defaultType="audio/ogg"),
+        )  # FIXME: We cheat for PoC because we know we are on the same host, so we use directly upload dir
         # pyjamas tests, redirected only for dev versions
-        if self.version[-1] == 'D':
-            self.putChild('test', web_util.Redirect('/libervia_test.html'))
+        if self.version[-1] == "D":
+            self.putChild("test", web_util.Redirect("/libervia_test.html"))
 
         # redirections
         root._initRedirections(self.options)
 
-        server.Request.defaultContentType = 'text/html; charset=utf-8'
-        wrapped = web_resource.EncodingResourceWrapper(root, [server.GzipEncoderFactory()])
+        server.Request.defaultContentType = "text/html; charset=utf-8"
+        wrapped = web_resource.EncodingResourceWrapper(
+            root, [server.GzipEncoderFactory()]
+        )
         self.site = server.Site(wrapped)
         self.site.sessionFactory = LiberviaSession
         self.renderer = template.Renderer(self)
-        self.putChild('templates', ProtectedFile(self.renderer.base_dir))
+        self.putChild("templates", ProtectedFile(self.renderer.base_dir))
 
     def initEb(self, failure):
         log.error(_(u"Init error: {msg}").format(msg=failure))
@@ -1522,8 +1781,10 @@
         return failure
 
     def _bridgeCb(self):
-        self.bridge.getReady(lambda: self.initialised.callback(None),
-                             lambda failure: self.initialised.errback(Exception(failure)))
+        self.bridge.getReady(
+            lambda: self.initialised.callback(None),
+            lambda failure: self.initialised.errback(Exception(failure)),
+        )
         self.initialised.addCallback(self.backendReady)
         self.initialised.addErrback(self.initEb)
 
@@ -1539,12 +1800,14 @@
     def full_version(self):
         """Return the full version of Libervia (with extra data when in development mode)"""
         version = self.version
-        if version[-1] == 'D':
+        if version[-1] == "D":
             # we are in debug version, we add extra data
             try:
                 return self._version_cache
             except AttributeError:
-                self._version_cache = u"{} ({})".format(version, utils.getRepositoryData(libervia))
+                self._version_cache = u"{} ({})".format(
+                    version, utils.getRepositoryData(libervia)
+                )
                 return self._version_cache
         else:
             return version
@@ -1567,7 +1830,11 @@
                 d.callback(args[0])
 
         def _errback(result):
-            d.errback(failure.Failure(jsonrpclib.Fault(C.ERRNUM_BRIDGE_ERRBACK, result.classname)))
+            d.errback(
+                failure.Failure(
+                    jsonrpclib.Fault(C.ERRNUM_BRIDGE_ERRBACK, result.classname)
+                )
+            )
 
         kwargs["callback"] = _callback
         kwargs["errback"] = _errback
@@ -1590,34 +1857,42 @@
         session = request.getSession()
         sat_session = session_iface.ISATSession(session)
         if sat_session.profile:
-            log.error(_(u'/!\\ Session has already a profile, this should NEVER happen!'))
+            log.error(_(u"/!\\ Session has already a profile, this should NEVER happen!"))
             raise failure.Failure(exceptions.ConflictError("Already active"))
 
         sat_session.profile = profile
         self.prof_connected.add(profile)
-        cache_dir = os.path.join(self.cache_root_dir, u'profiles', regex.pathEscape(profile))
+        cache_dir = os.path.join(
+            self.cache_root_dir, u"profiles", regex.pathEscape(profile)
+        )
         # FIXME: would be better to have a global /cache URL which redirect to profile's cache directory, without uuid
         self.cache_resource.putChild(sat_session.uuid, ProtectedFile(cache_dir))
-        log.debug(_(u"profile cache resource added from {uuid} to {path}").format(uuid=sat_session.uuid, path=cache_dir))
+        log.debug(
+            _(u"profile cache resource added from {uuid} to {path}").format(
+                uuid=sat_session.uuid, path=cache_dir
+            )
+        )
 
         def onExpire():
-            log.info(u"Session expired (profile={profile})".format(profile=profile,))
+            log.info(u"Session expired (profile={profile})".format(profile=profile))
             self.cache_resource.delEntity(sat_session.uuid)
-            log.debug(_(u"profile cache resource {uuid} deleted").format(uuid = sat_session.uuid))
+            log.debug(
+                _(u"profile cache resource {uuid} deleted").format(uuid=sat_session.uuid)
+            )
             try:
-                #We purge the queue
+                # We purge the queue
                 del self.signal_handler.queue[profile]
             except KeyError:
                 pass
-            #and now we disconnect the profile
+            # and now we disconnect the profile
             self.bridgeCall("disconnect", profile)
 
         session.notifyOnExpire(onExpire)
 
         # FIXME: those session infos should be returned by connect or isConnected
-        infos = yield self.bridgeCall('sessionInfosGet', profile)
-        sat_session.jid = jid.JID(infos['jid'])
-        sat_session.backend_started = int(infos['started'])
+        infos = yield self.bridgeCall("sessionInfosGet", profile)
+        sat_session.jid = jid.JID(infos["jid"])
+        sat_session.backend_started = int(infos["started"])
 
         state = C.PROFILE_LOGGED_EXT_JID if register_with_ext_jid else C.PROFILE_LOGGED
         defer.returnValue(state)
@@ -1645,18 +1920,20 @@
         """
 
         # XXX: all security checks must be done here, even if present in javascript
-        if login.startswith('@'):
-            raise failure.Failure(exceptions.DataError('No profile_key allowed'))
+        if login.startswith("@"):
+            raise failure.Failure(exceptions.DataError("No profile_key allowed"))
 
-        if login.startswith('guest@@') and login.count('@') == 2:
+        if login.startswith("guest@@") and login.count("@") == 2:
             log.debug("logging a guest account")
-        elif '@' in login:
-            if login.count('@') != 1:
-                raise failure.Failure(exceptions.DataError('Invalid login: {login}'.format(login=login)))
+        elif "@" in login:
+            if login.count("@") != 1:
+                raise failure.Failure(
+                    exceptions.DataError("Invalid login: {login}".format(login=login))
+                )
             try:
                 login_jid = jid.JID(login)
             except (RuntimeError, jid.InvalidFormat, AttributeError):
-                raise failure.Failure(exceptions.DataError('No profile_key allowed'))
+                raise failure.Failure(exceptions.DataError("No profile_key allowed"))
 
             # FIXME: should it be cached?
             new_account_domain = yield self.bridgeCall("getNewAccountDomain")
@@ -1672,17 +1949,29 @@
             profile = yield self.bridgeCall("profileNameGet", login)
         except Exception:  # XXX: ProfileUnknownError wouldn't work, it's encapsulated
             # FIXME: find a better way to handle bridge errors
-            if login_jid is not None and login_jid.user:  # try to create a new sat profile using the XMPP credentials
+            if (
+                login_jid is not None and login_jid.user
+            ):  # try to create a new sat profile using the XMPP credentials
                 if not self.options["allow_registration"]:
-                    log.warning(u"Trying to register JID account while registration is not allowed")
-                    raise failure.Failure(exceptions.DataError(u"JID login while registration is not allowed"))
-                profile = login # FIXME: what if there is a resource?
+                    log.warning(
+                        u"Trying to register JID account while registration is not allowed"
+                    )
+                    raise failure.Failure(
+                        exceptions.DataError(
+                            u"JID login while registration is not allowed"
+                        )
+                    )
+                profile = login  # FIXME: what if there is a resource?
                 connect_method = "asyncConnectWithXMPPCredentials"
                 register_with_ext_jid = True
-            else: # non existing username
+            else:  # non existing username
                 raise failure.Failure(exceptions.ProfileUnknownError())
         else:
-            if profile != login or (not password and profile not in self.options['empty_password_allowed_warning_dangerous_list']):
+            if profile != login or (
+                not password
+                and profile
+                not in self.options["empty_password_allowed_warning_dangerous_list"]
+            ):
                 # profiles with empty passwords are restricted to local frontends
                 raise exceptions.PermissionError
             register_with_ext_jid = False
@@ -1695,13 +1984,15 @@
             # yes, there is
             if sat_session.profile != profile:
                 # it's a different profile, we need to disconnect it
-                log.warning(_(u"{new_profile} requested login, but {old_profile} was already connected, disconnecting {old_profile}").format(
-                    old_profile = sat_session.profile,
-                    new_profile = profile))
+                log.warning(
+                    _(
+                        u"{new_profile} requested login, but {old_profile} was already connected, disconnecting {old_profile}"
+                    ).format(old_profile=sat_session.profile, new_profile=profile)
+                )
                 self.purgeSession(request)
 
         if self.waiting_profiles.getRequest(profile):
-            # FIXME: check if and when this can happen
+            #  FIXME: check if and when this can happen
             raise failure.Failure(exceptions.NotReady("Already waiting"))
 
         self.waiting_profiles.setRequest(request, profile, register_with_ext_jid)
@@ -1710,22 +2001,37 @@
         except Exception as failure_:
             fault = failure_.faultString
             self.waiting_profiles.purgeRequest(profile)
-            if fault in ('PasswordError', 'ProfileUnknownError'):
-                log.info(u"Profile {profile} doesn't exist or the submitted password is wrong".format(profile=profile))
+            if fault in ("PasswordError", "ProfileUnknownError"):
+                log.info(
+                    u"Profile {profile} doesn't exist or the submitted password is wrong".format(
+                        profile=profile
+                    )
+                )
                 raise failure.Failure(ValueError(C.PROFILE_AUTH_ERROR))
-            elif fault == 'SASLAuthError':
-                log.info(u"The XMPP password of profile {profile} is wrong".format(profile=profile))
+            elif fault == "SASLAuthError":
+                log.info(
+                    u"The XMPP password of profile {profile} is wrong".format(
+                        profile=profile
+                    )
+                )
                 raise failure.Failure(ValueError(C.XMPP_AUTH_ERROR))
-            elif fault == 'NoReply':
-                log.info(_("Did not receive a reply (the timeout expired or the connection is broken)"))
+            elif fault == "NoReply":
+                log.info(
+                    _(
+                        "Did not receive a reply (the timeout expired or the connection is broken)"
+                    )
+                )
                 raise exceptions.TimeOutError
             else:
-                log.error(u'Unmanaged fault string "{fault}" in errback for the connection of profile {profile}'.format(
-                    fault=fault, profile=profile))
+                log.error(
+                    u'Unmanaged fault string "{fault}" in errback for the connection of profile {profile}'.format(
+                        fault=fault, profile=profile
+                    )
+                )
                 raise failure.Failure(exceptions.InternalError(fault))
 
         if connected:
-            # profile is already connected in backend
+            #  profile is already connected in backend
             # do we have a corresponding session in Libervia?
             sat_session = session_iface.ISATSession(request.getSession())
             if sat_session.profile:
@@ -1733,14 +2039,21 @@
                 if sat_session.profile != profile:
                     # existing session should have been ended above
                     # so this line should never be reached
-                    log.error(_(u'session profile [{session_profile}] differs from login profile [{profile}], this should not happen!').format(
-                        session_profile = sat_session.profile,
-                        profile = profile
-                        ))
+                    log.error(
+                        _(
+                            u"session profile [{session_profile}] differs from login profile [{profile}], this should not happen!"
+                        ).format(session_profile=sat_session.profile, profile=profile)
+                    )
                     raise exceptions.InternalError("profile mismatch")
                 defer.returnValue(C.SESSION_ACTIVE)
-            log.info(_(u"profile {profile} was already connected in backend".format(profile=profile)))
-            # no, we have to create it
+            log.info(
+                _(
+                    u"profile {profile} was already connected in backend".format(
+                        profile=profile
+                    )
+                )
+            )
+            #  no, we have to create it
 
         state = yield self._logged(profile, request)
         defer.returnValue(state)
@@ -1760,12 +2073,18 @@
         @raise PermissionError: registration is now allowed in server configuration
         """
         if not self.options["allow_registration"]:
-            log.warning(_(u"Registration received while it is not allowed, hack attempt?"))
-            raise failure.Failure(exceptions.PermissionError(u"Registration is not allowed on this server"))
+            log.warning(
+                _(u"Registration received while it is not allowed, hack attempt?")
+            )
+            raise failure.Failure(
+                exceptions.PermissionError(u"Registration is not allowed on this server")
+            )
 
-        if not re.match(C.REG_LOGIN_RE, login) or \
-           not re.match(C.REG_EMAIL_RE, email, re.IGNORECASE) or \
-           len(password) < C.PASSWORD_MIN_LENGTH:
+        if (
+            not re.match(C.REG_LOGIN_RE, login)
+            or not re.match(C.REG_EMAIL_RE, email, re.IGNORECASE)
+            or len(password) < C.PASSWORD_MIN_LENGTH
+        ):
             return C.INVALID_INPUT
 
         def registered(result):
@@ -1778,8 +2097,11 @@
             elif status == "InternalError":
                 return C.INTERNAL_ERROR
             else:
-                log.error(_(u'Unknown registering error status: {status }').format(
-                    status = status))
+                log.error(
+                    _(u"Unknown registering error status: {status }").format(
+                        status=status
+                    )
+                )
                 return status
 
         d = self.bridgeCall("registerSatAccount", email, password, login)
@@ -1798,6 +2120,7 @@
 
     def startService(self):
         """Connect the profile for Libervia and start the HTTP(S) server(s)"""
+
         def eb(e):
             log.error(_(u"Connection failed: %s") % e)
             self.stop()
@@ -1807,14 +2130,22 @@
                 connected = self.bridge.isConnected(C.SERVICE_PROFILE)
             except Exception as e:
                 # we don't want the traceback
-                msg = [l for l in unicode(e).split('\n') if l][-1]
-                log.error(u"Can't check service profile ({profile}), are you sure it exists ?\n{error}".format(
-                    profile=C.SERVICE_PROFILE, error=msg))
+                msg = [l for l in unicode(e).split("\n") if l][-1]
+                log.error(
+                    u"Can't check service profile ({profile}), are you sure it exists ?\n{error}".format(
+                        profile=C.SERVICE_PROFILE, error=msg
+                    )
+                )
                 self.stop()
                 return
             if not connected:
-                self.bridge.connect(C.SERVICE_PROFILE, self.options['passphrase'],
-                                    {}, callback=self._startService, errback=eb)
+                self.bridge.connect(
+                    C.SERVICE_PROFILE,
+                    self.options["passphrase"],
+                    {},
+                    callback=self._startService,
+                    errback=eb,
+                )
             else:
                 self._startService()
 
@@ -1825,7 +2156,10 @@
     def putChild(self, path, resource):
         """Add a child to the root resource"""
         # FIXME: check that no information is leaked (c.f. https://twistedmatrix.com/documents/current/web/howto/using-twistedweb.html#request-encoders)
-        self.root.putChild(path, web_resource.EncodingResourceWrapper(resource, [server.GzipEncoderFactory()]))
+        self.root.putChild(
+            path,
+            web_resource.EncodingResourceWrapper(resource, [server.GzipEncoderFactory()]),
+        )
 
     def getExtBaseURLData(self, request):
         """Retrieve external base URL Data
@@ -1840,23 +2174,31 @@
         ext_data = self.base_url_ext_data
         url_path = request.URLPath()
         if not ext_data.scheme or not ext_data.netloc:
-            # ext_data is not specified, we check headers
-            if request.requestHeaders.hasHeader('x-forwarded-host'):
+            #  ext_data is not specified, we check headers
+            if request.requestHeaders.hasHeader("x-forwarded-host"):
                 # we are behing a proxy
                 # we fill proxy_scheme and proxy_netloc value
-                proxy_host = request.requestHeaders.getRawHeaders('x-forwarded-host')[0]
+                proxy_host = request.requestHeaders.getRawHeaders("x-forwarded-host")[0]
                 try:
-                    proxy_server = request.requestHeaders.getRawHeaders('x-forwarded-server')[0]
+                    proxy_server = request.requestHeaders.getRawHeaders(
+                        "x-forwarded-server"
+                    )[0]
                 except TypeError:
                     # no x-forwarded-server found, we use proxy_host
                     proxy_netloc = proxy_host
                 else:
                     # if the proxy host has a port, we use it with server name
-                    proxy_port = urlparse.urlsplit(u'//{}'.format(proxy_host)).port
-                    proxy_netloc = u'{}:{}'.format(proxy_server, proxy_port) if proxy_port is not None else proxy_server
-                proxy_netloc = proxy_netloc.decode('utf-8')
+                    proxy_port = urlparse.urlsplit(u"//{}".format(proxy_host)).port
+                    proxy_netloc = (
+                        u"{}:{}".format(proxy_server, proxy_port)
+                        if proxy_port is not None
+                        else proxy_server
+                    )
+                proxy_netloc = proxy_netloc.decode("utf-8")
                 try:
-                    proxy_scheme = request.requestHeaders.getRawHeaders('x-forwarded-proto')[0].decode('utf-8')
+                    proxy_scheme = request.requestHeaders.getRawHeaders(
+                        "x-forwarded-proto"
+                    )[0].decode("utf-8")
                 except TypeError:
                     proxy_scheme = None
             else:
@@ -1865,12 +2207,14 @@
             proxy_scheme, proxy_netloc = None, None
 
         return urlparse.SplitResult(
-            ext_data.scheme or proxy_scheme or url_path.scheme.decode('utf-8'),
-            ext_data.netloc or proxy_netloc or url_path.netloc.decode('utf-8'),
-            ext_data.path or u'/',
-            '', '')
+            ext_data.scheme or proxy_scheme or url_path.scheme.decode("utf-8"),
+            ext_data.netloc or proxy_netloc or url_path.netloc.decode("utf-8"),
+            ext_data.path or u"/",
+            "",
+            "",
+        )
 
-    def getExtBaseURL(self, request, path='', query='', fragment='', scheme=None):
+    def getExtBaseURL(self, request, path="", query="", fragment="", scheme=None):
         """Get external URL according to given elements
 
         external URL is the URL seen by external user
@@ -1883,11 +2227,15 @@
         @return (unicode): external URL
         """
         split_result = self.getExtBaseURLData(request)
-        return urlparse.urlunsplit((
-            split_result.scheme.decode('utf-8') if scheme is None else scheme,
-            split_result.netloc.decode('utf-8'),
-            os.path.join(split_result.path, path),
-            query, fragment))
+        return urlparse.urlunsplit(
+            (
+                split_result.scheme.decode("utf-8") if scheme is None else scheme,
+                split_result.netloc.decode("utf-8"),
+                os.path.join(split_result.path, path),
+                query,
+                fragment,
+            )
+        )
 
     def checkRedirection(self, url):
         """check is a part of the URL prefix is redirected then replace it
@@ -1896,14 +2244,14 @@
         @return (unicode): possibly redirected URL which should link to the same location
         """
         inv_redirections = self.root.inv_redirections
-        url_parts = url.strip(u'/').split(u'/')
+        url_parts = url.strip(u"/").split(u"/")
         for idx in xrange(len(url), 0, -1):
-            test_url = u'/' + u'/'.join(url_parts[:idx])
+            test_url = u"/" + u"/".join(url_parts[:idx])
             if test_url in inv_redirections:
                 rem_url = url_parts[idx:]
                 return os.path.join(
-                    u'/',
-                    u'/'.join([inv_redirections[test_url]] + rem_url))
+                    u"/", u"/".join([inv_redirections[test_url]] + rem_url)
+                )
         return url
 
     ## Sessions ##
@@ -1943,16 +2291,21 @@
         """
         sat_session = self.getSessionData(request, session_iface.ISATSession)
         if sat_session.profile is None:
-            raise exceptions.InternalError(u'profile must be set to use this method')
+            raise exceptions.InternalError(u"profile must be set to use this method")
         affiliation = sat_session.getAffiliation(service, node)
         if affiliation is not None:
             defer.returnValue(affiliation)
         else:
             try:
-                affiliations = yield self.bridgeCall('psAffiliationsGet', service.full(), node, sat_session.profile)
+                affiliations = yield self.bridgeCall(
+                    "psAffiliationsGet", service.full(), node, sat_session.profile
+                )
             except Exception as e:
-                log.warning("Can't retrieve affiliation for {service}/{node}: {reason}".format(
-                    service=service, node=node, reason=e))
+                log.warning(
+                    "Can't retrieve affiliation for {service}/{node}: {reason}".format(
+                        service=service, node=node, reason=e
+                    )
+                )
                 affiliation = u""
             else:
                 try:
@@ -1966,10 +2319,10 @@
 
     def getWebsocketURL(self, request):
         base_url_split = self.getExtBaseURLData(request)
-        if base_url_split.scheme.endswith('s'):
-            scheme = u'wss'
+        if base_url_split.scheme.endswith("s"):
+            scheme = u"wss"
         else:
-            scheme = u'ws'
+            scheme = u"ws"
 
         return self.getExtBaseURL(request, path=scheme, scheme=scheme)
 
@@ -1981,8 +2334,8 @@
     def getHTTPDate(self, timestamp=None):
         now = time.gmtime(timestamp)
         fmt_date = u"{day_name}, %d {month_name} %Y %H:%M:%S GMT".format(
-            day_name = C.HTTP_DAYS[now.tm_wday],
-            month_name = C.HTTP_MONTH[now.tm_mon-1])
+            day_name=C.HTTP_DAYS[now.tm_wday], month_name=C.HTTP_MONTH[now.tm_mon - 1]
+        )
         return time.strftime(fmt_date, now)
 
     ## TLS related methods ##
@@ -1992,15 +2345,14 @@
 
         Must be called only if TLS is activated
         """
-        if not self.options['tls_certificate']:
+        if not self.options["tls_certificate"]:
             log.error(u"a TLS certificate is needed to activate HTTPS connection")
             self.quit(1)
-        if not self.options['tls_private_key']:
-            self.options['tls_private_key'] = self.options['tls_certificate']
+        if not self.options["tls_private_key"]:
+            self.options["tls_private_key"] = self.options["tls_certificate"]
 
-
-        if not self.options['tls_private_key']:
-            self.options['tls_private_key'] = self.options['tls_certificate']
+        if not self.options["tls_private_key"]:
+            self.options["tls_private_key"] = self.options["tls_certificate"]
 
     def _loadCertificates(self, f):
         """Read a .pem file with a list of certificates
@@ -2016,9 +2368,13 @@
         while True:
             line = f.readline()
             buf.append(line)
-            if '-----END CERTIFICATE-----' in line:
-                certificates.append(OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, ''.join(buf)))
-                buf=[]
+            if "-----END CERTIFICATE-----" in line:
+                certificates.append(
+                    OpenSSL.crypto.load_certificate(
+                        OpenSSL.crypto.FILETYPE_PEM, "".join(buf)
+                    )
+                )
+                buf = []
             elif not line:
                 log.debug(u"{} certificate(s) found".format(len(certificates)))
                 return certificates
@@ -2048,24 +2404,41 @@
 
         cert_options = {}
 
-        for name, option, method in [('privateKey', 'tls_private_key', self._loadPKey),
-                                    ('certificate', 'tls_certificate', self._loadCertificate),
-                                    ('extraCertChain', 'tls_chain', self._loadCertificates)]:
+        for name, option, method in [
+            ("privateKey", "tls_private_key", self._loadPKey),
+            ("certificate", "tls_certificate", self._loadCertificate),
+            ("extraCertChain", "tls_chain", self._loadCertificates),
+        ]:
             path = self.options[option]
             if not path:
-                assert option=='tls_chain'
+                assert option == "tls_chain"
                 continue
             log.debug(u"loading {option} from {path}".format(option=option, path=path))
             try:
                 with open(path) as f:
                     cert_options[name] = method(f)
             except IOError as e:
-                log.error(u"Error while reading file {path} for option {option}: {error}".format(path=path, option=option, error=e))
+                log.error(
+                    u"Error while reading file {path} for option {option}: {error}".format(
+                        path=path, option=option, error=e
+                    )
+                )
                 self.quit(2)
             except OpenSSL.crypto.Error:
-                log.error(u"Error while parsing file {path} for option {option}, are you sure it is a valid .pem file?".format(path=path, option=option))
-                if option=='tls_private_key' and self.options['tls_certificate'] == path:
-                    log.error(u"You are using the same file for private key and public certificate, make sure that both a in {path} or use --tls_private_key option".format(path=path))
+                log.error(
+                    u"Error while parsing file {path} for option {option}, are you sure it is a valid .pem file?".format(
+                        path=path, option=option
+                    )
+                )
+                if (
+                    option == "tls_private_key"
+                    and self.options["tls_certificate"] == path
+                ):
+                    log.error(
+                        u"You are using the same file for private key and public certificate, make sure that both a in {path} or use --tls_private_key option".format(
+                            path=path
+                        )
+                    )
                 self.quit(2)
 
         return ssl.CertificateOptions(**cert_options)
@@ -2081,20 +2454,30 @@
         """
         # now that we have service profile connected, we add resource for its cache
         service_path = regex.pathEscape(C.SERVICE_PROFILE)
-        cache_dir = os.path.join(self.cache_root_dir, u'profiles', service_path)
+        cache_dir = os.path.join(self.cache_root_dir, u"profiles", service_path)
         self.cache_resource.putChild(service_path, ProtectedFile(cache_dir))
-        self.service_cache_url = u'/' + os.path.join(C.CACHE_DIR, service_path)
+        self.service_cache_url = u"/" + os.path.join(C.CACHE_DIR, service_path)
         session_iface.SATSession.service_cache_url = self.service_cache_url
 
-        if self.options['connection_type'] in ('https', 'both'):
+        if self.options["connection_type"] in ("https", "both"):
             self._TLSOptionsCheck()
             context_factory = self._getTLSContextFactory()
-            reactor.listenSSL(self.options['port_https'], self.site, context_factory)
-        if self.options['connection_type'] in ('http', 'both'):
-            if self.options['connection_type'] == 'both' and self.options['redirect_to_https']:
-                reactor.listenTCP(self.options['port'], server.Site(RedirectToHTTPS(self.options['port'], self.options['port_https_ext'])))
+            reactor.listenSSL(self.options["port_https"], self.site, context_factory)
+        if self.options["connection_type"] in ("http", "both"):
+            if (
+                self.options["connection_type"] == "both"
+                and self.options["redirect_to_https"]
+            ):
+                reactor.listenTCP(
+                    self.options["port"],
+                    server.Site(
+                        RedirectToHTTPS(
+                            self.options["port"], self.options["port_https_ext"]
+                        )
+                    ),
+                )
             else:
-                reactor.listenTCP(self.options['port'], self.site)
+                reactor.listenTCP(self.options["port"], self.site)
 
     @defer.inlineCallbacks
     def stopService(self):
@@ -2122,7 +2505,6 @@
 
 
 class RedirectToHTTPS(web_resource.Resource):
-
     def __init__(self, old_port, new_port):
         web_resource.Resource.__init__(self)
         self.isLeaf = True
@@ -2130,10 +2512,14 @@
         self.new_port = new_port
 
     def render(self, request):
-        netloc = request.URLPath().netloc.replace(':%s' % self.old_port, ':%s' % self.new_port)
+        netloc = request.URLPath().netloc.replace(
+            ":%s" % self.old_port, ":%s" % self.new_port
+        )
         url = "https://" + netloc + request.uri
         return web_util.redirectTo(url, request)
 
 
 registerAdapter(session_iface.SATSession, server.Session, session_iface.ISATSession)
-registerAdapter(session_iface.SATGuestSession, server.Session, session_iface.ISATGuestSession)
+registerAdapter(
+    session_iface.SATGuestSession, server.Session, session_iface.ISATGuestSession
+)