From: Ed Page Date: Fri, 12 Feb 2010 02:53:07 +0000 (-0600) Subject: Async connect/disconnect. Moved everything to a new Async and Timeout which cleans... X-Git-Url: http://git.maemo.org/git/?p=theonering;a=commitdiff_plain;h=1c892d1b9bf14b28eb54ce3590ed2ee29d5e3d25 Async connect/disconnect. Moved everything to a new Async and Timeout which cleans up common idle/timeout handling. Got rid of gtk_toolbox --- diff --git a/src/aliasing.py b/src/aliasing.py index 96c4dc3..22fa414 100644 --- a/src/aliasing.py +++ b/src/aliasing.py @@ -3,8 +3,7 @@ import logging import telepathy import tp -import gtk_toolbox -import util.misc as util_misc +import util.misc as misc_utils import handle @@ -72,7 +71,7 @@ def make_pretty(phonenumber): if phonenumber is None or phonenumber is "": return "" - phonenumber = util_misc.normalize_number(phonenumber) + phonenumber = misc_utils.normalize_number(phonenumber) if phonenumber[0] == "+": prettynumber = _make_pretty_international(phonenumber[1:]) @@ -114,16 +113,16 @@ class AliasingMixin(tp.ConnectionInterfaceAliasing): """ raise NotImplementedError("Abstract function called") - @gtk_toolbox.log_exception(_moduleLogger) + @misc_utils.log_exception(_moduleLogger) def GetAliasFlags(self): return 0 - @gtk_toolbox.log_exception(_moduleLogger) + @misc_utils.log_exception(_moduleLogger) def RequestAliases(self, contactHandleIds): _moduleLogger.debug("Called RequestAliases") return [self._get_alias(handleId) for handleId in contactHandleIds] - @gtk_toolbox.log_exception(_moduleLogger) + @misc_utils.log_exception(_moduleLogger) def GetAliases(self, contactHandleIds): _moduleLogger.debug("Called GetAliases") @@ -133,7 +132,7 @@ class AliasingMixin(tp.ConnectionInterfaceAliasing): ) return idToAlias - @gtk_toolbox.log_exception(_moduleLogger) + @misc_utils.log_exception(_moduleLogger) def SetAliases(self, aliases): _moduleLogger.debug("Called SetAliases") # first validate that no other handle types are included @@ -144,11 +143,11 @@ class AliasingMixin(tp.ConnectionInterfaceAliasing): else: raise telepathy.errors.PermissionDenied("No user customizable aliases") - uglyNumber = util_misc.normalize_number(alias) + uglyNumber = misc_utils.normalize_number(alias) if len(uglyNumber) == 0: # Reset to the original from login if one was provided uglyNumber = self.callbackNumberParameter - if not util_misc.is_valid_number(uglyNumber): + if not misc_utils.is_valid_number(uglyNumber): raise telepathy.errors.InvalidArgument("Invalid phone number %r" % (uglyNumber, )) # Update callback diff --git a/src/autogv.py b/src/autogv.py index d18b28b..1955e8b 100644 --- a/src/autogv.py +++ b/src/autogv.py @@ -19,7 +19,7 @@ import constants import util.coroutines as coroutines import util.go_utils as gobject_utils import util.tp_utils as telepathy_utils -import gtk_toolbox +import util.misc as misc_utils _moduleLogger = logging.getLogger("autogv") @@ -56,7 +56,7 @@ class NewGVConversations(object): ) self.__callback = None - @gtk_toolbox.log_exception(_moduleLogger) + @misc_utils.log_exception(_moduleLogger) def _on_conversations_updated(self, conv, conversationIds): _moduleLogger.debug("Incoming messages from: %r" % (conversationIds, )) for phoneNumber in conversationIds: @@ -108,7 +108,7 @@ class RefreshVoicemail(object): self._isStarted = False - @gtk_toolbox.log_exception(_moduleLogger) + @misc_utils.log_exception(_moduleLogger) def _on_new_channel(self, bus, serviceName, connObjectPath, channelObjectPath, channelType): if channelType != telepathy.interfaces.CHANNEL_TYPE_STREAMED_MEDIA: return @@ -125,13 +125,13 @@ class RefreshVoicemail(object): ) self._outstandingRequests.append(missDetection) - @gtk_toolbox.log_exception(_moduleLogger) + @misc_utils.log_exception(_moduleLogger) def _on_missed_call(self, missDetection): _moduleLogger.info("Missed a call") self._connRef().session.voicemailsStateMachine.reset_timers() self._outstandingRequests.remove(missDetection) - @gtk_toolbox.log_exception(_moduleLogger) + @misc_utils.log_exception(_moduleLogger) def _on_error_for_missed(self, missDetection, reason): _moduleLogger.debug("Error: %r claims %r" % (missDetection, reason)) self._outstandingRequests.remove(missDetection) @@ -147,7 +147,7 @@ class AutoDisconnect(object): self.__connection = None self.__connectionEventId = None - self.__delayedDisconnectEventId = None + self.__delayedDisconnect = gobject_utils.Timeout(self._on_delayed_disconnect) def start(self): if self.__connection is not None: @@ -156,7 +156,7 @@ class AutoDisconnect(object): def stop(self): self._cancel_delayed_disconnect() - @gtk_toolbox.log_exception(_moduleLogger) + @misc_utils.log_exception(_moduleLogger) def _on_connection_change(self, connection, event): """ @note Maemo specific @@ -168,9 +168,7 @@ class AutoDisconnect(object): if status == conic.STATUS_DISCONNECTED: _moduleLogger.info("Disconnected from network, starting countdown to logoff") - self.__delayedDisconnectEventId = gobject_utils.timeout_add_seconds( - 5, self._on_delayed_disconnect - ) + self.__delayedDisconnect.start(seconds=5) elif status == conic.STATUS_CONNECTED: _moduleLogger.info("Connected to network") self._cancel_delayed_disconnect() @@ -178,13 +176,10 @@ class AutoDisconnect(object): _moduleLogger.info("Other status: %r" % (status, )) def _cancel_delayed_disconnect(self): - if self.__delayedDisconnectEventId is None: - return _moduleLogger.info("Cancelling auto-log off") - gobject.source_reove(self.__delayedDisconnectEventId) - self.__delayedDisconnectEventId = None + self.__delayedDisconnect.cancel() - @gtk_toolbox.log_exception(_moduleLogger) + @misc_utils.log_exception(_moduleLogger) def _on_delayed_disconnect(self): if not self.session.is_logged_in(): _moduleLogger.info("Received connection change event when not logged in") @@ -193,8 +188,6 @@ class AutoDisconnect(object): self._connRef().disconnect(telepathy.CONNECTION_STATUS_REASON_NETWORK_ERROR) except Exception: _moduleLogger.exception("Error durring disconnect") - self.__delayedDisconnectEventId = None - return False class DisconnectOnShutdown(object): @@ -229,7 +222,7 @@ class DisconnectOnShutdown(object): pass # Either None or close was removed (in Fremantle) self._osso = None - @gtk_toolbox.log_exception(_moduleLogger) + @misc_utils.log_exception(_moduleLogger) def _on_device_state_change(self, shutdown, save_unsaved_data, memory_low, system_inactivity, message, userData): """ @note Hildon specific diff --git a/src/capabilities.py b/src/capabilities.py index 34872a6..7474eb0 100644 --- a/src/capabilities.py +++ b/src/capabilities.py @@ -3,7 +3,7 @@ import logging import telepathy import tp -import gtk_toolbox +import util.misc as misc_utils _moduleLogger = logging.getLogger('capabilities') @@ -32,7 +32,7 @@ class CapabilitiesMixin(tp.ConnectionInterfaceCapabilities): """ raise NotImplementedError("Abstract function called") - @gtk_toolbox.log_exception(_moduleLogger) + @misc_utils.log_exception(_moduleLogger) def GetCapabilities(self, handleIds): ret = [] for handleId in handleIds: diff --git a/src/channel/call.py b/src/channel/call.py index 5ab9a0b..0999a1a 100644 --- a/src/channel/call.py +++ b/src/channel/call.py @@ -5,7 +5,8 @@ import gobject import telepathy import tp -import gtk_toolbox +import util.go_utils as gobject_utils +import util.misc as misc_utils _moduleLogger = logging.getLogger("channel.call") @@ -20,7 +21,7 @@ class CallChannel( def __init__(self, connection, manager, props, contactHandle): self.__manager = manager self.__props = props - self.__cancelId = None + self._delayedCancel = gobject_utils.Async(self._on_cancel) if telepathy.interfaces.CHANNEL_INTERFACE + '.InitiatorHandle' in props: self._initiator = connection.get_handle_by_id( @@ -87,7 +88,7 @@ class CallChannel( def initial_video(self): return False - @gtk_toolbox.log_exception(_moduleLogger) + @misc_utils.log_exception(_moduleLogger) def Close(self): self.close() @@ -95,47 +96,45 @@ class CallChannel( _moduleLogger.debug("Closing call") tp.ChannelTypeStreamedMedia.Close(self) self.remove_from_connection() - if self.__cancelId is not None: - gobject.source_remove(self.__cancelId) - self.__cancelId = None + self._delayedCancel.cancel() - @gtk_toolbox.log_exception(_moduleLogger) + @misc_utils.log_exception(_moduleLogger) def GetLocalPendingMembersWithInfo(self): info = dbus.Array([], signature="(uuus)") for member in self._local_pending: info.append((member, self._handle, 0, '')) return info - @gtk_toolbox.log_exception(_moduleLogger) + @misc_utils.log_exception(_moduleLogger) def AddMembers(self, handles, message): _moduleLogger.info("Add members %r: %s" % (handles, message)) for handle in handles: if handle == int(self.GetSelfHandle()) and self.GetSelfHandle() in self._local_pending: _moduleLogger.info("Technically the user just accepted the call") - @gtk_toolbox.log_exception(_moduleLogger) + @misc_utils.log_exception(_moduleLogger) def RemoveMembers(self, handles, message): _moduleLogger.info("Remove members (no-op) %r: %s" % (handles, message)) - @gtk_toolbox.log_exception(_moduleLogger) + @misc_utils.log_exception(_moduleLogger) def RemoveMembersWithReason(self, handles, message, reason): _moduleLogger.info("Remove members (no-op) %r: %s (%i)" % (handles, message, reason)) - @gtk_toolbox.log_exception(_moduleLogger) + @misc_utils.log_exception(_moduleLogger) def ListStreams(self): """ For org.freedesktop.Telepathy.Channel.Type.StreamedMedia """ return () - @gtk_toolbox.log_exception(_moduleLogger) + @misc_utils.log_exception(_moduleLogger) def RemoveStreams(self, streams): """ For org.freedesktop.Telepathy.Channel.Type.StreamedMedia """ raise telepathy.errors.NotImplemented("Cannot remove a stream") - @gtk_toolbox.log_exception(_moduleLogger) + @misc_utils.log_exception(_moduleLogger) def RequestStreamDirection(self, stream, streamDirection): """ For org.freedesktop.Telepathy.Channel.Type.StreamedMedia @@ -145,7 +144,7 @@ class CallChannel( _moduleLogger.info("A request was made to change the stream direction") raise telepathy.errors.NotImplemented("Cannot change directions") - @gtk_toolbox.log_exception(_moduleLogger) + @misc_utils.log_exception(_moduleLogger) def RequestStreams(self, contactId, streamTypes): """ For org.freedesktop.Telepathy.Channel.Type.StreamedMedia @@ -157,7 +156,7 @@ class CallChannel( contactNumber = contact.phoneNumber self.CallStateChanged(self.__contactHandle, telepathy.constants.CHANNEL_CALL_STATE_RINGING) - self.__cancelId = gobject.idle_add(self._on_cancel) + self._delayedCancel.start() self._conn.session.backend.call(contactNumber) streamId = 0 @@ -166,7 +165,7 @@ class CallChannel( pendingSendFlags = telepathy.constants.MEDIA_STREAM_PENDING_REMOTE_SEND return [(streamId, contact, streamTypes[0], streamState, streamDirection, pendingSendFlags)] - @gtk_toolbox.log_exception(_moduleLogger) + @misc_utils.log_exception(_moduleLogger) def GetCallStates(self): """ For org.freedesktop.Telepathy.Channel.Interface.CallState @@ -176,9 +175,7 @@ class CallChannel( """ return {self.__contactHandle: telepathy.constants.CHANNEL_CALL_STATE_FORWARDED} - @gtk_toolbox.log_exception(_moduleLogger) + @misc_utils.log_exception(_moduleLogger) def _on_cancel(self, *args): self.CallStateChanged(self.__contactHandle, telepathy.constants.CHANNEL_CALL_STATE_FORWARDED) self.close() - self.__cancelId = None - return False diff --git a/src/channel/contact_list.py b/src/channel/contact_list.py index 8409991..e184d2d 100644 --- a/src/channel/contact_list.py +++ b/src/channel/contact_list.py @@ -4,7 +4,7 @@ import telepathy import tp import util.coroutines as coroutines -import gtk_toolbox +import util.misc as misc_utils import handle @@ -48,7 +48,7 @@ class AllContactsListChannel( self.GroupFlagsChanged(0, 0) - @gtk_toolbox.log_exception(_moduleLogger) + @misc_utils.log_exception(_moduleLogger) def Close(self): self.close() @@ -63,11 +63,11 @@ class AllContactsListChannel( tp.ChannelTypeContactList.Close(self) self.remove_from_connection() - @gtk_toolbox.log_exception(_moduleLogger) + @misc_utils.log_exception(_moduleLogger) def GetLocalPendingMembersWithInfo(self): return [] - @gtk_toolbox.log_exception(_moduleLogger) + @misc_utils.log_exception(_moduleLogger) def _on_contacts_refreshed(self, addressbook, added, removed, changed): self._process_refresh(addressbook, added, removed, changed) diff --git a/src/channel/debug_log.py b/src/channel/debug_log.py index 2a1c188..049754a 100644 --- a/src/channel/debug_log.py +++ b/src/channel/debug_log.py @@ -7,7 +7,7 @@ import telepathy import constants import tp -import gtk_toolbox +import util.misc as misc_utils _moduleLogger = logging.getLogger("channel.debug_log") @@ -79,7 +79,7 @@ class DebugLogChannel(tp.ChannelTypeFileTransfer): def get_initial_offset(self): return 0 - @gtk_toolbox.log_exception(_moduleLogger) + @misc_utils.log_exception(_moduleLogger) def AcceptFile(self, addressType, accessControl, accessControlParam, offset): _moduleLogger.info("%r %r %r %r" % (addressType, accessControl, accessControlParam, offset)) self.InitialOffsetDefined(0) @@ -111,11 +111,11 @@ class DebugLogChannel(tp.ChannelTypeFileTransfer): telepathy.constants.FILE_TRANSFER_STATE_CHANGE_REASON_NONE, ) - @gtk_toolbox.log_exception(_moduleLogger) + @misc_utils.log_exception(_moduleLogger) def ProvideFile(self, addressType, accessControl, accessControlParam): raise telepathy.errors.NotImplemented("Cannot send outbound files") - @gtk_toolbox.log_exception(_moduleLogger) + @misc_utils.log_exception(_moduleLogger) def Close(self): self.close() diff --git a/src/channel/debug_prompt.py b/src/channel/debug_prompt.py index 6b7cebc..9db5554 100644 --- a/src/channel/debug_prompt.py +++ b/src/channel/debug_prompt.py @@ -11,7 +11,7 @@ import telepathy import constants import tp -import gtk_toolbox +import util.misc as misc_utils import gvoice @@ -32,7 +32,7 @@ class DebugPromptChannel(tp.ChannelTypeText, cmd.Cmd): self.__otherHandle = contactHandle - @gtk_toolbox.log_exception(_moduleLogger) + @misc_utils.log_exception(_moduleLogger) def Send(self, messageType, text): if messageType != telepathy.CHANNEL_TEXT_MESSAGE_TYPE_NORMAL: raise telepathy.errors.NotImplemented("Unhandled message type: %r" % messageType) @@ -51,7 +51,7 @@ class DebugPromptChannel(tp.ChannelTypeText, cmd.Cmd): if stdoutData: self._report_new_message(stdoutData) - @gtk_toolbox.log_exception(_moduleLogger) + @misc_utils.log_exception(_moduleLogger) def Close(self): self.close() diff --git a/src/channel/text.py b/src/channel/text.py index c7a7da5..d4cb5c0 100644 --- a/src/channel/text.py +++ b/src/channel/text.py @@ -6,7 +6,7 @@ import telepathy import tp import util.coroutines as coroutines -import gtk_toolbox +import util.misc as misc_utils _moduleLogger = logging.getLogger("channel.text") @@ -53,7 +53,7 @@ class TextChannel(tp.ChannelTypeText): else: self._report_conversation(mergedConversations) - @gtk_toolbox.log_exception(_moduleLogger) + @misc_utils.log_exception(_moduleLogger) def Send(self, messageType, text): if messageType != telepathy.CHANNEL_TEXT_MESSAGE_TYPE_NORMAL: raise telepathy.errors.NotImplemented("Unhandled message type: %r" % messageType) @@ -79,7 +79,7 @@ class TextChannel(tp.ChannelTypeText): self.Sent(int(time.time()), messageType, text) - @gtk_toolbox.log_exception(_moduleLogger) + @misc_utils.log_exception(_moduleLogger) def Close(self): self.close() @@ -101,7 +101,7 @@ class TextChannel(tp.ChannelTypeText): contactKey = self.__otherHandle.phoneNumber return contactKey - @gtk_toolbox.log_exception(_moduleLogger) + @misc_utils.log_exception(_moduleLogger) def _on_conversations_updated(self, conv, conversationIds): if self._contactKey not in conversationIds: return diff --git a/src/channel_manager.py b/src/channel_manager.py index 04faca1..ee844c0 100644 --- a/src/channel_manager.py +++ b/src/channel_manager.py @@ -5,7 +5,7 @@ import telepathy import tp import channel -import util.misc as util_misc +import util.misc as misc_utils _moduleLogger = logging.getLogger("channel_manager") @@ -69,7 +69,7 @@ class ChannelManager(tp.ChannelManager): def _get_text_channel(self, props): _, surpress_handler, h = self._get_type_requested_handle(props) - accountNumber = util_misc.normalize_number(self._conn.session.backend.get_account_number()) + accountNumber = misc_utils.normalize_number(self._conn.session.backend.get_account_number()) if h.phoneNumber == accountNumber: _moduleLogger.debug('New Debug channel') chan = channel.debug_prompt.DebugPromptChannel(self._conn, self, props, h) diff --git a/src/connection.py b/src/connection.py index 73ec4a4..88a02e6 100644 --- a/src/connection.py +++ b/src/connection.py @@ -6,8 +6,8 @@ import telepathy import constants import tp -import util.misc as util_misc -import gtk_toolbox +import util.go_utils as gobject_utils +import util.misc as misc_utils import gvoice import handle @@ -82,14 +82,14 @@ class TheOneRingConnection( "password", )) - @gtk_toolbox.log_exception(_moduleLogger) + @misc_utils.log_exception(_moduleLogger) def __init__(self, manager, parameters): self.check_parameters(parameters) account = unicode(parameters['account']) encodedAccount = parameters['account'].encode('utf-8') encodedPassword = parameters['password'].encode('utf-8') - encodedCallback = util_misc.normalize_number(parameters['forward'].encode('utf-8')) - if encodedCallback and not util_misc.is_valid_number(encodedCallback): + encodedCallback = misc_utils.normalize_number(parameters['forward'].encode('utf-8')) + if encodedCallback and not misc_utils.is_valid_number(encodedCallback): raise telepathy.errors.InvalidArgument("Invalid forwarding number") # Connection init must come first @@ -136,6 +136,7 @@ class TheOneRingConnection( autogv.RefreshVoicemail(weakref.ref(self)), autogv.AutoDisconnect(weakref.ref(self)), ] + self._delayedConnect = gobject_utils.Async(self._delayed_connect) _moduleLogger.info("Connection to the account %s created" % account) @@ -176,11 +177,19 @@ class TheOneRingConnection( def _channel_manager(self): return self.__channelManager - @gtk_toolbox.log_exception(_moduleLogger) + @misc_utils.log_exception(_moduleLogger) def Connect(self): """ For org.freedesktop.telepathy.Connection """ + if self._status != telepathy.CONNECTION_STATUS_DISCONNECTED: + _moduleLogger.info("Attempting connect when not disconnected") + return + _moduleLogger.info("Kicking off connect") + self._delayedConnect.start() + + @misc_utils.log_exception(_moduleLogger) + def _delayed_connect(self): _moduleLogger.info("Connecting...") self.StatusChanged( telepathy.CONNECTION_STATUS_CONNECTING, @@ -196,7 +205,7 @@ class TheOneRingConnection( callback = gvoice.backend.get_sane_callback( self.session.backend ) - self.__callbackNumberParameter = util_misc.normalize_number(callback) + self.__callbackNumberParameter = misc_utils.normalize_number(callback) self.session.backend.set_callback_number(self.__callbackNumberParameter) subscribeHandle = self.get_handle_by_name(telepathy.HANDLE_TYPE_LIST, "subscribe") @@ -220,17 +229,15 @@ class TheOneRingConnection( telepathy.CONNECTION_STATUS_REASON_REQUESTED ) - @gtk_toolbox.log_exception(_moduleLogger) + @misc_utils.log_exception(_moduleLogger) def Disconnect(self): """ For org.freedesktop.telepathy.Connection """ - try: - self.disconnect(telepathy.CONNECTION_STATUS_REASON_REQUESTED) - except Exception: - _moduleLogger.exception("Error durring disconnect") + _moduleLogger.info("Kicking off disconnect") + self._delayed_disconnect() - @gtk_toolbox.log_exception(_moduleLogger) + @misc_utils.log_exception(_moduleLogger) def RequestChannel(self, type, handleType, handleId, suppressHandler): """ For org.freedesktop.telepathy.Connection @@ -267,8 +274,16 @@ class TheOneRingConnection( return props + @gobject_utils.async + def _delayed_disconnect(self): + self.disconnect(telepathy.CONNECTION_STATUS_REASON_REQUESTED) + return False + def disconnect(self, reason): _moduleLogger.info("Disconnecting") + + self._delayedConnect.cancel() + # Not having the disconnect first can cause weird behavior with clients # including not being able to reconnect or even crashing self.StatusChanged( diff --git a/src/connection_manager.py b/src/connection_manager.py index 92a12ae..64b6bf8 100644 --- a/src/connection_manager.py +++ b/src/connection_manager.py @@ -1,12 +1,11 @@ import logging -import gobject import telepathy import constants import tp -import gtk_toolbox import util.go_utils as gobject_utils +import util.misc as misc_utils import connection @@ -23,7 +22,7 @@ class TheOneRingConnectionManager(tp.ConnectionManager): self._on_shutdown = shutdown_func _moduleLogger.info("Connection manager created") - @gtk_toolbox.log_exception(_moduleLogger) + @misc_utils.log_exception(_moduleLogger) def GetParameters(self, proto): """ For org.freedesktop.telepathy.ConnectionManager @@ -75,7 +74,7 @@ class TheOneRingConnectionManager(tp.ConnectionManager): connection.Disconnect() _moduleLogger.info("Connection manager quitting") - @gtk_toolbox.log_exception(_moduleLogger) + @misc_utils.log_exception(_moduleLogger) def _shutdown(self): if ( self._on_shutdown is not None and diff --git a/src/contacts.py b/src/contacts.py index 8a7d16b..fd41265 100644 --- a/src/contacts.py +++ b/src/contacts.py @@ -3,6 +3,8 @@ import logging import dbus import telepathy +import util.misc as misc_utils + _moduleLogger = logging.getLogger('contacts') @@ -32,6 +34,7 @@ class ContactsMixin(telepathy.server.ConnectionInterfaceContacts): raise NotImplementedError("Abstract function called") # Overwrite the dbus attribute to get the sender argument + @misc_utils.log_exception(_moduleLogger) @dbus.service.method(telepathy.CONNECTION_INTERFACE_CONTACTS, in_signature='auasb', out_signature='a{ua{sv}}', sender_keyword='sender') def GetContactAttributes(self, handles, interfaces, hold, sender): diff --git a/src/gtk_toolbox.py b/src/gtk_toolbox.py deleted file mode 100644 index 09ce9fe..0000000 --- a/src/gtk_toolbox.py +++ /dev/null @@ -1,280 +0,0 @@ -#!/usr/bin/python - -from __future__ import with_statement - -import os -import errno -import time -import functools -import contextlib -import logging -import threading -import Queue - - -_moduleLogger = logging.getLogger("gtk_toolbox") - - -@contextlib.contextmanager -def flock(path, timeout=-1): - WAIT_FOREVER = -1 - DELAY = 0.1 - timeSpent = 0 - - acquired = False - - while timeSpent <= timeout or timeout == WAIT_FOREVER: - try: - fd = os.open(path, os.O_CREAT | os.O_EXCL | os.O_RDWR) - acquired = True - break - except OSError, e: - if e.errno != errno.EEXIST: - raise - time.sleep(DELAY) - timeSpent += DELAY - - assert acquired, "Failed to grab file-lock %s within timeout %d" % (path, timeout) - - try: - yield fd - finally: - os.unlink(path) - - -def make_idler(func): - """ - Decorator that makes a generator-function into a function that will continue execution on next call - """ - a = [] - - @functools.wraps(func) - def decorated_func(*args, **kwds): - if not a: - a.append(func(*args, **kwds)) - try: - a[0].next() - return True - except StopIteration: - del a[:] - return False - - return decorated_func - - -def autostart(func): - """ - >>> @autostart - ... def grep_sink(pattern): - ... print "Looking for %s" % pattern - ... while True: - ... line = yield - ... if pattern in line: - ... print line, - >>> g = grep_sink("python") - Looking for python - >>> g.send("Yeah but no but yeah but no") - >>> g.send("A series of tubes") - >>> g.send("python generators rock!") - python generators rock! - >>> g.close() - """ - - @functools.wraps(func) - def start(*args, **kwargs): - cr = func(*args, **kwargs) - cr.next() - return cr - - return start - - -@autostart -def printer_sink(format = "%s"): - """ - >>> pr = printer_sink("%r") - >>> pr.send("Hello") - 'Hello' - >>> pr.send("5") - '5' - >>> pr.send(5) - 5 - >>> p = printer_sink() - >>> p.send("Hello") - Hello - >>> p.send("World") - World - >>> # p.throw(RuntimeError, "Goodbye") - >>> # p.send("Meh") - >>> # p.close() - """ - while True: - item = yield - print format % (item, ) - - -@autostart -def null_sink(): - """ - Good for uses like with cochain to pick up any slack - """ - while True: - item = yield - - -@autostart -def comap(function, target): - """ - >>> p = printer_sink() - >>> cm = comap(lambda x: x+1, p) - >>> cm.send((0, )) - 1 - >>> cm.send((1.0, )) - 2.0 - >>> cm.send((-2, )) - -1 - """ - while True: - try: - item = yield - mappedItem = function(*item) - target.send(mappedItem) - except Exception, e: - _moduleLogger.exception("Forwarding exception!") - target.throw(e.__class__, str(e)) - - -def _flush_queue(queue): - while not queue.empty(): - yield queue.get() - - -@autostart -def queue_sink(queue): - """ - >>> q = Queue.Queue() - >>> qs = queue_sink(q) - >>> qs.send("Hello") - >>> qs.send("World") - >>> qs.throw(RuntimeError, "Goodbye") - >>> qs.send("Meh") - >>> qs.close() - >>> print [i for i in _flush_queue(q)] - [(None, 'Hello'), (None, 'World'), (, 'Goodbye'), (None, 'Meh'), (, None)] - """ - while True: - try: - item = yield - queue.put((None, item)) - except Exception, e: - queue.put((e.__class__, str(e))) - except GeneratorExit: - queue.put((GeneratorExit, None)) - raise - - -def decode_item(item, target): - if item[0] is None: - target.send(item[1]) - return False - elif item[0] is GeneratorExit: - target.close() - return True - else: - target.throw(item[0], item[1]) - return False - - -def nonqueue_source(queue, target): - isDone = False - while not isDone: - item = queue.get() - isDone = decode_item(item, target) - while not queue.empty(): - queue.get_nowait() - - -def threaded_stage(target, thread_factory = threading.Thread): - messages = Queue.Queue() - - run_source = functools.partial(nonqueue_source, messages, target) - thread = thread_factory(target=run_source) - thread.setDaemon(True) - thread.start() - - # Sink running in current thread - return queue_sink(messages) - - -def safecall(f, errorDisplay=None, default=None, exception=Exception): - ''' - Returns modified f. When the modified f is called and throws an - exception, the default value is returned - ''' - def _safecall(*args, **argv): - try: - return f(*args, **argv) - except exception, e: - if errorDisplay is not None: - errorDisplay.push_exception(e) - return default - return _safecall - - -def log_call(logger): - - def log_call_decorator(func): - - @functools.wraps(func) - def wrapper(*args, **kwds): - _moduleLogger.info("-> %s" % (func.__name__, )) - try: - return func(*args, **kwds) - finally: - _moduleLogger.info("<- %s" % (func.__name__, )) - - return wrapper - - return log_call_decorator - - -def log_exception(logger): - - def log_exception_decorator(func): - - @functools.wraps(func) - def wrapper(*args, **kwds): - try: - return func(*args, **kwds) - except Exception: - logger.exception(func.__name__) - raise - - return wrapper - - return log_exception_decorator - - -_indentationLevel = [0] - - -def trace(logger): - - def trace_decorator(func): - - @functools.wraps(func) - def wrapper(*args, **kwds): - try: - logger.debug("%s> %s" % (" " * _indentationLevel[0], func.__name__, )) - _indentationLevel[0] += 1 - return func(*args, **kwds) - except Exception: - logger.exception(func.__name__) - raise - finally: - _indentationLevel[0] -= 1 - logger.debug("%s< %s" % (" " * _indentationLevel[0], func.__name__, )) - - return wrapper - - return trace_decorator diff --git a/src/gvoice/addressbook.py b/src/gvoice/addressbook.py index 9071b19..f5fa02e 100644 --- a/src/gvoice/addressbook.py +++ b/src/gvoice/addressbook.py @@ -4,7 +4,7 @@ import logging import util.coroutines as coroutines -import util.misc as util_misc +import util.misc as misc_utils _moduleLogger = logging.getLogger("gvoice.addressbook") @@ -72,7 +72,7 @@ class Addressbook(object): contactName = contactDetails["name"] contactNumbers = ( ( - util_misc.normalize_number(numberDetails["phoneNumber"]), + misc_utils.normalize_number(numberDetails["phoneNumber"]), numberDetails.get("phoneType", "Mobile"), ) for numberDetails in contactDetails["numbers"] diff --git a/src/gvoice/conversations.py b/src/gvoice/conversations.py index 7fbe1ca..4ccce3a 100644 --- a/src/gvoice/conversations.py +++ b/src/gvoice/conversations.py @@ -12,7 +12,7 @@ except ImportError: import constants import util.coroutines as coroutines -import util.misc as util_misc +import util.misc as misc_utils _moduleLogger = logging.getLogger("gvoice.conversations") @@ -64,7 +64,7 @@ class Conversations(object): conversations = list(self._get_raw_conversations()) conversations.sort() for conversation in conversations: - key = util_misc.normalize_number(conversation.number) + key = misc_utils.normalize_number(conversation.number) try: mergedConversations = self._conversations[key] except KeyError: diff --git a/src/gvoice/state_machine.py b/src/gvoice/state_machine.py index bb58c7e..4dcd809 100644 --- a/src/gvoice/state_machine.py +++ b/src/gvoice/state_machine.py @@ -6,7 +6,7 @@ import gobject import util.go_utils as gobject_utils import util.coroutines as coroutines -import gtk_toolbox +import util.misc as misc_utils _moduleLogger = logging.getLogger("gvoice.state_machine") @@ -228,7 +228,7 @@ class UpdateStateMachine(StateMachine): self._maxTime = maxTime self._state = self.STATE_ACTIVE - self._timeoutId = None + self._onTimeout = gobject_utils.Timeout(self._on_timeout) self._strategies = {} self._callback = coroutines.func_sink( @@ -247,19 +247,18 @@ class UpdateStateMachine(StateMachine): self._strategies[state] = strategy def start(self): - assert self._timeoutId is None for strategy in self._strategies.itervalues(): strategy.initialize_state() if self._strategy.timeout != self.INFINITE_PERIOD: - self._timeoutId = gobject.idle_add(self._on_timeout) + self._onTimeout.start(seconds=0) _moduleLogger.info("%s Starting State Machine" % (self._name, )) def stop(self): _moduleLogger.info("%s Stopping State Machine" % (self._name, )) - self._stop_update() + self._onTimeout.cancel() def close(self): - assert self._timeoutId is None + self._onTimeout.cancel() self._callback = None def set_state(self, newState): @@ -290,7 +289,7 @@ class UpdateStateMachine(StateMachine): def maxTime(self): return self._maxTime - @gtk_toolbox.log_exception(_moduleLogger) + @misc_utils.log_exception(_moduleLogger) def _request_reset_timers(self, *args): self._reset_timers() @@ -298,37 +297,28 @@ class UpdateStateMachine(StateMachine): if self._timeoutId is None: return # not started yet _moduleLogger.info("%s Resetting State Machine" % (self._name, )) - self._stop_update() + self._onTimeout.cancel() if initialize: self._strategy.initialize_state() else: self._strategy.reinitialize_state() self._schedule_update() - def _stop_update(self): - if self._timeoutId is None: - return - gobject.source_remove(self._timeoutId) - self._timeoutId = None - def _schedule_update(self): - assert self._timeoutId is None self._strategy.increment_state() nextTimeout = self._strategy.timeout if nextTimeout != self.INFINITE_PERIOD and nextTimeout < self._maxTime: assert 0 < nextTimeout - self._timeoutId = gobject_utils.timeout_add_seconds(nextTimeout, self._on_timeout) + self._onTimeout.start(seconds=nextTimeout) _moduleLogger.info("%s Next update in %s seconds" % (self._name, nextTimeout, )) else: _moduleLogger.info("%s No further updates (timeout is %s seconds)" % (self._name, nextTimeout, )) - @gtk_toolbox.log_exception(_moduleLogger) + @misc_utils.log_exception(_moduleLogger) def _on_timeout(self): - self._timeoutId = None self._schedule_update() for item in self._updateItems: try: item.update(force=True) except Exception: _moduleLogger.exception("Update failed for %r" % item) - return False # do not continue diff --git a/src/handle.py b/src/handle.py index 5fc4e98..9baf35b 100644 --- a/src/handle.py +++ b/src/handle.py @@ -4,7 +4,7 @@ import weakref import telepathy import tp -import util.misc as util_misc +import util.misc as misc_utils _moduleLogger = logging.getLogger("handle") @@ -42,7 +42,7 @@ class ConnectionHandle(TheOneRingHandle): class ContactHandle(TheOneRingHandle): def __init__(self, connection, id, phoneNumber): - self._phoneNumber = util_misc.normalize_number(phoneNumber) + self._phoneNumber = misc_utils.normalize_number(phoneNumber) handleType = telepathy.HANDLE_TYPE_CONTACT handleName = self._phoneNumber diff --git a/src/location.py b/src/location.py index 59195fd..a77d0c7 100644 --- a/src/location.py +++ b/src/location.py @@ -2,7 +2,7 @@ import logging import telepathy -import gtk_toolbox +import util.misc as misc_utils _moduleLogger = logging.getLogger('location') @@ -22,21 +22,21 @@ class LocationMixin(object): """ raise NotImplementedError("Abstract property called") - @gtk_toolbox.log_exception(_moduleLogger) + @misc_utils.log_exception(_moduleLogger) def GetLocations(self, contacts): """ @returns {Contact: {Location Type: Location}} """ raise telepathy.errors.NotImplemented("Yet") - @gtk_toolbox.log_exception(_moduleLogger) + @misc_utils.log_exception(_moduleLogger) def RequestLocation(self, contact): """ @returns {Location Type: Location} """ raise telepathy.errors.NotImplemented("Yet") - @gtk_toolbox.log_exception(_moduleLogger) + @misc_utils.log_exception(_moduleLogger) def SetLocation(self, location): """ Since presence is based off of phone numbers, not allowing the client to change it diff --git a/src/presence.py b/src/presence.py index ab6fc66..c73a061 100644 --- a/src/presence.py +++ b/src/presence.py @@ -1,7 +1,7 @@ import logging import tp -import gtk_toolbox +import util.misc as misc_utils import simple_presence @@ -14,7 +14,7 @@ class PresenceMixin(tp.ConnectionInterfacePresence, simple_presence.TheOneRingPr tp.ConnectionInterfacePresence.__init__(self) simple_presence.TheOneRingPresence.__init__(self) - @gtk_toolbox.log_exception(_moduleLogger) + @misc_utils.log_exception(_moduleLogger) def GetStatuses(self): # the arguments are in common to all on-line presences arguments = {} @@ -24,16 +24,16 @@ class PresenceMixin(tp.ConnectionInterfacePresence, simple_presence.TheOneRingPr for (localType, telepathyType) in self.TO_PRESENCE_TYPE.iteritems() ) - @gtk_toolbox.log_exception(_moduleLogger) + @misc_utils.log_exception(_moduleLogger) def RequestPresence(self, contactIds): presences = self.__get_presences(contactIds) self.PresenceUpdate(presences) - @gtk_toolbox.log_exception(_moduleLogger) + @misc_utils.log_exception(_moduleLogger) def GetPresence(self, contactIds): return self.__get_presences(contactIds) - @gtk_toolbox.log_exception(_moduleLogger) + @misc_utils.log_exception(_moduleLogger) def SetStatus(self, statuses): assert len(statuses) == 1 status, arguments = statuses.items()[0] diff --git a/src/simple_presence.py b/src/simple_presence.py index 5449344..ac8b875 100644 --- a/src/simple_presence.py +++ b/src/simple_presence.py @@ -2,7 +2,7 @@ import logging import telepathy -import gtk_toolbox +import util.misc as misc_utils import tp import handle import gvoice.state_machine as state_machine @@ -98,7 +98,7 @@ class SimplePresenceMixin(tp.ConnectionInterfaceSimplePresence, TheOneRingPresen {'Statuses' : self._get_statuses} ) - @gtk_toolbox.log_exception(_moduleLogger) + @misc_utils.log_exception(_moduleLogger) def GetPresences(self, contacts): """ @return {ContactHandle: (Status, Presence Type, Message)} @@ -109,7 +109,7 @@ class SimplePresenceMixin(tp.ConnectionInterfaceSimplePresence, TheOneRingPresen for (h, (presenceType, presence)) in self.get_presences(contacts).iteritems() ) - @gtk_toolbox.log_exception(_moduleLogger) + @misc_utils.log_exception(_moduleLogger) def SetPresence(self, status, message): if message: raise telepathy.errors.InvalidArgument("Messages aren't supported") diff --git a/src/util/go_utils.py b/src/util/go_utils.py index 2753ec8..8b72dff 100644 --- a/src/util/go_utils.py +++ b/src/util/go_utils.py @@ -4,9 +4,35 @@ from __future__ import with_statement import time import functools +import logging import gobject +import misc + + +_moduleLogger = logging.getLogger("go_utils") + + +def make_idler(func): + """ + Decorator that makes a generator-function into a function that will continue execution on next call + """ + a = [] + + @functools.wraps(func) + def decorated_func(*args, **kwds): + if not a: + a.append(func(*args, **kwds)) + try: + a[0].next() + return True + except StopIteration: + del a[:] + return False + + return decorated_func + def async(func): """ @@ -29,6 +55,65 @@ def async(func): return new_function +class Async(object): + + def __init__(self, func, once = True): + self.__func = func + self.__idleId = None + self.__once = once + + def start(self): + assert self.__idleId is None + if self.__once: + self.__idleId = gobject.idle_add(self._on_once) + else: + self.__idleId = gobject.idle_add(self.__func) + + def cancel(self): + if self.__idleId is not None: + gobject.source_remove(self.__idleId) + self.__idleId = None + + @misc.log_exception(_moduleLogger) + def _on_once(self): + self.cancel() + try: + self.__func() + finally: + return False + + +class Timeout(object): + + def __init__(self, func): + self.__func = func + self.__timeoutId = None + + def start(self, **kwds): + assert self.__timeoutId is None + + assert len(kwds) == 1 + timeoutInSeconds = kwds["seconds"] + assert 0 <= timeoutInSeconds + if timeoutInSeconds == 0: + self.__timeoutId = gobject.idle_add(self._on_once) + else: + timeout_add_seconds(timeoutInSeconds, self._on_once) + + def cancel(self): + if self.__timeoutId is not None: + gobject.source_remove(self.__timeoutId) + self.__timeoutId = None + + @misc.log_exception(_moduleLogger) + def _on_once(self): + self.cancel() + try: + self.__func() + finally: + return False + + def throttled(minDelay, queue): """ Throttle the calls to a function by queueing all the calls that happen diff --git a/src/util/misc.py b/src/util/misc.py index ba5f506..69a5b09 100644 --- a/src/util/misc.py +++ b/src/util/misc.py @@ -16,6 +16,45 @@ import warnings import string +_indentationLevel = [0] + + +def log_call(logger): + + def log_call_decorator(func): + + @functools.wraps(func) + def wrapper(*args, **kwds): + logger.debug("%s> %s" % (" " * _indentationLevel[0], func.__name__, )) + _indentationLevel[0] += 1 + try: + return func(*args, **kwds) + finally: + _indentationLevel[0] -= 1 + logger.debug("%s< %s" % (" " * _indentationLevel[0], func.__name__, )) + + return wrapper + + return log_call_decorator + + +def log_exception(logger): + + def log_exception_decorator(func): + + @functools.wraps(func) + def wrapper(*args, **kwds): + try: + return func(*args, **kwds) + except Exception: + logger.exception(func.__name__) + raise + + return wrapper + + return log_exception_decorator + + def printfmt(template): """ This hides having to create the Template object and call substitute/safe_substitute on it. For example: diff --git a/src/util/tp_utils.py b/src/util/tp_utils.py index dbf06ec..9ae525b 100644 --- a/src/util/tp_utils.py +++ b/src/util/tp_utils.py @@ -7,7 +7,7 @@ import dbus import telepathy import util.go_utils as gobject_utils -import gtk_toolbox +import misc _moduleLogger = logging.getLogger("tp_utils") @@ -25,7 +25,8 @@ class WasMissedCall(object): self._didClose = False self._didReport = False - self._timeoutId = gobject_utils.timeout_add_seconds(10, self._on_timeout) + self._onTimeout = misc.Timeout(self._on_timeout) + self._onTimeout.start(seconds=10) chan[telepathy.interfaces.CHANNEL_INTERFACE_GROUP].connect_to_signal( "MembersChanged", @@ -63,40 +64,36 @@ class WasMissedCall(object): def _report_success(self): assert not self._didReport self._didReport = True - if self._timeoutId: - gobject.source_remove(self._timeoutId) - self._timeoutId = None + self._onTimeout.cancel() self._on_success(self) def _report_error(self, reason): assert not self._didReport self._didReport = True - if self._timeoutId: - gobject.source_remove(self._timeoutId) - self._timeoutId = None + self._onTimeout.cancel() self._on_error(self, reason) - @gtk_toolbox.log_exception(_moduleLogger) + @misc.log_exception(_moduleLogger) def _on_got_all(self, properties): self._requested = properties["Requested"] self._report_missed_if_ready() - @gtk_toolbox.log_exception(_moduleLogger) + @misc.log_exception(_moduleLogger) def _on_members_changed(self, message, added, removed, lp, rp, actor, reason): if added: self._didMembersChange = True self._report_missed_if_ready() - @gtk_toolbox.log_exception(_moduleLogger) + @misc.log_exception(_moduleLogger) def _on_closed(self): self._didClose = True self._report_missed_if_ready() - @gtk_toolbox.log_exception(_moduleLogger) + @misc.log_exception(_moduleLogger) def _on_error(self, *args): self._report_error(args) - @gtk_toolbox.log_exception(_moduleLogger) + @misc.log_exception(_moduleLogger) def _on_timeout(self): self._report_error("timeout") return False @@ -126,7 +123,7 @@ class NewChannelSignaller(object): None ) - @gtk_toolbox.log_exception(_moduleLogger) + @misc.log_exception(_moduleLogger) def _on_new_channel( self, channelObjectPath, channelType, handleType, handle, supressHandler ):