diff sat/core/xmpp.py @ 2624:56f94936df1e

code style reformatting using black
author Goffi <goffi@goffi.org>
date Wed, 27 Jun 2018 20:14:46 +0200
parents 93d64ce7a429
children 189e38fb11ff
line wrap: on
line diff
--- a/sat/core/xmpp.py	Wed Jun 27 07:51:29 2018 +0200
+++ b/sat/core/xmpp.py	Wed Jun 27 20:14:46 2018 +0200
@@ -31,6 +31,7 @@
 from wokkel import component
 from wokkel import delay
 from sat.core.log import getLogger
+
 log = getLogger(__name__)
 from sat.core import exceptions
 from zope.interface import implements
@@ -53,10 +54,10 @@
         self.profile = profile
         self.host_app = host_app
         self.cache = cache.Cache(host_app, profile)
-        self._mess_id_uid = {} # map from message id to uid used in history. Key: (full_jid,message_id) Value: uid
+        self._mess_id_uid = {}  # map from message id to uid used in history. Key: (full_jid,message_id) Value: uid
         self.conn_deferred = defer.Deferred()
         self._progress_cb = {}  # callback called when a progress is requested (key = progress id)
-        self.actions = {} # used to keep track of actions for retrieval (key = action_id)
+        self.actions = {}  # used to keep track of actions for retrieval (key = action_id)
 
     ## initialisation ##
 
@@ -117,24 +118,37 @@
         #        but client should not be deleted except if session is finished (independently of connection/deconnection
         #
         try:
-            port = int(host.memory.getParamA(C.FORCE_PORT_PARAM, "Connection", profile_key=profile))
+            port = int(
+                host.memory.getParamA(
+                    C.FORCE_PORT_PARAM, "Connection", profile_key=profile
+                )
+            )
         except ValueError:
             log.debug(_("Can't parse port value, using default value"))
-            port = None  # will use default value 5222 or be retrieved from a DNS SRV record
+            port = (
+                None
+            )  # will use default value 5222 or be retrieved from a DNS SRV record
 
-        password = yield host.memory.asyncGetParamA("Password", "Connection", profile_key=profile)
-        entity = host.profiles[profile] = cls(host, profile,
+        password = yield host.memory.asyncGetParamA(
+            "Password", "Connection", profile_key=profile
+        )
+        entity = host.profiles[profile] = cls(
+            host,
+            profile,
             jid.JID(host.memory.getParamA("JabberID", "Connection", profile_key=profile)),
-            password, host.memory.getParamA(C.FORCE_SERVER_PARAM, "Connection", profile_key=profile) or None,
-            port, max_retries)
+            password,
+            host.memory.getParamA(C.FORCE_SERVER_PARAM, "Connection", profile_key=profile)
+            or None,
+            port,
+            max_retries,
+        )
 
         entity._createSubProtocols()
 
         entity.fallBack = SatFallbackHandler(host)
         entity.fallBack.setHandlerParent(entity)
 
-        entity.versionHandler = SatVersionHandler(C.APP_NAME_FULL,
-                                                  host.full_version)
+        entity.versionHandler = SatVersionHandler(C.APP_NAME_FULL, host.full_version)
         entity.versionHandler.setHandlerParent(entity)
 
         entity.identityHandler = SatIdentityHandler()
@@ -162,10 +176,17 @@
                 log.error(_(u"Plugins initialisation error"))
                 for idx, (success, result) in enumerate(results):
                     if not success:
-                        log.error(u"error (plugin %(name)s): %(failure)s" %
-                                  {'name': plugin_conn_cb[idx][0]._info['import_name'], 'failure': result})
+                        log.error(
+                            u"error (plugin %(name)s): %(failure)s"
+                            % {
+                                "name": plugin_conn_cb[idx][0]._info["import_name"],
+                                "failure": result,
+                            }
+                        )
 
-        yield list_d.addCallback(logPluginResults) # FIXME: we should have a timeout here, and a way to know if a plugin freeze
+        yield list_d.addCallback(
+            logPluginResults
+        )  # FIXME: we should have a timeout here, and a way to know if a plugin freeze
         # TODO: mesure launch time of each plugin
 
     def getConnectionDeferred(self):
@@ -190,9 +211,13 @@
         self._connected.addCallback(self._disconnectionCb)
         self._connected.addErrback(self._disconnectionEb)
 
-        log.info(_(u"********** [{profile}] CONNECTED **********").format(profile=self.profile))
+        log.info(
+            _(u"********** [{profile}] CONNECTED **********").format(profile=self.profile)
+        )
         self.streamInitialized()
-        self.host_app.bridge.connected(self.profile, unicode(self.jid))  # we send the signal to the clients
+        self.host_app.bridge.connected(
+            self.profile, unicode(self.jid)
+        )  # we send the signal to the clients
 
     def _finish_connection(self, dummy):
         self.conn_deferred.callback(None)
@@ -200,7 +225,9 @@
     def streamInitialized(self):
         """Called after _authd"""
         log.debug(_(u"XML stream is initialized"))
-        self.keep_alife = task.LoopingCall(self.xmlstream.send, " ")  # Needed to avoid disconnection (specially with openfire)
+        self.keep_alife = task.LoopingCall(
+            self.xmlstream.send, " "
+        )  # Needed to avoid disconnection (specially with openfire)
         self.keep_alife.start(C.XMPP_KEEP_ALIFE)
 
         self.disco = SatDiscoProtocol(self)
@@ -215,7 +242,12 @@
         disco_d.addCallback(self._finish_connection)
 
     def initializationFailed(self, reason):
-        log.error(_(u"ERROR: XMPP connection failed for profile '%(profile)s': %(reason)s" % {'profile': self.profile, 'reason': reason}))
+        log.error(
+            _(
+                u"ERROR: XMPP connection failed for profile '%(profile)s': %(reason)s"
+                % {"profile": self.profile, "reason": reason}
+            )
+        )
         self.conn_deferred.errback(reason.value)
         try:
             super(SatXMPPEntity, self).initializationFailed(reason)
@@ -231,14 +263,24 @@
         except AttributeError:
             log.debug(_("No keep_alife"))
         if self._connected is not None:
-            self.host_app.bridge.disconnected(self.profile)  # we send the signal to the clients
+            self.host_app.bridge.disconnected(
+                self.profile
+            )  # we send the signal to the clients
             self._connected.callback(None)
-            self.host_app.purgeEntity(self.profile)  # and we remove references to this client
-            log.info(_(u"********** [{profile}] DISCONNECTED **********").format(profile=self.profile))
+            self.host_app.purgeEntity(
+                self.profile
+            )  # and we remove references to this client
+            log.info(
+                _(u"********** [{profile}] DISCONNECTED **********").format(
+                    profile=self.profile
+                )
+            )
         if not self.conn_deferred.called:
             # FIXME: real error is not gotten here (e.g. if jid is not know by Prosody,
             #        we should have the real error)
-            self.conn_deferred.errback(error.StreamError(u"Server unexpectedly closed the connection"))
+            self.conn_deferred.errback(
+                error.StreamError(u"Server unexpectedly closed the connection")
+            )
 
     @defer.inlineCallbacks
     def _cleanConnection(self, dummy):
@@ -265,7 +307,7 @@
 
     ## sending ##
 
-    def IQ(self, type_=u'set', timeout=60):
+    def IQ(self, type_=u"set", timeout=60):
         """shortcut to create an IQ element managing deferred
 
         @param type_(unicode): IQ type ('set' or 'get')
@@ -301,26 +343,28 @@
                 - extra
         @return (dict) message data
         """
-        data['xml'] = message_elt = domish.Element((None, 'message'))
+        data["xml"] = message_elt = domish.Element((None, "message"))
         message_elt["to"] = data["to"].full()
-        message_elt["from"] = data['from'].full()
+        message_elt["from"] = data["from"].full()
         message_elt["type"] = data["type"]
-        if data['uid']: # key must be present but can be set to ''
-                        # by a plugin to avoid id on purpose
-            message_elt['id'] = data['uid']
+        if data["uid"]:  # key must be present but can be set to ''
+            # by a plugin to avoid id on purpose
+            message_elt["id"] = data["uid"]
         for lang, subject in data["subject"].iteritems():
             subject_elt = message_elt.addElement("subject", content=subject)
             if lang:
-                subject_elt[(C.NS_XML, 'lang')] = lang
+                subject_elt[(C.NS_XML, "lang")] = lang
         for lang, message in data["message"].iteritems():
             body_elt = message_elt.addElement("body", content=message)
             if lang:
-                body_elt[(C.NS_XML, 'lang')] = lang
+                body_elt[(C.NS_XML, "lang")] = lang
         try:
-            thread = data['extra']['thread']
+            thread = data["extra"]["thread"]
         except KeyError:
-            if 'thread_parent' in data['extra']:
-                raise exceptions.InternalError(u"thread_parent found while there is not associated thread")
+            if "thread_parent" in data["extra"]:
+                raise exceptions.InternalError(
+                    u"thread_parent found while there is not associated thread"
+                )
         else:
             thread_elt = message_elt.addElement("thread", content=thread)
             try:
@@ -336,7 +380,16 @@
         """
         raise NotImplementedError
 
-    def sendMessage(self, to_jid, message, subject=None, mess_type='auto', extra=None, uid=None, no_trigger=False):
+    def sendMessage(
+        self,
+        to_jid,
+        message,
+        subject=None,
+        mess_type="auto",
+        extra=None,
+        uid=None,
+        no_trigger=False,
+    ):
         """Send a message to an entity
 
         @param to_jid(jid.JID): destinee of the message
@@ -371,18 +424,26 @@
             "extra": extra,
             "timestamp": time.time(),
         }
-        pre_xml_treatments = defer.Deferred() # XXX: plugin can add their pre XML treatments to this deferred
-        post_xml_treatments = defer.Deferred() # XXX: plugin can add their post XML treatments to this deferred
+        pre_xml_treatments = (
+            defer.Deferred()
+        )  # XXX: plugin can add their pre XML treatments to this deferred
+        post_xml_treatments = (
+            defer.Deferred()
+        )  # XXX: plugin can add their post XML treatments to this deferred
 
         if data["type"] == C.MESS_TYPE_AUTO:
             # we try to guess the type
             if data["subject"]:
                 data["type"] = C.MESS_TYPE_NORMAL
-            elif not data["to"].resource:  # if to JID has a resource, the type is not 'groupchat'
+            elif not data[
+                "to"
+            ].resource:  # if to JID has a resource, the type is not 'groupchat'
                 # we may have a groupchat message, we check if the we know this jid
                 try:
-                    entity_type = self.host_app.memory.getEntityData(data["to"], ['type'], self.profile)["type"]
-                    #FIXME: should entity_type manage resources ?
+                    entity_type = self.host_app.memory.getEntityData(
+                        data["to"], ["type"], self.profile
+                    )["type"]
+                    # FIXME: should entity_type manage resources ?
                 except (exceptions.UnknownEntityError, KeyError):
                     entity_type = "contact"
 
@@ -397,19 +458,33 @@
         # FIXME: send_only is used by libervia's OTR plugin to avoid
         #        the triggers from frontend, and no_trigger do the same
         #        thing internally, this could be unified
-        send_only = data['extra'].get('send_only', False)
+        send_only = data["extra"].get("send_only", False)
 
         if not no_trigger and not send_only:
-            if not self.host_app.trigger.point("sendMessage" + self.trigger_suffix, self, data, pre_xml_treatments, post_xml_treatments):
+            if not self.host_app.trigger.point(
+                "sendMessage" + self.trigger_suffix,
+                self,
+                data,
+                pre_xml_treatments,
+                post_xml_treatments,
+            ):
                 return defer.succeed(None)
 
-        log.debug(_(u"Sending message (type {type}, to {to})").format(type=data["type"], to=to_jid.full()))
+        log.debug(
+            _(u"Sending message (type {type}, to {to})").format(
+                type=data["type"], to=to_jid.full()
+            )
+        )
 
         pre_xml_treatments.addCallback(lambda dummy: self.generateMessageXML(data))
         pre_xml_treatments.chainDeferred(post_xml_treatments)
         post_xml_treatments.addCallback(self.sendMessageData)
         if send_only:
-            log.debug(_("Triggers, storage and echo have been inhibited by the 'send_only' parameter"))
+            log.debug(
+                _(
+                    "Triggers, storage and echo have been inhibited by the 'send_only' parameter"
+                )
+            )
         else:
             self.addPostXmlCallbacks(post_xml_treatments)
             post_xml_treatments.addErrback(self._cancelErrorTrap)
@@ -430,10 +505,12 @@
         if data[u"type"] != C.MESS_TYPE_GROUPCHAT:
             # we don't add groupchat message to history, as we get them back
             # and they will be added then
-            if data[u'message'] or data[u'subject']: # we need a message to store
+            if data[u"message"] or data[u"subject"]:  # we need a message to store
                 self.host_app.memory.addToHistory(self, data)
             else:
-               log.warning(u"No message found") # empty body should be managed by plugins before this point
+                log.warning(
+                    u"No message found"
+                )  # empty body should be managed by plugins before this point
         return data
 
     def messageSendToBridge(self, data):
@@ -445,11 +522,23 @@
         if data[u"type"] != C.MESS_TYPE_GROUPCHAT:
             # we don't send groupchat message to bridge, as we get them back
             # and they will be added the
-            if data[u'message'] or data[u'subject']: # we need a message to send something
+            if (
+                data[u"message"] or data[u"subject"]
+            ):  # we need a message to send something
                 # We send back the message, so all frontends are aware of it
-                self.host_app.bridge.messageNew(data[u'uid'], data[u'timestamp'], data[u'from'].full(), data[u'to'].full(), data[u'message'], data[u'subject'], data[u'type'], data[u'extra'], profile=self.profile)
+                self.host_app.bridge.messageNew(
+                    data[u"uid"],
+                    data[u"timestamp"],
+                    data[u"from"].full(),
+                    data[u"to"].full(),
+                    data[u"message"],
+                    data[u"subject"],
+                    data[u"type"],
+                    data[u"extra"],
+                    profile=self.profile,
+                )
             else:
-               log.warning(_(u"No message found"))
+                log.warning(_(u"No message found"))
         return data
 
 
@@ -458,7 +547,16 @@
     trigger_suffix = ""
     is_component = False
 
-    def __init__(self, host_app, profile, user_jid, password, host=None, port=C.XMPP_C2S_PORT, max_retries=C.XMPP_MAX_RETRIES):
+    def __init__(
+        self,
+        host_app,
+        profile,
+        user_jid,
+        password,
+        host=None,
+        port=C.XMPP_C2S_PORT,
+        max_retries=C.XMPP_MAX_RETRIES,
+    ):
         # XXX: DNS SRV records are checked when the host is not specified.
         # If no SRV record is found, the host is directly extracted from the JID.
         self.started = time.time()
@@ -482,25 +580,30 @@
             if isinstance(host_data, basestring):
                 host = host_data
             elif isinstance(host_data, dict):
-                if u'host' in host_data:
-                    host = host_data[u'host']
-                if u'port' in host_data:
-                    port = host_data[u'port']
+                if u"host" in host_data:
+                    host = host_data[u"host"]
+                if u"port" in host_data:
+                    port = host_data[u"port"]
             else:
-                log.warning(_(u"invalid data used for host: {data}").format(data=host_data))
+                log.warning(
+                    _(u"invalid data used for host: {data}").format(data=host_data)
+                )
                 host_data = None
             if host_data is not None:
-                log.info(u"using {host}:{port} for host {host_ori} as requested in config".format(
-                    host_ori = user_jid.host,
-                    host = host,
-                    port = port))
+                log.info(
+                    u"using {host}:{port} for host {host_ori} as requested in config".format(
+                        host_ori=user_jid.host, host=host, port=port
+                    )
+                )
 
-        wokkel_client.XMPPClient.__init__(self, user_jid, password, host or None, port or C.XMPP_C2S_PORT)
+        wokkel_client.XMPPClient.__init__(
+            self, user_jid, password, host or None, port or C.XMPP_C2S_PORT
+        )
         SatXMPPEntity.__init__(self, host_app, profile, max_retries)
 
     def _getPluginsList(self):
         for p in self.host_app.plugins.itervalues():
-            if C.PLUG_MODE_CLIENT in p._info[u'modes']:
+            if C.PLUG_MODE_CLIENT in p._info[u"modes"]:
                 yield p
 
     def _createSubProtocols(self):
@@ -531,9 +634,9 @@
         #      (out of band transmission for instance).
         #      e2e should have a priority of 0 here, and out of band transmission
         #      a lower priority
-        # FIXME: trigger not used yet, can be uncommented when e2e full stanza encryption is implemented
-        # if not self.host_app.trigger.point("send", self, obj):
-        #     return
+        #  FIXME: trigger not used yet, can be uncommented when e2e full stanza encryption is implemented
+        #  if not self.host_app.trigger.point("send", self, obj):
+        #      return
         super(SatXMPPClient, self).send(obj)
 
     def sendMessageData(self, mess_data):
@@ -549,7 +652,7 @@
         #      This is intented for e2e encryption which doesn't do full stanza encryption (e.g. OTR)
         #      This trigger point can't cancel the method
         self.host_app.trigger.point("sendMessageData", self, mess_data)
-        self.send(mess_data[u'xml'])
+        self.send(mess_data[u"xml"])
         return mess_data
 
     def feedback(self, to_jid, message):
@@ -560,15 +663,17 @@
         @param to_jid(jid.JID): destinee jid
         @param message(unicode): message to send to frontends
         """
-        self.host_app.bridge.messageNew(uid=unicode(uuid.uuid4()),
-                                        timestamp=time.time(),
-                                        from_jid=self.jid.full(),
-                                        to_jid=to_jid.full(),
-                                        message={u'': message},
-                                        subject={},
-                                        mess_type=C.MESS_TYPE_INFO,
-                                        extra={},
-                                        profile=self.profile)
+        self.host_app.bridge.messageNew(
+            uid=unicode(uuid.uuid4()),
+            timestamp=time.time(),
+            from_jid=self.jid.full(),
+            to_jid=to_jid.full(),
+            message={u"": message},
+            subject={},
+            mess_type=C.MESS_TYPE_INFO,
+            extra={},
+            profile=self.profile,
+        )
 
     def _finish_connection(self, dummy):
         self.roster.requestRoster()
@@ -583,12 +688,26 @@
     An entry point plugin is launched after component is connected.
     Component need to instantiate MessageProtocol itself
     """
+
     implements(iwokkel.IDisco)
-    trigger_suffix = "Component"  # used for to distinguish some trigger points set in SatXMPPEntity
+    trigger_suffix = (
+        "Component"
+    )  # used for to distinguish some trigger points set in SatXMPPEntity
     is_component = True
-    sendHistory = False  # XXX: set to True from entry plugin to keep messages in history for received messages
+    sendHistory = (
+        False
+    )  # XXX: set to True from entry plugin to keep messages in history for received messages
 
-    def __init__(self, host_app, profile, component_jid, password, host=None, port=None, max_retries=C.XMPP_MAX_RETRIES):
+    def __init__(
+        self,
+        host_app,
+        profile,
+        component_jid,
+        password,
+        host=None,
+        port=None,
+        max_retries=C.XMPP_MAX_RETRIES,
+    ):
         self.started = time.time()
         if port is None:
             port = C.XMPP_COMPONENT_PORT
@@ -598,15 +717,18 @@
         try:
             self.entry_plugin = host_app.plugins[entry_point]
         except KeyError:
-            raise exceptions.NotFound(_(u"The requested entry point ({entry_point}) is not available").format(
-                entry_point = entry_point))
+            raise exceptions.NotFound(
+                _(u"The requested entry point ({entry_point}) is not available").format(
+                    entry_point=entry_point
+                )
+            )
 
         self.identities = [disco.DiscoIdentity(u"component", u"generic", C.APP_NAME)]
         # jid is set automatically on bind by Twisted for Client, but not for Component
         self.jid = component_jid
         if host is None:
             try:
-                host = component_jid.host.split(u'.', 1)[1]
+                host = component_jid.host.split(u".", 1)[1]
             except IndexError:
                 raise ValueError(u"Can't guess host from jid, please specify a host")
         # XXX: component.Component expect unicode jid, while Client expect jid.JID.
@@ -628,14 +750,18 @@
         @raise InternalError: one of the plugin is not handling components
         @raise KeyError: one plugin should be present in self.host_app.plugins but it is not
         """
-        if C.PLUG_MODE_COMPONENT not in current._info[u'modes']:
+        if C.PLUG_MODE_COMPONENT not in current._info[u"modes"]:
             if not required:
                 return
             else:
-                log.error(_(u"Plugin {current_name} is needed for {entry_name}, but it doesn't handle component mode").format(
-                    current_name = current._info[u'import_name'],
-                    entry_name = self.entry_plugin._info[u'import_name']
-                    ))
+                log.error(
+                    _(
+                        u"Plugin {current_name} is needed for {entry_name}, but it doesn't handle component mode"
+                    ).format(
+                        current_name=current._info[u"import_name"],
+                        entry_name=self.entry_plugin._info[u"import_name"],
+                    )
+                )
                 raise exceptions.InternalError(_(u"invalid plugin mode"))
 
         for import_name in current._info.get(C.PI_DEPENDENCIES, []):
@@ -651,7 +777,7 @@
                 dep = self.host_app.plugins[import_name]
             except KeyError:
                 continue
-            self._buildDependencies(dep, plugins, required = False)
+            self._buildDependencies(dep, plugins, required=False)
 
         if current not in plugins:
             # current can be required for several plugins and so
@@ -680,7 +806,6 @@
 
 
 class SatMessageProtocol(xmppim.MessageProtocol):
-
     def __init__(self, host):
         xmppim.MessageProtocol.__init__(self)
         self.host = host
@@ -697,52 +822,60 @@
         message = {}
         subject = {}
         extra = {}
-        data = {"from": jid.JID(message_elt['from']),
-                "to": jid.JID(message_elt['to']),
-                "uid": message_elt.getAttribute('uid', unicode(uuid.uuid4())), # XXX: uid is not a standard attribute but may be added by plugins
-                "message": message,
-                "subject": subject,
-                "type": message_elt.getAttribute('type', 'normal'),
-                "extra": extra}
+        data = {
+            "from": jid.JID(message_elt["from"]),
+            "to": jid.JID(message_elt["to"]),
+            "uid": message_elt.getAttribute(
+                "uid", unicode(uuid.uuid4())
+            ),  # XXX: uid is not a standard attribute but may be added by plugins
+            "message": message,
+            "subject": subject,
+            "type": message_elt.getAttribute("type", "normal"),
+            "extra": extra,
+        }
 
         if client is not None:
             try:
-                data['stanza_id'] = message_elt['id']
+                data["stanza_id"] = message_elt["id"]
             except KeyError:
                 pass
             else:
-                client._mess_id_uid[(data['from'], data['stanza_id'])] = data['uid']
+                client._mess_id_uid[(data["from"], data["stanza_id"])] = data["uid"]
 
         # message
-        for e in message_elt.elements(C.NS_CLIENT, 'body'):
-            message[e.getAttribute((C.NS_XML,'lang'),'')] = unicode(e)
+        for e in message_elt.elements(C.NS_CLIENT, "body"):
+            message[e.getAttribute((C.NS_XML, "lang"), "")] = unicode(e)
 
         # subject
-        for e in message_elt.elements(C.NS_CLIENT, 'subject'):
-            subject[e.getAttribute((C.NS_XML, 'lang'),'')] = unicode(e)
+        for e in message_elt.elements(C.NS_CLIENT, "subject"):
+            subject[e.getAttribute((C.NS_XML, "lang"), "")] = unicode(e)
 
         # delay and timestamp
         try:
-            delay_elt = message_elt.elements(delay.NS_DELAY, 'delay').next()
+            delay_elt = message_elt.elements(delay.NS_DELAY, "delay").next()
         except StopIteration:
-            data['timestamp'] = time.time()
+            data["timestamp"] = time.time()
         else:
             parsed_delay = delay.Delay.fromElement(delay_elt)
-            data['timestamp'] = calendar.timegm(parsed_delay.stamp.utctimetuple())
-            data['received_timestamp'] = unicode(time.time())
+            data["timestamp"] = calendar.timegm(parsed_delay.stamp.utctimetuple())
+            data["received_timestamp"] = unicode(time.time())
             if parsed_delay.sender:
-                data['delay_sender'] = parsed_delay.sender.full()
+                data["delay_sender"] = parsed_delay.sender.full()
         return data
 
     def onMessage(self, message_elt):
         # TODO: handle threads
         client = self.parent
-        if not 'from' in message_elt.attributes:
-            message_elt['from'] = client.jid.host
-        log.debug(_(u"got message from: {from_}").format(from_=message_elt['from']))
-        post_treat = defer.Deferred() # XXX: plugin can add their treatments to this deferred
+        if not "from" in message_elt.attributes:
+            message_elt["from"] = client.jid.host
+        log.debug(_(u"got message from: {from_}").format(from_=message_elt["from"]))
+        post_treat = (
+            defer.Deferred()
+        )  # XXX: plugin can add their treatments to this deferred
 
-        if not self.host.trigger.point("MessageReceived", client, message_elt, post_treat):
+        if not self.host.trigger.point(
+            "MessageReceived", client, message_elt, post_treat
+        ):
             return
 
         data = self.parseMessage(message_elt, client)
@@ -754,25 +887,35 @@
         post_treat.callback(data)
 
     def skipEmptyMessage(self, data):
-        if not data['message'] and not data['extra'] and not data['subject']:
+        if not data["message"] and not data["extra"] and not data["subject"]:
             raise failure.Failure(exceptions.CancelError("Cancelled empty message"))
         return data
 
     def addToHistory(self, data, client):
-        if data.pop(u'history', None) == C.HISTORY_SKIP:
-            log.info(u'history is skipped as requested')
-            data[u'extra'][u'history'] = C.HISTORY_SKIP
+        if data.pop(u"history", None) == C.HISTORY_SKIP:
+            log.info(u"history is skipped as requested")
+            data[u"extra"][u"history"] = C.HISTORY_SKIP
         else:
             return self.host.memory.addToHistory(client, data)
 
     def bridgeSignal(self, dummy, client, data):
         try:
-            data['extra']['received_timestamp'] = data['received_timestamp']
-            data['extra']['delay_sender'] = data['delay_sender']
+            data["extra"]["received_timestamp"] = data["received_timestamp"]
+            data["extra"]["delay_sender"] = data["delay_sender"]
         except KeyError:
             pass
         if data is not None:
-            self.host.bridge.messageNew(data['uid'], data['timestamp'], data['from'].full(), data['to'].full(), data['message'], data['subject'], data['type'], data['extra'], profile=client.profile)
+            self.host.bridge.messageNew(
+                data["uid"],
+                data["timestamp"],
+                data["from"].full(),
+                data["to"].full(),
+                data["message"],
+                data["subject"],
+                data["type"],
+                data["extra"],
+                profile=client.profile,
+            )
         return data
 
     def cancelErrorTrap(self, failure_):
@@ -781,26 +924,29 @@
 
 
 class SatRosterProtocol(xmppim.RosterClientProtocol):
-
     def __init__(self, host):
         xmppim.RosterClientProtocol.__init__(self)
         self.host = host
-        self.got_roster = defer.Deferred() # called when roster is received and ready
-        #XXX: the two following dicts keep a local copy of the roster
+        self.got_roster = defer.Deferred()  # called when roster is received and ready
+        # XXX: the two following dicts keep a local copy of the roster
         self._groups = {}  # map from groups to jids: key=group value=set of jids
         self._jids = None  # map from jids to RosterItem: key=jid value=RosterItem
 
     def rosterCb(self, roster):
-        assert roster is not None # FIXME: must be managed with roster versioning
+        assert roster is not None  # FIXME: must be managed with roster versioning
         self._groups.clear()
         self._jids = roster
         for item in roster.itervalues():
             if not item.subscriptionTo and not item.subscriptionFrom and not item.ask:
-                #XXX: current behaviour: we don't want contact in our roster list
+                # XXX: current behaviour: we don't want contact in our roster list
                 # if there is no presence subscription
                 # may change in the future
-                log.info(u"Removing contact {} from roster because there is no presence subscription".format(item.jid))
-                self.removeItem(item.entity) # FIXME: to be checked
+                log.info(
+                    u"Removing contact {} from roster because there is no presence subscription".format(
+                        item.jid
+                    )
+                )
+                self.removeItem(item.entity)  # FIXME: to be checked
             else:
                 self._registerItem(item)
 
@@ -812,10 +958,16 @@
         """
         log.debug(u"registering item: {}".format(item.entity.full()))
         if item.entity.resource:
-            log.warning(u"Received a roster item with a resource, this is not common but not restricted by RFC 6121, this case may be not well tested.")
+            log.warning(
+                u"Received a roster item with a resource, this is not common but not restricted by RFC 6121, this case may be not well tested."
+            )
         if not item.subscriptionTo:
             if not item.subscriptionFrom:
-                log.info(_(u"There's no subscription between you and [{}]!").format(item.entity.full()))
+                log.info(
+                    _(u"There's no subscription between you and [{}]!").format(
+                        item.entity.full()
+                    )
+                )
             else:
                 log.info(_(u"You are not subscribed to [{}]!").format(item.entity.full()))
         if not item.subscriptionFrom:
@@ -843,16 +995,17 @@
         @param item: RosterItem
         @return: dictionary of attributes
         """
-        item_attr = {'to': unicode(item.subscriptionTo),
-                     'from': unicode(item.subscriptionFrom),
-                     'ask': unicode(item.ask)
-                     }
+        item_attr = {
+            "to": unicode(item.subscriptionTo),
+            "from": unicode(item.subscriptionFrom),
+            "ask": unicode(item.ask),
+        }
         if item.name:
-            item_attr['name'] = item.name
+            item_attr["name"] = item.name
         return item_attr
 
     def setReceived(self, request):
-        #TODO: implement roster versioning (cf RFC 6121 §2.6)
+        # TODO: implement roster versioning (cf RFC 6121 §2.6)
         item = request.item
         try:  # update the cache for the groups the contact has been removed from
             left_groups = set(self._jids[item.entity].groups).difference(item.groups)
@@ -865,7 +1018,9 @@
             pass  # no previous item registration (or it's been cleared)
         self._jids[item.entity] = item
         self._registerItem(item)
-        self.host.bridge.newContact(item.entity.full(), self.getAttributes(item), item.groups, self.parent.profile)
+        self.host.bridge.newContact(
+            item.entity.full(), self.getAttributes(item), item.groups, self.parent.profile
+        )
 
     def removeReceived(self, request):
         entity = request.item.entity
@@ -875,7 +1030,11 @@
         try:
             item = self._jids.pop(entity)
         except KeyError:
-            log.error(u"Received a roster remove event for an item not in cache ({})".format(entity))
+            log.error(
+                u"Received a roster remove event for an item not in cache ({})".format(
+                    entity
+                )
+            )
             return
         for group in item.groups:
             try:
@@ -884,8 +1043,10 @@
                 if not jids_set:
                     del self._groups[group]
             except KeyError:
-                log.warning(u"there is no cache for the group [%(group)s] of the removed roster item [%(jid)s]" %
-                            {"group": group, "jid": entity})
+                log.warning(
+                    u"there is no cache for the group [%(group)s] of the removed roster item [%(jid)s]"
+                    % {"group": group, "jid": entity}
+                )
 
         # then we send the bridge signal
         self.host.bridge.contactDeleted(entity.full(), self.parent.profile)
@@ -939,7 +1100,7 @@
         @return (set(jid.JID)): set of selected jids
         """
         if type_ == C.ALL and groups is not None:
-            raise ValueError('groups must not be set for {} type'.format(C.ALL))
+            raise ValueError("groups must not be set for {} type".format(C.ALL))
 
         if type_ == C.ALL:
             return set(self.getJids())
@@ -949,7 +1110,7 @@
                 jids.update(self.getJidsFromGroup(group))
             return jids
         else:
-            raise ValueError(u'Unexpected type_ {}'.format(type_))
+            raise ValueError(u"Unexpected type_ {}".format(type_))
 
     def getNick(self, entity_jid):
         """Return a nick name for an entity
@@ -965,7 +1126,6 @@
 
 
 class SatPresenceProtocol(xmppim.PresenceClientProtocol):
-
     def __init__(self, host):
         xmppim.PresenceClientProtocol.__init__(self)
         self.host = host
@@ -977,7 +1137,17 @@
         presence_d.addCallback(lambda __: super(SatPresenceProtocol, self).send(obj))
 
     def availableReceived(self, entity, show=None, statuses=None, priority=0):
-        log.debug(_(u"presence update for [%(entity)s] (available, show=%(show)s statuses=%(statuses)s priority=%(priority)d)") % {'entity': entity, C.PRESENCE_SHOW: show, C.PRESENCE_STATUSES: statuses, C.PRESENCE_PRIORITY: priority})
+        log.debug(
+            _(
+                u"presence update for [%(entity)s] (available, show=%(show)s statuses=%(statuses)s priority=%(priority)d)"
+            )
+            % {
+                "entity": entity,
+                C.PRESENCE_SHOW: show,
+                C.PRESENCE_STATUSES: statuses,
+                C.PRESENCE_PRIORITY: priority,
+            }
+        )
 
         if not statuses:
             statuses = {}
@@ -985,20 +1155,25 @@
         if None in statuses:  # we only want string keys
             statuses[C.PRESENCE_STATUSES_DEFAULT] = statuses.pop(None)
 
-        if not self.host.trigger.point("presenceReceived", entity, show, priority, statuses, self.parent.profile):
+        if not self.host.trigger.point(
+            "presenceReceived", entity, show, priority, statuses, self.parent.profile
+        ):
             return
 
-        self.host.memory.setPresenceStatus(entity, show or "",
-                                           int(priority), statuses,
-                                           self.parent.profile)
+        self.host.memory.setPresenceStatus(
+            entity, show or "", int(priority), statuses, self.parent.profile
+        )
 
         # now it's time to notify frontends
-        self.host.bridge.presenceUpdate(entity.full(), show or "",
-                                        int(priority), statuses,
-                                        self.parent.profile)
+        self.host.bridge.presenceUpdate(
+            entity.full(), show or "", int(priority), statuses, self.parent.profile
+        )
 
     def unavailableReceived(self, entity, statuses=None):
-        log.debug(_(u"presence update for [%(entity)s] (unavailable, statuses=%(statuses)s)") % {'entity': entity, C.PRESENCE_STATUSES: statuses})
+        log.debug(
+            _(u"presence update for [%(entity)s] (unavailable, statuses=%(statuses)s)")
+            % {"entity": entity, C.PRESENCE_STATUSES: statuses}
+        )
 
         if not statuses:
             statuses = {}
@@ -1006,21 +1181,33 @@
         if None in statuses:  # we only want string keys
             statuses[C.PRESENCE_STATUSES_DEFAULT] = statuses.pop(None)
 
-        if not self.host.trigger.point("presenceReceived", entity, "unavailable", 0, statuses, self.parent.profile):
+        if not self.host.trigger.point(
+            "presenceReceived", entity, "unavailable", 0, statuses, self.parent.profile
+        ):
             return
 
         # now it's time to notify frontends
         # if the entity is not known yet in this session or is already unavailable, there is no need to send an unavailable signal
         try:
-            presence = self.host.memory.getEntityDatum(entity, "presence", self.parent.profile)
+            presence = self.host.memory.getEntityDatum(
+                entity, "presence", self.parent.profile
+            )
         except (KeyError, exceptions.UnknownEntityError):
             # the entity has not been seen yet in this session
             pass
         else:
             if presence.show != C.PRESENCE_UNAVAILABLE:
-                self.host.bridge.presenceUpdate(entity.full(), C.PRESENCE_UNAVAILABLE, 0, statuses, self.parent.profile)
+                self.host.bridge.presenceUpdate(
+                    entity.full(),
+                    C.PRESENCE_UNAVAILABLE,
+                    0,
+                    statuses,
+                    self.parent.profile,
+                )
 
-        self.host.memory.setPresenceStatus(entity, C.PRESENCE_UNAVAILABLE, 0, statuses, self.parent.profile)
+        self.host.memory.setPresenceStatus(
+            entity, C.PRESENCE_UNAVAILABLE, 0, statuses, self.parent.profile
+        )
 
     def available(self, entity=None, show=None, statuses=None, priority=None):
         """Set a presence and statuses.
@@ -1032,7 +1219,11 @@
         """
         if priority is None:
             try:
-                priority = int(self.host.memory.getParamA("Priority", "Connection", profile_key=self.parent.profile))
+                priority = int(
+                    self.host.memory.getParamA(
+                        "Priority", "Connection", profile_key=self.parent.profile
+                    )
+                )
             except ValueError:
                 priority = 0
 
@@ -1048,7 +1239,7 @@
 
         # ... before switching back
         if None in statuses:
-            statuses['default'] = statuses.pop(None)
+            statuses["default"] = statuses.pop(None)
 
         if not self.host.trigger.point("presence_available", presence_elt, self.parent):
             return
@@ -1060,7 +1251,9 @@
         xmppim.PresenceClientProtocol.subscribed(self, entity)
         self.host.memory.delWaitingSub(entity.userhost(), self.parent.profile)
         item = self.parent.roster.getItem(entity)
-        if not item or not item.subscriptionTo:  # we automatically subscribe to 'to' presence
+        if (
+            not item or not item.subscriptionTo
+        ):  # we automatically subscribe to 'to' presence
             log.debug(_('sending automatic "from" subscription request'))
             self.subscribe(entity)
 
@@ -1070,11 +1263,11 @@
 
     def subscribedReceived(self, entity):
         log.debug(_(u"subscription approved for [%s]") % entity.userhost())
-        self.host.bridge.subscribe('subscribed', entity.userhost(), self.parent.profile)
+        self.host.bridge.subscribe("subscribed", entity.userhost(), self.parent.profile)
 
     def unsubscribedReceived(self, entity):
         log.debug(_(u"unsubscription confirmed for [%s]") % entity.userhost())
-        self.host.bridge.subscribe('unsubscribed', entity.userhost(), self.parent.profile)
+        self.host.bridge.subscribe("unsubscribed", entity.userhost(), self.parent.profile)
 
     @defer.inlineCallbacks
     def subscribeReceived(self, entity):
@@ -1083,11 +1276,15 @@
         item = self.parent.roster.getItem(entity)
         if item and item.subscriptionTo:
             # We automatically accept subscription if we are already subscribed to contact presence
-            log.debug(_('sending automatic subscription acceptance'))
+            log.debug(_("sending automatic subscription acceptance"))
             self.subscribed(entity)
         else:
-            self.host.memory.addWaitingSub('subscribe', entity.userhost(), self.parent.profile)
-            self.host.bridge.subscribe('subscribe', entity.userhost(), self.parent.profile)
+            self.host.memory.addWaitingSub(
+                "subscribe", entity.userhost(), self.parent.profile
+            )
+            self.host.bridge.subscribe(
+                "subscribe", entity.userhost(), self.parent.profile
+            )
 
     @defer.inlineCallbacks
     def unsubscribeReceived(self, entity):
@@ -1095,9 +1292,9 @@
         yield self.parent.roster.got_roster
         item = self.parent.roster.getItem(entity)
         if item and item.subscriptionFrom:  # we automatically remove contact
-            log.debug(_('automatic contact deletion'))
+            log.debug(_("automatic contact deletion"))
             self.host.delContact(entity, self.parent.profile)
-        self.host.bridge.subscribe('unsubscribe', entity.userhost(), self.parent.profile)
+        self.host.bridge.subscribe("unsubscribe", entity.userhost(), self.parent.profile)
 
 
 class SatDiscoProtocol(disco.DiscoClientProtocol):
@@ -1117,9 +1314,8 @@
 
 
 class SatVersionHandler(generic.VersionHandler):
-
     def getDiscoInfo(self, requestor, target, node):
-        #XXX: We need to work around wokkel's behaviour (namespace not added if there is a
+        # XXX: We need to work around wokkel's behaviour (namespace not added if there is a
         # node) as it cause issues with XEP-0115 & PEP (XEP-0163): there is a node when server
         # ask for disco info, and not when we generate the key, so the hash is used with different
         # disco features, and when the server (seen on ejabberd) generate its own hash for security check
@@ -1131,11 +1327,12 @@
     """ Manage disco Identity of SàT.
 
     """
-    #TODO: dynamic identity update (see docstring). Note that a XMPP entity can have several identities
+
+    # TODO: dynamic identity update (see docstring). Note that a XMPP entity can have several identities
     implements(iwokkel.IDisco)
 
-    def getDiscoInfo(self, requestor, target, nodeIdentifier=''):
+    def getDiscoInfo(self, requestor, target, nodeIdentifier=""):
         return self.parent.identities
 
-    def getDiscoItems(self, requestor, target, nodeIdentifier=''):
+    def getDiscoItems(self, requestor, target, nodeIdentifier=""):
         return []