From f3a2d532f5faba5a543f958c4512139b359e2bbf Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Thu, 17 May 2007 17:41:42 +0100 Subject: [PATCH 1/8] services/presence: buddy: add mapping to/from Telepathy handles --- services/presence/buddy.py | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/services/presence/buddy.py b/services/presence/buddy.py index 429f8249..d434221c 100644 --- a/services/presence/buddy.py +++ b/services/presence/buddy.py @@ -236,6 +236,28 @@ class Buddy(ExportedGObject): full set of properties, just the changes. """ + @dbus.service.signal(_BUDDY_INTERFACE, signature='sou') + def TelepathyHandleAdded(self, tp_conn_name, tp_conn_path, handle): + """Another Telepathy handle has become associated with the buddy. + + This must only be emitted for non-channel-specific handles. + + tp_conn_name -- The bus name at which the Telepathy connection may be + found + tp_conn_path -- The object path at which the Telepathy connection may + be found + handle -- The handle of type CONTACT, which is not channel-specific, + newly associated with the buddy + """ + + @dbus.service.signal(_BUDDY_INTERFACE, signature='sou') + def TelepathyHandleRemoved(self, tp_conn_name, tp_conn_path, handle): + """A Telepathy handle has ceased to be associated with the buddy, + probably because that contact went offline. + + The parameters are the same as for TelepathyHandleAdded. + """ + # dbus methods @dbus.service.method(_BUDDY_INTERFACE, in_signature="", out_signature="ay") @@ -294,6 +316,22 @@ class Buddy(ExportedGObject): props[_PROP_CURACT] = "" return props + @dbus.service.method(_BUDDY_INTERFACE, + in_signature='', out_signature='a(sou)') + def GetTelepathyHandles(self): + """Return a list of non-channel-specific Telepathy contact handles + associated with this Buddy. + + :Returns: + An array of triples (connection well-known bus name, connection + object path, handle). + """ + ret = [] + for plugin in self.handles: + conn = plugin.get_connection() + ret.append((str(conn.service_name), conn.object_path, + self.handles[plugin])) + # methods def object_path(self): """Retrieve our dbus.ObjectPath object""" From bf84eca102e300301042b0391228a8ccdda8021d Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Fri, 18 May 2007 15:21:36 +0100 Subject: [PATCH 2/8] services/presence/server_plugin.py: Ensure _conn always exists (even if None) This means cleanup() will not fail on an uninitialized server plugin, and get_connection() will return None rather than raising AttributeError. --- services/presence/server_plugin.py | 1 + 1 file changed, 1 insertion(+) diff --git a/services/presence/server_plugin.py b/services/presence/server_plugin.py index d4930c9e..daa31aa1 100644 --- a/services/presence/server_plugin.py +++ b/services/presence/server_plugin.py @@ -125,6 +125,7 @@ class ServerPlugin(gobject.GObject): """ gobject.GObject.__init__(self) + self._conn = None self._icon_cache = BuddyIconCache() self._registry = registry From 78356b195669c92c5358c3194cae489e29ac6d09 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Fri, 18 May 2007 15:22:32 +0100 Subject: [PATCH 3/8] services/presence/: move more of the handle tracking into the Buddy object --- services/presence/buddy.py | 25 ++++++++++++- services/presence/presenceservice.py | 55 +++++++++++++++++++++++----- 2 files changed, 70 insertions(+), 10 deletions(-) diff --git a/services/presence/buddy.py b/services/presence/buddy.py index d434221c..a1d9a437 100644 --- a/services/presence/buddy.py +++ b/services/presence/buddy.py @@ -80,7 +80,9 @@ class Buddy(ExportedGObject): 'property-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT])), 'icon-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, - ([gobject.TYPE_PYOBJECT])) + ([gobject.TYPE_PYOBJECT])), + 'disappeared': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, + ([])), } __gproperties__ = { @@ -236,6 +238,12 @@ class Buddy(ExportedGObject): full set of properties, just the changes. """ + def add_telepathy_handle(self, tp_client, handle): + """Add a Telepathy handle.""" + conn = tp_client.get_connection() + self.TelepathyHandleAdded(conn.service_name, conn.object_path, handle) + self.handles[tp_client] = handle + @dbus.service.signal(_BUDDY_INTERFACE, signature='sou') def TelepathyHandleAdded(self, tp_conn_name, tp_conn_path, handle): """Another Telepathy handle has become associated with the buddy. @@ -250,6 +258,21 @@ class Buddy(ExportedGObject): newly associated with the buddy """ + def remove_telepathy_handle(self, tp_client, handle): + """Remove a Telepathy handle.""" + conn = tp_client.get_connection() + my_handle = self.handles.get(tp_client, 0) + if my_handle == handle: + del self.handles[tp_client] + self.TelepathyHandleRemoved(conn.service_name, conn.object_path, + handle) + if not self.handles: + self.emit('disappeared') + else: + _logger.debug('Telepathy handle %u supposedly removed, but ' + 'my handle on that connection is %u - ignoring', + handle, my_handle) + @dbus.service.signal(_BUDDY_INTERFACE, signature='sou') def TelepathyHandleRemoved(self, tp_conn_name, tp_conn_path, handle): """A Telepathy handle has ceased to be associated with the buddy, diff --git a/services/presence/presenceservice.py b/services/presence/presenceservice.py index c554ce6e..6273a22c 100644 --- a/services/presence/presenceservice.py +++ b/services/presence/presenceservice.py @@ -126,12 +126,12 @@ class PresenceService(ExportedGObject): objid = self._get_next_object_id() buddy = Buddy(self._bus_name, objid, key=key) buddy.connect("validity-changed", self._buddy_validity_changed_cb) + buddy.connect("disappeared", self._buddy_disappeared_cb) self._buddies[key] = buddy - buddies = self._handles_buddies[tp] - buddies[handle] = buddy + self._handles_buddies[tp][handle] = buddy # store the handle of the buddy for this CM - buddy.handles[tp] = handle + buddy.add_telepathy_handle(tp, handle) buddy.set_properties(props) @@ -143,6 +143,12 @@ class PresenceService(ExportedGObject): self.BuddyDisappeared(buddy.object_path()) _logger.debug("Buddy left: %s (%s)" % (buddy.props.nick, buddy.props.color)) + def _buddy_disappeared_cb(self, buddy): + if buddy.props.valid: + self.BuddyDisappeared(buddy.object_path()) + _logger.debug('Buddy left: %s (%s)' % (buddy.props.nick, buddy.props.color) + self._buddies.pop(buddy.props.key) + def _contact_offline(self, tp, handle): if not self._handles_buddies[tp].has_key(handle): return @@ -151,12 +157,7 @@ class PresenceService(ExportedGObject): key = buddy.props.key # the handle of the buddy for this CM is not valid anymore - buddy.handles.pop(tp) - if not buddy.handles: - if buddy.props.valid: - self.BuddyDisappeared(buddy.object_path()) - _logger.debug("Buddy left: %s (%s)" % (buddy.props.nick, buddy.props.color)) - self._buddies.pop(key) + buddy.remove_telepathy_handle(tp, handle) def _get_next_object_id(self): """Increment and return the object ID counter.""" @@ -304,6 +305,42 @@ class PresenceService(ExportedGObject): return buddy.object_path() raise NotFoundError("The buddy was not found.") + @dbus.service.method(_PRESENCE_INTERFACE, in_signature='sou', + out_signature='o') + def GetBuddyByTelepathyHandle(self, tp_conn_name, tp_conn_path, handle): + """Get the buddy corresponding to a Telepathy handle. + + :Parameters: + `tp_conn_name` : str + The well-known bus name of a Telepathy connection + `tp_conn_path` : dbus.ObjectPath + The object path of the Telepathy connection + `handle` : int or long + The handle of a Telepathy contact on that connection, + of type HANDLE_TYPE_CONTACT. This may not be a + channel-specific handle. + :Returns: the object path of a Buddy + :Raises NotFoundError: if the buddy is not found. + """ + for tp, handles in self._handles_buddies.iteritems(): + conn = tp.get_connection() + if conn is None: + continue + if (conn.service_name == tp_conn_name + and conn.object_path == tp_conn_path): + buddy = handles.get(handle) + if buddy is not None and buddy.props.valid: + return buddy.object_path() + # either the handle is invalid, or we don't have a Buddy + # object for that buddy because we don't have all their + # details yet + raise NotFoundError("The buddy %u was not found on the " + "connection to %s:%s" + % (handle, tp_conn_name, tp_conn_path)) + raise NotFoundError("The buddy %u was not found: we have no " + "connection to %s:%s" % (handle, tp_conn_name, + tp_conn_path)) + @dbus.service.method(_PRESENCE_INTERFACE, out_signature="o") def GetOwner(self): if not self._owner: From c6ac2873c881455bc0ba1aa6befae9bd44a1d1e4 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Fri, 18 May 2007 15:24:58 +0100 Subject: [PATCH 4/8] sugar.presence.presenceservice: Add get_buddy_by_telepathy_handle() --- sugar/presence/presenceservice.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/sugar/presence/presenceservice.py b/sugar/presence/presenceservice.py index 1bc9a579..fc9d13e1 100644 --- a/sugar/presence/presenceservice.py +++ b/sugar/presence/presenceservice.py @@ -317,6 +317,32 @@ class PresenceService(gobject.GObject): return None return self._new_object(buddy_op) + def get_buddy_by_telepathy_handle(self, tp_conn_name, tp_conn_path, + handle): + """Retrieve single Buddy object for the given public key + + :Parameters: + `tp_conn_name` : str + The well-known bus name of a Telepathy connection + `tp_conn_path` : dbus.ObjectPath + The object path of the Telepathy connection + `handle` : int or long + The handle of a Telepathy contact on that connection, + of type HANDLE_TYPE_CONTACT. This may not be a + channel-specific handle. + :Returns: the Buddy object, or None if the buddy is not found + """ + try: + buddy_op = self._ps.GetBuddyByTelepathyHandle(tp_conn_name, + tp_conn_path, + handle) + except dbus.exceptions.DBusException, err: + _logger.warn('Unable to retrieve buddy handle for handle %u at ' + 'conn %s:%s from presence service: %s', + handle, tp_conn_name, tp_conn_path, err) + return None + return self._new_object(buddy_op) + def get_owner(self): """Retrieves the laptop "owner" Buddy object.""" try: From 29cc39968d491288ee460c8daa066479592d7e2b Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Fri, 18 May 2007 15:31:32 +0100 Subject: [PATCH 5/8] Don't rely on dbus importing the exceptions module - import it explicitly. Also divide up imports in a PEP8-compliant way. --- sugar/presence/presenceservice.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sugar/presence/presenceservice.py b/sugar/presence/presenceservice.py index fc9d13e1..9d435e7e 100644 --- a/sugar/presence/presenceservice.py +++ b/sugar/presence/presenceservice.py @@ -16,9 +16,13 @@ # Free Software Foundation, Inc., 59 Temple Place - Suite 330, # Boston, MA 02111-1307, USA. -import dbus, dbus.glib, gobject import logging +import dbus +import dbus.exceptions +import dbus.glib +import gobject + # XXX use absolute imports # from sugar.presence import buddy, activity # this *kind* of relative import is deprecated From ccc224e76f4fff2d800ac2dae2527d361415a1cf Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Fri, 18 May 2007 15:33:21 +0100 Subject: [PATCH 6/8] sugar.presence.presenceservice: Use absolute imports --- sugar/presence/presenceservice.py | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/sugar/presence/presenceservice.py b/sugar/presence/presenceservice.py index 9d435e7e..a0b60ee1 100644 --- a/sugar/presence/presenceservice.py +++ b/sugar/presence/presenceservice.py @@ -23,17 +23,8 @@ import dbus.exceptions import dbus.glib import gobject -# XXX use absolute imports -# from sugar.presence import buddy, activity -# this *kind* of relative import is deprecated -# with an explicit relative import slated to be -# introduced (available in Python 2.5 with a __future__ -# import), that would read as: -# from . import buddy, activity -# see PEP: http://docs.python.org/whatsnew/pep-328.html - -import buddy -from activity import Activity +from sugar.presence.buddy import Buddy +from sugar.presence.activity import Activity DBUS_SERVICE = "org.laptop.Sugar.Presence" @@ -163,7 +154,7 @@ class PresenceService(gobject.GObject): obj = self._objcache[object_path] except KeyError: if object_path.startswith(self._PS_BUDDY_OP): - obj = buddy.Buddy(self._bus, self._new_object, + obj = Buddy(self._bus, self._new_object, self._del_object, object_path) elif object_path.startswith(self._PS_ACTIVITY_OP): obj = Activity(self._bus, self._new_object, From 1d79d936274deeca634ecfc44a7a546c09492832 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Mon, 21 May 2007 11:50:29 +0100 Subject: [PATCH 7/8] Fix syntax --- services/presence/presenceservice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/presence/presenceservice.py b/services/presence/presenceservice.py index 6273a22c..4bd4da80 100644 --- a/services/presence/presenceservice.py +++ b/services/presence/presenceservice.py @@ -146,7 +146,7 @@ class PresenceService(ExportedGObject): def _buddy_disappeared_cb(self, buddy): if buddy.props.valid: self.BuddyDisappeared(buddy.object_path()) - _logger.debug('Buddy left: %s (%s)' % (buddy.props.nick, buddy.props.color) + _logger.debug('Buddy left: %s (%s)' % (buddy.props.nick, buddy.props.color)) self._buddies.pop(buddy.props.key) def _contact_offline(self, tp, handle): From 494e17b3cd03aa43a5d170d42e4463428a000272 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Thu, 24 May 2007 12:34:36 +0100 Subject: [PATCH 8/8] Revert "services/presence/: move more of the handle tracking into the Buddy object". It shouldn't have been applied before "services/presence: buddy: add mapping to/from Telepathy handles" which has not yet been reviewed. This reverts commit 78356b195669c92c5358c3194cae489e29ac6d09. Conflicts: services/presence/presenceservice.py --- services/presence/buddy.py | 25 +------------ services/presence/presenceservice.py | 55 +++++----------------------- 2 files changed, 10 insertions(+), 70 deletions(-) diff --git a/services/presence/buddy.py b/services/presence/buddy.py index eb072a18..ab397ef2 100644 --- a/services/presence/buddy.py +++ b/services/presence/buddy.py @@ -80,9 +80,7 @@ class Buddy(ExportedGObject): 'property-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT])), 'icon-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, - ([gobject.TYPE_PYOBJECT])), - 'disappeared': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, - ([])), + ([gobject.TYPE_PYOBJECT])) } __gproperties__ = { @@ -238,12 +236,6 @@ class Buddy(ExportedGObject): full set of properties, just the changes. """ - def add_telepathy_handle(self, tp_client, handle): - """Add a Telepathy handle.""" - conn = tp_client.get_connection() - self.TelepathyHandleAdded(conn.service_name, conn.object_path, handle) - self.handles[tp_client] = handle - @dbus.service.signal(_BUDDY_INTERFACE, signature='sou') def TelepathyHandleAdded(self, tp_conn_name, tp_conn_path, handle): """Another Telepathy handle has become associated with the buddy. @@ -258,21 +250,6 @@ class Buddy(ExportedGObject): newly associated with the buddy """ - def remove_telepathy_handle(self, tp_client, handle): - """Remove a Telepathy handle.""" - conn = tp_client.get_connection() - my_handle = self.handles.get(tp_client, 0) - if my_handle == handle: - del self.handles[tp_client] - self.TelepathyHandleRemoved(conn.service_name, conn.object_path, - handle) - if not self.handles: - self.emit('disappeared') - else: - _logger.debug('Telepathy handle %u supposedly removed, but ' - 'my handle on that connection is %u - ignoring', - handle, my_handle) - @dbus.service.signal(_BUDDY_INTERFACE, signature='sou') def TelepathyHandleRemoved(self, tp_conn_name, tp_conn_path, handle): """A Telepathy handle has ceased to be associated with the buddy, diff --git a/services/presence/presenceservice.py b/services/presence/presenceservice.py index 4bd4da80..c554ce6e 100644 --- a/services/presence/presenceservice.py +++ b/services/presence/presenceservice.py @@ -126,12 +126,12 @@ class PresenceService(ExportedGObject): objid = self._get_next_object_id() buddy = Buddy(self._bus_name, objid, key=key) buddy.connect("validity-changed", self._buddy_validity_changed_cb) - buddy.connect("disappeared", self._buddy_disappeared_cb) self._buddies[key] = buddy - self._handles_buddies[tp][handle] = buddy + buddies = self._handles_buddies[tp] + buddies[handle] = buddy # store the handle of the buddy for this CM - buddy.add_telepathy_handle(tp, handle) + buddy.handles[tp] = handle buddy.set_properties(props) @@ -143,12 +143,6 @@ class PresenceService(ExportedGObject): self.BuddyDisappeared(buddy.object_path()) _logger.debug("Buddy left: %s (%s)" % (buddy.props.nick, buddy.props.color)) - def _buddy_disappeared_cb(self, buddy): - if buddy.props.valid: - self.BuddyDisappeared(buddy.object_path()) - _logger.debug('Buddy left: %s (%s)' % (buddy.props.nick, buddy.props.color)) - self._buddies.pop(buddy.props.key) - def _contact_offline(self, tp, handle): if not self._handles_buddies[tp].has_key(handle): return @@ -157,7 +151,12 @@ class PresenceService(ExportedGObject): key = buddy.props.key # the handle of the buddy for this CM is not valid anymore - buddy.remove_telepathy_handle(tp, handle) + buddy.handles.pop(tp) + if not buddy.handles: + if buddy.props.valid: + self.BuddyDisappeared(buddy.object_path()) + _logger.debug("Buddy left: %s (%s)" % (buddy.props.nick, buddy.props.color)) + self._buddies.pop(key) def _get_next_object_id(self): """Increment and return the object ID counter.""" @@ -305,42 +304,6 @@ class PresenceService(ExportedGObject): return buddy.object_path() raise NotFoundError("The buddy was not found.") - @dbus.service.method(_PRESENCE_INTERFACE, in_signature='sou', - out_signature='o') - def GetBuddyByTelepathyHandle(self, tp_conn_name, tp_conn_path, handle): - """Get the buddy corresponding to a Telepathy handle. - - :Parameters: - `tp_conn_name` : str - The well-known bus name of a Telepathy connection - `tp_conn_path` : dbus.ObjectPath - The object path of the Telepathy connection - `handle` : int or long - The handle of a Telepathy contact on that connection, - of type HANDLE_TYPE_CONTACT. This may not be a - channel-specific handle. - :Returns: the object path of a Buddy - :Raises NotFoundError: if the buddy is not found. - """ - for tp, handles in self._handles_buddies.iteritems(): - conn = tp.get_connection() - if conn is None: - continue - if (conn.service_name == tp_conn_name - and conn.object_path == tp_conn_path): - buddy = handles.get(handle) - if buddy is not None and buddy.props.valid: - return buddy.object_path() - # either the handle is invalid, or we don't have a Buddy - # object for that buddy because we don't have all their - # details yet - raise NotFoundError("The buddy %u was not found on the " - "connection to %s:%s" - % (handle, tp_conn_name, tp_conn_path)) - raise NotFoundError("The buddy %u was not found: we have no " - "connection to %s:%s" % (handle, tp_conn_name, - tp_conn_path)) - @dbus.service.method(_PRESENCE_INTERFACE, out_signature="o") def GetOwner(self): if not self._owner: