core: style improvments (90 chars limit)

This commit is contained in:
Goffi 2018-07-29 18:44:27 +02:00
commit ff79fa3ce0
2 changed files with 219 additions and 196 deletions

View file

@ -55,7 +55,8 @@ class SAT(service.Service):
self._menus = (
OrderedDict()
) # dynamic menus. key: callback_id, value: menu data (dictionnary)
self._menus_paths = {} # path to id. key: (menu_type, lower case tuple of path), value: menu id
self._menus_paths = {} # path to id. key: (menu_type, lower case tuple of path),
# value: menu id
self.initialised = defer.Deferred()
self.profiles = {}
self.plugins = {}
@ -153,7 +154,10 @@ class SAT(service.Service):
@property
def full_version(self):
"""Return the full version of SàT (with release name and extra data when in development mode)"""
"""Return the full version of SàT
In developement mode, release name and extra data are returned too
"""
version = self.version
if version[-1] == "D":
# we are in debug version, we add extra data
@ -201,8 +205,9 @@ class SAT(service.Service):
# FIXME: module imported but cancelled should be deleted
# TODO: make this more generic and reusable in tools.common
# FIXME: should use imp
# TODO: do not import all plugins if no needed: component plugins are not needed if we
# just use a client, and plugin blacklisting should be possible in sat.conf
# TODO: do not import all plugins if no needed: component plugins are not needed
# if we just use a client, and plugin blacklisting should be possible in
# sat.conf
plugins_path = os.path.dirname(sat.plugins.__file__)
plugin_glob = "plugin*." + C.PLUGIN_EXT
plug_lst = [
@ -219,7 +224,8 @@ class SAT(service.Service):
except exceptions.MissingModule as e:
self._unimport_plugin(plugin_path)
log.warning(
u"Can't import plugin [{path}] because of an unavailale third party module:\n{msg}".format(
u"Can't import plugin [{path}] because of an unavailale third party "
u"module:\n{msg}".format(
path=plugin_path, msg=e
)
)
@ -265,7 +271,8 @@ class SAT(service.Service):
if import_name in plugins_to_import:
log.error(
_(
u"Name conflict for import name [{import_name}], can't import plugin [{name}]"
u"Name conflict for import name [{import_name}], can't import "
u"plugin [{name}]"
).format(**plugin_info)
)
continue
@ -283,9 +290,12 @@ class SAT(service.Service):
):
"""Recursively import and their dependencies in the right order
@param plugins_to_import(dict): key=import_name and values=(plugin_path, module, plugin_info)
@param import_name(unicode, None): name of the plugin to import as found in PLUGIN_INFO['import_name']
@param optional(bool): if False and plugin is not found, an ImportError exception is raised
@param plugins_to_import(dict): key=import_name and values=(plugin_path, module,
plugin_info)
@param import_name(unicode, None): name of the plugin to import as found in
PLUGIN_INFO['import_name']
@param optional(bool): if False and plugin is not found, an ImportError exception
is raised
"""
if import_name in self.plugins:
log.debug(u"Plugin {} already imported, passing".format(import_name))
@ -417,7 +427,8 @@ class SAT(service.Service):
Return list of activated plugins and plugin specific data
@param profile_key: %(doc_profile_key)s
C.PROF_KEY_NONE can be used to have general plugins data (i.e. not profile dependent)
C.PROF_KEY_NONE can be used to have general plugins data (i.e. not profile
dependent)
@return (dict)[Deferred]: features data where:
- key is plugin import name, present only for activated plugins
- value is a an other dict, when meaning is specific to each plugin.
@ -534,8 +545,9 @@ class SAT(service.Service):
raise exceptions.NotFound(profile_key)
def getClients(self, profile_key):
"""Convenient method to get list of clients from profile key (manage list through profile_key like C.PROF_KEY_ALL)
"""Convenient method to get list of clients from profile key
Manage list through profile_key like C.PROF_KEY_ALL
@param profile_key: %(doc_profile_key)s
@return: list of clients
"""
@ -599,8 +611,8 @@ class SAT(service.Service):
@param *extra_path: extra path element(s) to use
@return (unicode): path
"""
# FIXME: component and profile are parsed with **kwargs because of python 2 limitations
# once moved to python 3, this can be fixed
# FIXME: component and profile are parsed with **kwargs because of python 2
# limitations. Once moved to python 3, this can be fixed
component = kwargs.pop("component", False)
profile = kwargs.pop("profile", True)
assert not kwargs
@ -639,18 +651,12 @@ class SAT(service.Service):
## XMPP methods ##
def _messageSend(
self,
to_jid_s,
message,
subject=None,
mess_type="auto",
extra=None,
profile_key=C.PROF_KEY_NONE,
):
def _messageSend(self, to_jid_s, message, subject=None, mess_type="auto", extra=None,
profile_key=C.PROF_KEY_NONE,):
client = self.getClient(profile_key)
to_jid = jid.JID(to_jid_s)
# XXX: we need to use the dictionary comprehension because D-Bus return its own types, and pickle can't manage them. TODO: Need to find a better way
# XXX: we need to use the dictionary comprehension because D-Bus return its own
# types, and pickle can't manage them. TODO: Need to find a better way
return client.sendMessage(
to_jid,
message,
@ -674,7 +680,8 @@ class SAT(service.Service):
self.memory.getParamA("Priority", "Connection", profile_key=profile)
)
self.profiles[profile].presence.available(to_jid, show, statuses, priority)
# XXX: FIXME: temporary fix to work around openfire 3.7.0 bug (presence is not broadcasted to generating resource)
# XXX: FIXME: temporary fix to work around openfire 3.7.0 bug (presence is not
# broadcasted to generating resource)
if "" in statuses:
statuses[C.PRESENCE_STATUSES_DEFAULT] = statuses.pop("")
self.bridge.presenceUpdate(
@ -709,7 +716,8 @@ class SAT(service.Service):
"""Add a contact in roster list"""
profile = self.memory.getProfileName(profile_key)
assert profile
# presence is sufficient, as a roster push will be sent according to RFC 6121 §3.1.2
# presence is sufficient, as a roster push will be sent according to
# RFC 6121 §3.1.2
self.profiles[profile].presence.subscribe(to_jid)
def _updateContact(self, to_jid_s, name, groups, profile_key):
@ -801,16 +809,18 @@ class SAT(service.Service):
"""retrieve all services or contacts managing a set a features
@param namespaces(list[unicode]): features which must be handled
@param identities(list[tuple[unicode,unicode]], None): if not None or empty, only keep those identities
tuple must by (category, type)
@param identities(list[tuple[unicode,unicode]], None): if not None or empty,
only keep those identities tuple must by (category, type)
@param bare_jids(bool): retrieve only bare_jids if True
if False, retrieve full jid of connected devices
@param service(bool): if True return service from our roster
@param roster(bool): if True, return entities in roster
full jid of all matching resources available will be returned
@param own_jid(bool): if True, return profile's jid resources
@param local_device(bool): if True, return profile's jid local resource (i.e. client.jid)
@return (tuple(dict[jid.JID(), tuple[unicode, unicode, unicode]]*3)): found entities in a tuple with:
@param local_device(bool): if True, return profile's jid local resource
(i.e. client.jid)
@return (tuple(dict[jid.JID(), tuple[unicode, unicode, unicode]]*3)): found
entities in a tuple with:
- service entities
- own entities
- roster entities
@ -896,9 +906,9 @@ class SAT(service.Service):
@param action_data(dict): action data (see bridge documentation)
@param security_limit: %(doc_security_limit)s
@param keep_id(None, unicode): if not None, used to keep action for differed retrieval
must be set to the callback_id
action will be deleted after 30 min.
@param keep_id(None, unicode): if not None, used to keep action for differed
retrieval. Must be set to the callback_id.
Action will be deleted after 30 min.
@param profile: %(doc_profile)s
"""
id_ = unicode(uuid.uuid4())
@ -948,7 +958,8 @@ class SAT(service.Service):
@return (dict): data with the following keys:
'position' (int): current possition
'size' (int): end_position
if id doesn't exists (may be a finished progression), and empty dict is returned
if id doesn't exists (may be a finished progression), and empty dict is
returned
"""
client = self.getClient(profile)
try:
@ -969,7 +980,8 @@ class SAT(service.Service):
"""Return all progress metadata at once
@param profile_key: %(doc_profile)s
if C.PROF_KEY_ALL is used, all progress metadata from all profiles are returned
if C.PROF_KEY_ALL is used, all progress metadata from all profiles are
returned
@return (dict[dict[dict]]): a dict which map profile to progress_dict
progress_dict map progress_id to progress_data
progress_metadata is the same dict as sent by [progressStarted]
@ -1012,7 +1024,8 @@ class SAT(service.Service):
@param callback(callable): method to call
@param kwargs: can contain:
with_data(bool): True if the callback use the optional data dict
force_id(unicode): id to avoid generated id. Can lead to name conflict, avoid if possible
force_id(unicode): id to avoid generated id. Can lead to name conflict, avoid
if possible
one_shot(bool): True to delete callback once it have been called
@return: id of the registered callback
"""
@ -1050,7 +1063,8 @@ class SAT(service.Service):
@profile_key: %(doc_profile_key)s
@return: a deferred which fire a dict where key can be:
- xmlui: a XMLUI need to be displayed
- validated: if present, can be used to launch a callback, it can have the values
- validated: if present, can be used to launch a callback, it can have the
values
- C.BOOL_TRUE
- C.BOOL_FALSE
"""
@ -1117,25 +1131,33 @@ class SAT(service.Service):
):
"""register a new menu for frontends
@param path(iterable[unicode]): path to go to the menu (category/subcategory/.../item) (e.g.: ("File", "Open"))
@param path(iterable[unicode]): path to go to the menu
(category/subcategory/.../item) (e.g.: ("File", "Open"))
/!\ use D_() instead of _() for translations (e.g. (D_("File"), D_("Open")))
untranslated/lower case path can be used to identity a menu, for this reason it must be unique independently of case.
@param callback(callable): method to be called when menuitem is selected, callable or a callback id (string) as returned by [registerCallback]
untranslated/lower case path can be used to identity a menu, for this reason
it must be unique independently of case.
@param callback(callable): method to be called when menuitem is selected, callable
or a callback id (string) as returned by [registerCallback]
@param security_limit(int): %(doc_security_limit)s
/!\ security_limit MUST be added to data in launchCallback if used #TODO
@param help_string(unicode): string used to indicate what the menu do (can be show as a tooltip).
@param help_string(unicode): string used to indicate what the menu do (can be
show as a tooltip).
/!\ use D_() instead of _() for translations
@param type(unicode): one of:
- C.MENU_GLOBAL: classical menu, can be shown in a menubar on top (e.g. something like File/Open)
- C.MENU_GLOBAL: classical menu, can be shown in a menubar on top (e.g.
something like File/Open)
- C.MENU_ROOM: like a global menu, but only shown in multi-user chat
menu_data must contain a "room_jid" data
- C.MENU_SINGLE: like a global menu, but only shown in one2one chat
menu_data must contain a "jid" data
- C.MENU_JID_CONTEXT: contextual menu, used with any jid (e.g.: ad hoc commands, jid is already filled)
- C.MENU_JID_CONTEXT: contextual menu, used with any jid (e.g.: ad hoc
commands, jid is already filled)
menu_data must contain a "jid" data
- C.MENU_ROSTER_JID_CONTEXT: like JID_CONTEXT, but restricted to jids in roster.
- C.MENU_ROSTER_JID_CONTEXT: like JID_CONTEXT, but restricted to jids in
roster.
menu_data must contain a "room_jid" data
- C.MENU_ROSTER_GROUP_CONTEXT: contextual menu, used with group (e.g.: publish microblog, group is already filled)
- C.MENU_ROSTER_GROUP_CONTEXT: contextual menu, used with group (e.g.: publish
microblog, group is already filled)
menu_data must contain a "group" data
@return (unicode): menu_id (same as callback_id)
"""

View file

@ -54,9 +54,11 @@ class SatXMPPEntity(object):
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._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)
## initialisation ##
@ -113,10 +115,12 @@ class SatXMPPEntity(object):
@defer.inlineCallbacks
def startConnection(cls, host, profile, max_retries):
"""instantiate the entity and start the connection"""
# FIXME: reconnection doesn't seems to be handled correclty (client is deleted then recreated from scrash
# most of methods called here should be called once on first connection (e.g. adding subprotocols)
# but client should not be deleted except if session is finished (independently of connection/deconnection
#
# FIXME: reconnection doesn't seems to be handled correclty
# (client is deleted then recreated from scratch)
# most of methods called here should be called once on first connection
# (e.g. adding subprotocols)
# but client should not be deleted except if session is finished
# (independently of connection/deconnection)
try:
port = int(
host.memory.getParamA(
@ -164,9 +168,10 @@ class SatXMPPEntity(object):
yield defer.maybeDeferred(entity.entityConnected)
# Call profileConnected callback for all plugins, and print error message if any of them fails
# Call profileConnected callback for all plugins,
# and print error message if any of them fails
conn_cb_list = []
for dummy, callback in plugin_conn_cb:
for __, callback in plugin_conn_cb:
conn_cb_list.append(defer.maybeDeferred(callback, entity))
list_d = defer.DeferredList(conn_cb_list)
@ -193,7 +198,7 @@ class SatXMPPEntity(object):
"""Return a deferred which fire when the client is connected"""
return self.conn_deferred
def _disconnectionCb(self, dummy):
def _disconnectionCb(self, __):
self._connected = None
def _disconnectionEb(self, failure_):
@ -219,7 +224,7 @@ class SatXMPPEntity(object):
self.profile, unicode(self.jid)
) # we send the signal to the clients
def _finish_connection(self, dummy):
def _finish_connection(self, __):
self.conn_deferred.callback(None)
def streamInitialized(self):
@ -283,7 +288,7 @@ class SatXMPPEntity(object):
)
@defer.inlineCallbacks
def _cleanConnection(self, dummy):
def _cleanConnection(self, __):
"""method called on disconnection
used to call profileDisconnected* triggers
@ -380,17 +385,9 @@ class SatXMPPEntity(object):
"""
raise NotImplementedError
def sendMessage(
self,
to_jid,
message,
subject=None,
mess_type="auto",
extra=None,
uid=None,
no_trigger=False,
):
"""Send a message to an entity
def sendMessage(self, to_jid, message, subject=None, mess_type="auto", extra=None,
uid=None, no_trigger=False,):
r"""Send a message to an entity
@param to_jid(jid.JID): destinee of the message
@param message(dict): message body, key is the language (use '' when unknown)
@ -470,21 +467,15 @@ class SatXMPPEntity(object):
):
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.addCallback(lambda __: 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(_(u"Triggers, storage and echo have been inhibited by the "
u"'send_only' parameter"))
else:
self.addPostXmlCallbacks(post_xml_treatments)
post_xml_treatments.addErrback(self._cancelErrorTrap)
@ -562,8 +553,10 @@ class SatXMPPClient(SatXMPPEntity, wokkel_client.XMPPClient):
self.started = time.time()
# Currently, we use "client/pc/Salut à Toi", but as
# SàT is multi-frontends and can be used on mobile devices, as a bot, with a web frontend,
# etc., we should implement a way to dynamically update identities through the bridge
# SàT is multi-frontends and can be used on mobile devices, as a bot,
# with a web frontend,
# etc., we should implement a way to dynamically update identities through the
# bridge
self.identities = [disco.DiscoIdentity(u"client", u"pc", C.APP_NAME)]
if sys.platform == "android":
# FIXME: temporary hack as SRV is not working on android
@ -591,9 +584,8 @@ class SatXMPPClient(SatXMPPEntity, wokkel_client.XMPPClient):
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
)
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__(
@ -634,7 +626,8 @@ class SatXMPPClient(SatXMPPEntity, wokkel_client.XMPPClient):
# (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
#  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)
@ -648,8 +641,10 @@ class SatXMPPClient(SatXMPPEntity, wokkel_client.XMPPClient):
@param mess_data(dict): message data as constructed by onMessage workflow
@return (dict): mess_data (so it can be used in a deferred chain)
"""
# XXX: This is the last trigger before u"send" (last but one globally) for sending message.
# This is intented for e2e encryption which doesn't do full stanza encryption (e.g. OTR)
# XXX: This is the last trigger before u"send" (last but one globally)
# for sending message.
# 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"])
@ -675,10 +670,10 @@ class SatXMPPClient(SatXMPPEntity, wokkel_client.XMPPClient):
profile=self.profile,
)
def _finish_connection(self, dummy):
def _finish_connection(self, __):
self.roster.requestRoster()
self.presence.available()
super(SatXMPPClient, self)._finish_connection(dummy)
super(SatXMPPClient, self)._finish_connection(__)
class SatXMPPComponent(SatXMPPEntity, component.Component):
@ -696,7 +691,8 @@ class SatXMPPComponent(SatXMPPEntity, component.Component):
is_component = True
sendHistory = (
False
) # XXX: set to True from entry plugin to keep messages in history for received messages
) # XXX: set to True from entry plugin to keep messages in history for received
# messages
def __init__(
self,
@ -748,7 +744,8 @@ class SatXMPPComponent(SatXMPPEntity, component.Component):
@param required(bool): True if plugin is mandatory
for recursive calls only, should not be modified by inital caller
@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
@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 not required:
@ -756,7 +753,8 @@ class SatXMPPComponent(SatXMPPEntity, component.Component):
else:
log.error(
_(
u"Plugin {current_name} is needed for {entry_name}, but it doesn't handle component mode"
u"Plugin {current_name} is needed for {entry_name}, "
u"but it doesn't handle component mode"
).format(
current_name=current._info[u"import_name"],
entry_name=self.entry_plugin._info[u"import_name"],
@ -898,7 +896,7 @@ class SatMessageProtocol(xmppim.MessageProtocol):
else:
return self.host.memory.addToHistory(client, data)
def bridgeSignal(self, dummy, client, data):
def bridgeSignal(self, __, client, data):
try:
data["extra"]["received_timestamp"] = data["received_timestamp"]
data["extra"]["delay_sender"] = data["delay_sender"]
@ -942,7 +940,8 @@ class SatRosterProtocol(xmppim.RosterClientProtocol):
# 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(
u"Removing contact {} from roster because there is no presence "
u"subscription".format(
item.jid
)
)
@ -959,7 +958,8 @@ class SatRosterProtocol(xmppim.RosterClientProtocol):
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."
u"Received a roster item with a resource, this is not common but not "
u"restricted by RFC 6121, this case may be not well tested."
)
if not item.subscriptionTo:
if not item.subscriptionFrom:
@ -1044,8 +1044,8 @@ class SatRosterProtocol(xmppim.RosterClientProtocol):
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}
u"there is no cache for the group [{group}] of the removed roster "
u"item [{jid_}]".format(group=group, jid=entity)
)
# then we send the bridge signal
@ -1139,14 +1139,15 @@ class SatPresenceProtocol(xmppim.PresenceClientProtocol):
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)"
u"presence update for [{entity}] (available, show={show} "
u"statuses={statuses} priority={priority})"
)
.format(
entity=entity,
show=show,
statuses=statuses,
priority=priority,
)
% {
"entity": entity,
C.PRESENCE_SHOW: show,
C.PRESENCE_STATUSES: statuses,
C.PRESENCE_PRIORITY: priority,
}
)
if not statuses:
@ -1187,7 +1188,8 @@ class SatPresenceProtocol(xmppim.PresenceClientProtocol):
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
# 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
@ -1275,7 +1277,8 @@ class SatPresenceProtocol(xmppim.PresenceClientProtocol):
yield self.parent.roster.got_roster
item = self.parent.roster.getItem(entity)
if item and item.subscriptionTo:
# We automatically accept subscription if we are already subscribed to contact presence
# We automatically accept subscription if we are already subscribed to
# contact presence
log.debug(_("sending automatic subscription acceptance"))
self.subscribed(entity)
else:
@ -1314,22 +1317,22 @@ class SatFallbackHandler(generic.FallbackHandler):
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
# 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
# it reject our features (resulting in e.g. no notification on PEP)
# 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 it reject our
# features (resulting in e.g. no notification on PEP)
return generic.VersionHandler.getDiscoInfo(self, requestor, target, None)
class SatIdentityHandler(XMPPHandler):
""" Manage disco Identity of SàT.
"""
# TODO: dynamic identity update (see docstring). Note that a XMPP entity can have several identities
"""Manage disco Identity of SàT."""
implements(iwokkel.IDisco)
# TODO: dynamic identity update (see docstring). Note that a XMPP entity can have
# several identities
def getDiscoInfo(self, requestor, target, nodeIdentifier=""):
return self.parent.identities