rationalize use of deferreds twisty
authorMark Hammond <mhammond@skippinet.com.au>
Sun, 15 Mar 2009 11:10:37 +1100
branchtwisty
changeset 95 605f125ba61382470117c2985fce8188bb0537e4
parent 94 32b03461d034dd400367326021acfbb9748a1f5e
child 96 7a7131b6b38cc06a6f5763c7a3ab14c627fc0335
push id1
push userroot
push dateWed, 08 Apr 2009 01:46:05 +0000
rationalize use of deferreds
server/python/junius/proto/imap.py
--- a/server/python/junius/proto/imap.py
+++ b/server/python/junius/proto/imap.py
@@ -15,21 +15,28 @@ logger = logging.getLogger(__name__)
 imap_encoding = 'utf-8'
 
 class ImapClient(imap4.IMAP4Client):
   '''
   Much of our logic here should perhaps be in another class that holds a
   reference to the IMAP4Client instance subclass.  Consider refactoring if we
   don't turn out to benefit from the subclassing relationship.
   '''
+  def finished(self, result):
+    # See bottom of file - it would be good to remove this...
+    logger.info("Finished synchronizing IMAP folders")
+    # XXX - this should be done via callback/errback
+    from ..sync import get_conductor
+    return get_conductor().accountFinishedSync(self.account, result)
+
   def serverGreeting(self, caps):
     logger.debug("IMAP server greeting: capabilities are %s", caps)
-    d = self._doAuthenticate()
-    d.addCallback(self._reqList)
-
+    return self._doAuthenticate(
+            ).addCallback(self._reqList
+            ).addBoth(self.finished)
 
   def _doAuthenticate(self):
     if self.account.details.get('crypto') == 'TLS':
       d = self.startTLS(self.factory.ctx)
       d.addErrback(self.accountStatus,
                    brat.SERVER, brat.BAD, brat.CRYPTO, brat.PERMANENT)
       d.addCallback(self._doLogin)
     else:
@@ -51,20 +58,17 @@ class ImapClient(imap4.IMAP4Client):
     # As per http://python.codefetch.com/example/ow/tnpe_code/ch07/imapdownload.py,
     # We keep a 'stack' of items left to process in an instance variable...
     self.folder_infos = result[:]
     return self._processNextFolder()
 
   def _processNextFolder(self):
     if not self.folder_infos:
       # yay - all done!
-      logger.info("Finished synchronizing IMAP folders")
-      # need to report we are done somewhere?
-      from ..sync import get_conductor
-      return get_conductor().accountFinishedSync(self.account)
+      return
 
     flags, delim, name = self.folder_infos.pop()
     self.current_folder_path = cfp = name.split(delim)
     logger.debug('Processing folder %s (flags=%s)', name, flags)
     if r"\Noselect" in flags:
       logger.debug("'%s' is unselectable - skipping", name)
       return self._processNextFolder()
 
@@ -78,19 +82,19 @@ class ImapClient(imap4.IMAP4Client):
     return self.examine(name
                  ).addCallback(self._examineFolder, cfp
                  ).addErrback(self._cantExamineFolder, cfp)
 
   def _examineFolder(self, result, folder_path):
     logger.debug('Looking for messages already fetched for folder %s', folder_path)
     startkey=['rfc822', self.account.details['_id'], [folder_path, 0]]
     endkey=['rfc822', self.account.details['_id'], [folder_path, 4000000000]]
-    get_db().openView('raindrop!messages!by', 'by_storage',
-                      startkey=startkey, endkey=endkey,
-        ).addCallback(self._fetchAndProcess, folder_path)
+    return get_db().openView('raindrop!messages!by', 'by_storage',
+                             startkey=startkey, endkey=endkey,
+              ).addCallback(self._fetchAndProcess, folder_path)
 
   def _fetchAndProcess(self, rows, folder_path):
     allMessages = imap4.MessageSet(1, None)
     seen_ids = [r['key'][2][1] for r in rows]
     logger.debug("%d messages already exist from %s",
                  len(seen_ids), folder_path)
     return self.fetchUID(allMessages, True).addCallback(
             self._gotUIDs, folder_path, seen_ids)
@@ -150,20 +154,20 @@ class ImapClient(imap4.IMAP4Client):
     doc = dict(
       type='rawMessage',
       subtype='rfc822',
       account_id=self.account.details['_id'],
       storage_key=[self.current_folder_path, int(result['UID'])],
       rfc822=body,
       imap_flags=flags,
       )
-    get_db().saveDoc(doc
-            ).addCallback(self._savedDocument
-            ).addErrback(self._cantSaveDocument
-            )
+    return get_db().saveDoc(doc
+                ).addCallback(self._savedDocument
+                ).addErrback(self._cantSaveDocument
+                )
 
   def _cantGetMessage(self, failure):
     logger.error("Failed to fetch message: %s", failure)
     return self._processNextMessage()
 
   def _savedDocument(self, result):
     logger.debug("Saved message %s", result)
     return self._processNextMessage()
@@ -172,17 +176,17 @@ class ImapClient(imap4.IMAP4Client):
     logger.error("Failed to save message: %s", failure)
     return self._processNextMessage()
 
   def _cantExamineFolder(self, failure, name, *args, **kw):
     logger.warning("Failed to examine folder '%s': %s", name, failure)
     return self._processNextFolder()
 
   def accountStatus(self, result, *args):
-    self.account.reportStatus(*args)
+    return self.account.reportStatus(*args)
 
 
 class ImapClientFactory(protocol.ClientFactory):
   protocol = ImapClient
 
   def __init__(self, account):
     self.account = account
 
@@ -234,8 +238,11 @@ class ImapClientFactory(protocol.ClientF
 class IMAPAccount(base.AccountBase):
   def __init__(self, db, details):
     self.db = db
     self.details = details
 
   def startSync(self, conductor):
     self.factory = ImapClientFactory(self)
     self.factory.connect()
+    # XXX - wouldn't it be good to return a deferred here, so the conductor
+    # can reliably wait for the deferred to complete, rather than forcing
+    # each deferred to manage 'finished' itself?