Switching over to a copy-paste version of python-telepathy's channel manager
authorEd Page <eopage@byu.net>
Wed, 6 Jan 2010 02:27:51 +0000 (20:27 -0600)
committerEd Page <eopage@byu.net>
Wed, 6 Jan 2010 02:27:51 +0000 (20:27 -0600)
src/channel/call.py
src/channel/contact_list.py
src/channel/text.py
src/channel_manager.py
src/connection.py

index e855d32..52d17ad 100644 (file)
@@ -15,7 +15,10 @@ class CallChannel(
                telepathy.server.ChannelInterfaceGroup,
        ):
 
-       def __init__(self, connection, contactHandle):
+       def __init__(self, connection, manager, props, contactHandle):
+               self._manager = manager
+               self._props = props
+
                telepathy.server.ChannelTypeStreamedMedia.__init__(self, connection, None)
                telepathy.server.ChannelInterfaceCallState.__init__(self)
                telepathy.server.ChannelInterfaceGroup.__init__(self)
@@ -40,8 +43,10 @@ class CallChannel(
 
        def close(self):
                telepathy.server.ChannelTypeStreamedMedia.Close(self)
+               if self._manager.channel_exists(self._props):
+                       # Older python-telepathy requires doing this manually
+                       self._manager.remove_channel(self)
                self.remove_from_connection()
-               self._prop_getters = None # HACK to get around python-telepathy memory leaks
 
        @gtk_toolbox.log_exception(_moduleLogger)
        def ListStreams(self):
index 7f9981d..8c0860e 100644 (file)
@@ -17,7 +17,10 @@ class AbstractListChannel(
        ):
        "Abstract Contact List channels"
 
-       def __init__(self, connection, h):
+       def __init__(self, connection, manager, props, h):
+               self._manager = manager
+               self._props = props
+
                telepathy.server.ChannelTypeContactList.__init__(self, connection, h)
                telepathy.server.ChannelInterfaceGroup.__init__(self)
 
@@ -29,8 +32,8 @@ class AllContactsListChannel(AbstractListChannel):
        The group of contacts for whom you receive presence
        """
 
-       def __init__(self, connection, h):
-               AbstractListChannel.__init__(self, connection, h)
+       def __init__(self, connection, manager, props, h):
+               AbstractListChannel.__init__(self, connection, manager, props, h)
 
                self._callback = coroutines.func_sink(
                        coroutines.expand_positional(
@@ -58,8 +61,10 @@ class AllContactsListChannel(AbstractListChannel):
                self._callback = None
 
                telepathy.server.ChannelTypeContactList.Close(self)
+               if self._manager.channel_exists(self._props):
+                       # Older python-telepathy requires doing this manually
+                       self._manager.remove_channel(self)
                self.remove_from_connection()
-               self._prop_getters = None # HACK to get around python-telepathy memory leaks
 
        @gobject_utils.async
        @gtk_toolbox.log_exception(_moduleLogger)
index 3a4d067..4166c6e 100644 (file)
@@ -17,12 +17,15 @@ class TextChannel(telepathy.server.ChannelTypeText):
        Look into implementing ChannelInterfaceMessages for rich text formatting
        """
 
-       def __init__(self, connection, h):
-               telepathy.server.ChannelTypeText.__init__(self, connection, h)
+       def __init__(self, connection, manager, props, contactHandle):
+               self._manager = manager
+               self._props = props
+
+               telepathy.server.ChannelTypeText.__init__(self, connection, contactHandle)
                self._nextRecievedId = 0
                self._lastMessageTimestamp = datetime.datetime(1, 1, 1)
 
-               self._otherHandle = h
+               self._otherHandle = contactHandle
 
                self._callback = coroutines.func_sink(
                        coroutines.expand_positional(
@@ -57,15 +60,16 @@ class TextChannel(telepathy.server.ChannelTypeText):
                self.close()
 
        def close(self):
-               _moduleLogger.info("Closing text channel for %r" % (self._otherHandle, ))
                self._conn.session.conversations.updateSignalHandler.unregister_sink(
                        self._callback
                )
                self._callback = None
 
                telepathy.server.ChannelTypeText.Close(self)
+               if self._manager.channel_exists(self._props):
+                       # Older python-telepathy requires doing this manually
+                       self._manager.remove_channel(self)
                self.remove_from_connection()
-               self._prop_getters = None # HACK to get around python-telepathy memory leaks
 
        @property
        def _contactKey(self):
index d1e9942..efcf16d 100644 (file)
@@ -1,7 +1,6 @@
-import weakref
-import itertools
 import logging
 
+import dbus
 import telepathy
 
 import channel
@@ -10,59 +9,144 @@ import channel
 _moduleLogger = logging.getLogger("channel_manager")
 
 
-class ChannelManager(object):
+class TelepathyChannelManager(object):
 
        def __init__(self, connection):
-               self._connRef = weakref.ref(connection)
-               self._listChannels = weakref.WeakValueDictionary()
-               self._textChannels = weakref.WeakValueDictionary()
-               self._callChannels = weakref.WeakValueDictionary()
+               self._conn = connection
+
+               self._requestable_channel_classes = dict()
+               self._channels = dict()
+               self._fixed_properties = dict()
+               self._available_properties = dict()
 
        def close(self):
-               for chan in itertools.chain(
-                       self._listChannels.values(), self._textChannels.values(), self._callChannels.values()
-               ):
-                       try:
-                               chan.close()
-                       except Exception:
-                               _moduleLogger.exception("Shutting down %r" % (chan, ))
-
-       def channel_for_list(self, handle, suppress_handler=False):
-               try:
-                       chan = self._listChannels[handle]
-               except KeyError, e:
-                       if handle.get_type() != telepathy.HANDLE_TYPE_LIST:
-                               raise telepathy.errors.NotImplemented("Only server lists are allowed")
-                       _moduleLogger.debug("Requesting new contact list channel")
-
-                       chan = channel.contact_list.create_contact_list_channel(self._connRef(), handle)
-                       self._listChannels[handle] = chan
-                       self._connRef().add_channel(chan, handle, suppress_handler)
+               for channel_type in self._requestable_channel_classes:
+                       for chan in self._channels[channel_type].values():
+                               try:
+                                       _moduleLogger.info("Closing %s %s" % (channel_type, chan._object_path))
+                                       chan.Close()
+                               except Exception:
+                                       _moduleLogger.exception("Shutting down %r" % (chan, ))
+
+       def remove_channel(self, chan):
+               for channel_type in self._requestable_channel_classes:
+                       for handle, ichan in self._channels[channel_type].items():
+                               if chan == ichan:
+                                       del self._channels[channel_type][handle]
+
+       def _get_type_requested_handle(self, props):
+               type = props[telepathy.interfaces.CHANNEL_INTERFACE + '.ChannelType']
+               requested = props[telepathy.interfaces.CHANNEL_INTERFACE + '.Requested']
+               target_handle = props[telepathy.interfaces.CHANNEL_INTERFACE + '.TargetHandle']
+               target_handle_type = props[telepathy.interfaces.CHANNEL_INTERFACE + '.TargetHandleType']
+
+               handle = self._conn._handles[target_handle_type, target_handle]
+
+               return (type, requested, handle)
+
+       def channel_exists(self, props):
+               type, _, handle = self._get_type_requested_handle(props)
+
+               if type in self._channels:
+                       if handle in self._channels[type]:
+                               return True
+
+               return False
+
+       def channel_for_props(self, props, signal=True, **args):
+               type, suppress_handler, handle = self._get_type_requested_handle(props)
+
+               if type not in self._requestable_channel_classes:
+                       raise NotImplemented('Unknown channel type "%s"' % type)
+
+               if self.channel_exists(props):
+                       return self._channels[type][handle]
+
+               chan = self._requestable_channel_classes[type](props, **args)
+
+               if hasattr(self._conn, "add_channels"):
+                       # Newer python-telepathy
+                       self._conn.add_channels([chan], signal=signal)
+               elif hasattr(self._conn, "add_channel"):
+                       # Older python-telepathy
+                       self._conn.add_channel(chan, handle, suppress_handler)
+               else:
+                       raise RuntimeError("Uhh, what just happened with the connection")
+               self._channels[type][handle] = chan
+
                return chan
 
-       def channel_for_text(self, handle, suppress_handler=False):
-               try:
-                       chan = self._textChannels[handle]
-               except KeyError, e:
-                       if handle.get_type() != telepathy.HANDLE_TYPE_CONTACT:
-                               raise telepathy.errors.NotImplemented("Only Contacts are allowed")
-                       _moduleLogger.debug("Requesting new text channel")
-
-                       chan = channel.text.TextChannel(self._connRef(), handle)
-                       self._textChannels[handle] = chan
-                       self._connRef().add_channel(chan, handle, suppress_handler)
+       def _implement_channel_class(self, type, make_channel, fixed, available):
+               self._requestable_channel_classes[type] = make_channel
+               self._channels.setdefault(type, {})
+
+               self._fixed_properties[type] = fixed
+               self._available_properties[type] = available
+
+       def get_requestable_channel_classes(self):
+               retval = []
+
+               for channel_type in self._requestable_channel_classes:
+                       retval.append((self._fixed_properties[channel_type],
+                               self._available_properties[channel_type]))
+
+               return retval
+
+
+class ChannelManager(TelepathyChannelManager):
+
+       def __init__(self, connection):
+               TelepathyChannelManager.__init__(self, connection)
+
+               fixed = {
+                       telepathy.CHANNEL_INTERFACE + '.ChannelType': telepathy.CHANNEL_TYPE_TEXT,
+                       telepathy.CHANNEL_INTERFACE + '.TargetHandleType': dbus.UInt32(telepathy.HANDLE_TYPE_CONTACT)
+               }
+               self._implement_channel_class(
+                       telepathy.CHANNEL_TYPE_TEXT,
+                       self._get_text_channel,
+                       fixed,
+                       []
+               )
+
+               fixed = {
+                       telepathy.CHANNEL_INTERFACE + '.ChannelType': telepathy.CHANNEL_TYPE_CONTACT_LIST
+               }
+               self._implement_channel_class(
+                       telepathy.CHANNEL_TYPE_CONTACT_LIST,
+                       self._get_list_channel,
+                       fixed,
+                       []
+               )
+
+               fixed = {
+                       telepathy.CHANNEL_INTERFACE + '.ChannelType': telepathy.CHANNEL_TYPE_STREAMED_MEDIA,
+                       telepathy.CHANNEL_INTERFACE + '.TargetHandleType': dbus.UInt32(telepathy.HANDLE_TYPE_CONTACT)
+               }
+               self._implement_channel_class(
+                       telepathy.CHANNEL_TYPE_STREAMED_MEDIA,
+                       self._get_media_channel,
+                       fixed,
+                       [telepathy.CHANNEL_INTERFACE + '.TargetHandle']
+               )
+
+       def _get_list_channel(self, props):
+               _, surpress_handler, handle = self._get_type_requested_handle(props)
+
+               _moduleLogger.debug('New contact list channel')
+               chan = channel.contact_list.create_contact_list_channel(self._conn, self, props, handle)
                return chan
 
-       def channel_for_call(self, handle, suppress_handler=False):
-               try:
-                       chan = self._callChannels[handle]
-               except KeyError, e:
-                       if handle.get_type() != telepathy.HANDLE_TYPE_CONTACT:
-                               _moduleLogger.warning("Using deprecated means to create a call")
-                               raise telepathy.errors.NotImplemented("Not implementing depcrecated means")
-                       _moduleLogger.debug("Requesting new call channel")
-
-                       chan = channel.call.CallChannel(self._connRef(), handle)
-                       self._callChannels[handle] = chan
-                       self._connRef().add_channel(chan, handle, suppress_handler)
+       def _get_text_channel(self, props):
+               _, surpress_handler, handle = self._get_type_requested_handle(props)
+
+               _moduleLogger.debug('New text channel')
+               chan = channel.text.TextChannel(self._conn, self, props, handle)
+               return chan
+
+       def _get_media_channel(self, props):
+               _, surpress_handler, handle = self._get_type_requested_handle(props)
+
+               _moduleLogger.debug('New media channel')
+               chan = channel.call.CallChannel(self._conn, self, props, handle)
                return chan
index 8c29f83..a307e6f 100644 (file)
@@ -176,24 +176,16 @@ class TheOneRingConnection(
                self.check_connected()
                self.check_handle(handleType, handleId)
 
-               channel = None
-               channelManager = self._channelManager
-               handle = self.handle(handleType, handleId)
-
-               if type == telepathy.CHANNEL_TYPE_CONTACT_LIST:
-                       _moduleLogger.info("RequestChannel ContactList")
-                       channel = channelManager.channel_for_list(handle, suppressHandler)
-               elif type == telepathy.CHANNEL_TYPE_TEXT:
-                       _moduleLogger.info("RequestChannel Text")
-                       channel = channelManager.channel_for_text(handle, suppressHandler)
-               elif type == telepathy.CHANNEL_TYPE_STREAMED_MEDIA:
-                       _moduleLogger.info("RequestChannel Media")
-                       channel = channelManager.channel_for_call(handle, suppressHandler)
-               else:
-                       raise telepathy.errors.NotImplemented("unknown channel type %s" % type)
+               h = self.handle(handleType, handleId) if handleId != 0 else None
+               props = self._generate_props(type, h, suppressHandler)
+               if hasattr(self, "_validate_handle"):
+                       # On newer python-telepathy
+                       self._validate_handle(props)
 
-               _moduleLogger.info("RequestChannel Object Path: %s" % channel._object_path)
-               return channel._object_path
+               chan = self._channelManager.channel_for_props(props, signal=True)
+               path = chan._object_path
+               _moduleLogger.info("RequestChannel Object Path: %s" % path)
+               return path
 
        @gtk_toolbox.log_exception(_moduleLogger)
        def RequestHandles(self, handleType, names, sender):
@@ -244,8 +236,8 @@ class TheOneRingConnection(
                # @todo get conversations update running
                # @todo test conversatiuons
                _moduleLogger.info("Incoming messages from: %r" % (conversationIds, ))
-               channelManager = self._channelManager
                for contactId, phoneNumber in conversationIds:
                        h = handle.create_handle(self, 'contact', contactId, phoneNumber)
                        # Just let the TextChannel decide whether it should be reported to the user or not
-                       channel = channelManager.channel_for_text(h)
+                       props = self._generate_props(telepathy.CHANNEL_TYPE_TEXT, h, False)
+                       channel = self._channelManager.channel_for_props(props, signal=True)