diff --git a/Changelog.draft.rst b/Changelog.draft.rst index e840717..53fb381 100644 --- a/Changelog.draft.rst +++ b/Changelog.draft.rst @@ -13,6 +13,10 @@ others. New Features ------------ +* When a message upload/download fails, we do not abort the whole folder + synchronization, but only skip that message, informing the user at the + end of the sync run. + Changes ------- diff --git a/offlineimap/accounts.py b/offlineimap/accounts.py index a989441..8653d48 100644 --- a/offlineimap/accounts.py +++ b/offlineimap/accounts.py @@ -22,6 +22,7 @@ from offlineimap.threadutil import InstanceLimitedThread from subprocess import Popen, PIPE from threading import Event import os +from sys import exc_info import traceback def getaccountlist(customconfig): @@ -178,16 +179,16 @@ class SyncableAccount(Account): except (KeyboardInterrupt, SystemExit): raise except OfflineImapError, e: - self.ui.warn(e.reason) # Stop looping and bubble up Exception if needed. if e.severity >= OfflineImapError.ERROR.REPO: if looping: looping -= 1 if e.severity >= OfflineImapError.ERROR.CRITICAL: raise - except: - self.ui.warn("Error occured attempting to sync account "\ - "'%s':\n%s"% (self, traceback.format_exc())) + self.ui.error(e, exc_info()[2]) + except Exception, e: + self.ui.error(e, msg = "While attempting to sync " + "account %s:\n %s"% (self, traceback.format_exc())) else: # after success sync, reset the looping counter to 3 if self.refreshperiod: @@ -276,8 +277,10 @@ class SyncableAccount(Account): r = p.communicate() self.ui.callhook("Hook stdout: %s\nHook stderr:%s\n" % r) self.ui.callhook("Hook return code: %d" % p.returncode) - except: - self.ui.warn("Exception occured while calling hook") + except (KeyboardInterrupt, SystemExit): + raise + except Exception, e: + self.ui.error(e, exc_info()[2], msg = "Calling hook") def syncfolder(accountname, remoterepos, remotefolder, localrepos, @@ -366,9 +369,9 @@ def syncfolder(accountname, remoterepos, remotefolder, localrepos, if e.severity > OfflineImapError.ERROR.FOLDER: raise else: - ui.warn("Aborting folder sync '%s' [acc: '%s']\nReason was: %s" %\ - (localfolder.name, accountname, e.reason)) - except: - ui.warn("ERROR in syncfolder for %s folder %s: %s" % \ + ui.error(e, exc_info()[2], msg = "Aborting folder sync '%s' " + "[acc: '%s']" % (localfolder, accountname)) + except Exception, e: + ui.error(e, msg = "ERROR in syncfolder for %s folder %s: %s" % \ (accountname,remotefolder.getvisiblename(), traceback.format_exc())) diff --git a/offlineimap/folder/Base.py b/offlineimap/folder/Base.py index 0d5ddae..5a67775 100644 --- a/offlineimap/folder/Base.py +++ b/offlineimap/folder/Base.py @@ -19,6 +19,7 @@ from offlineimap import threadutil from offlineimap.ui import getglobalui import os.path import re +from sys import exc_info import traceback class BaseFolder(object): @@ -229,62 +230,55 @@ class BaseFolder(object): # synced to the status cache. This is only a problem with # self.getmessage(). So, don't call self.getmessage unless # really needed. - try: - if register: # output that we start a new thread - self.ui.registerthread(self.getaccountname()) + if register: # output that we start a new thread + self.ui.registerthread(self.getaccountname()) - message = None - flags = self.getmessageflags(uid) - rtime = self.getmessagetime(uid) + message = None + flags = self.getmessageflags(uid) + rtime = self.getmessagetime(uid) - if uid > 0 and dstfolder.uidexists(uid): - # dst has message with that UID already, only update status - statusfolder.savemessage(uid, None, flags, rtime) - return + if uid > 0 and dstfolder.uidexists(uid): + # dst has message with that UID already, only update status + statusfolder.savemessage(uid, None, flags, rtime) + return - self.ui.copyingmessage(uid, self, [dstfolder]) - # If any of the destinations actually stores the message body, - # load it up. - if dstfolder.storesmessages(): - - message = self.getmessage(uid) - #Succeeded? -> IMAP actually assigned a UID. If newid - #remained negative, no server was willing to assign us an - #UID. If newid is 0, saving succeeded, but we could not - #retrieve the new UID. Ignore message in this case. - newuid = dstfolder.savemessage(uid, message, flags, rtime) - if newuid > 0: - if newuid != uid: - # Got new UID, change the local uid. - #TODO: Maildir could do this with a rename rather than - #load/save/del operation, IMPLEMENT a changeuid() - #function or so. - self.savemessage(newuid, message, flags, rtime) - self.deletemessage(uid) - uid = newuid - # Save uploaded status in the statusfolder - statusfolder.savemessage(uid, message, flags, rtime) - elif newuid == 0: - # Message was stored to dstfolder, but we can't find it's UID - # This means we can't link current message to the one created - # in IMAP. So we just delete local message and on next run - # we'll sync it back - # XXX This could cause infinite loop on syncing between two - # IMAP servers ... + self.ui.copyingmessage(uid, self, [dstfolder]) + # If any of the destinations actually stores the message body, + # load it up. + if dstfolder.storesmessages(): + message = self.getmessage(uid) + #Succeeded? -> IMAP actually assigned a UID. If newid + #remained negative, no server was willing to assign us an + #UID. If newid is 0, saving succeeded, but we could not + #retrieve the new UID. Ignore message in this case. + newuid = dstfolder.savemessage(uid, message, flags, rtime) + if newuid > 0: + if newuid != uid: + # Got new UID, change the local uid. + #TODO: Maildir could do this with a rename rather than + #load/save/del operation, IMPLEMENT a changeuid() + #function or so. + self.savemessage(newuid, message, flags, rtime) self.deletemessage(uid) - else: - raise UserWarning("Trying to save msg (uid %d) on folder " - "%s returned invalid uid %d" % \ - (uid, - dstfolder.getvisiblename(), - newuid)) - except (KeyboardInterrupt): - raise - except: - self.ui.warn("ERROR attempting to copy message " + str(uid) \ - + " for account " + self.getaccountname() + ":" \ - + traceback.format_exc()) - raise + uid = newuid + # Save uploaded status in the statusfolder + statusfolder.savemessage(uid, message, flags, rtime) + + elif newuid == 0: + # Message was stored to dstfolder, but we can't find it's UID + # This means we can't link current message to the one created + # in IMAP. So we just delete local message and on next run + # we'll sync it back + # XXX This could cause infinite loop on syncing between two + # IMAP servers ... + self.deletemessage(uid) + else: + raise OfflineImapError("Trying to save msg (uid %d) on folder " + "%s returned invalid uid %d" % \ + (uid, + dstfolder.getvisiblename(), + newuid), + OfflineImapError.ERROR.MESSAGE) def syncmessagesto_copy(self, dstfolder, statusfolder): """Pass1: Copy locally existing messages not on the other side @@ -303,20 +297,30 @@ class BaseFolder(object): statusfolder.uidexists(uid), self.getmessageuidlist()) for uid in copylist: - if self.suggeststhreads(): - self.waitforthread() - thread = threadutil.InstanceLimitedThread(\ - self.getcopyinstancelimit(), - target = self.copymessageto, - name = "Copy message %d from %s" % (uid, + try: + if self.suggeststhreads(): + self.waitforthread() + thread = threadutil.InstanceLimitedThread(\ + self.getcopyinstancelimit(), + target = self.copymessageto, + name = "Copy message %d from %s" % (uid, self.getvisiblename()), - args = (uid, dstfolder, statusfolder)) - thread.setDaemon(1) - thread.start() - threads.append(thread) - else: - self.copymessageto(uid, dstfolder, statusfolder, register = 0) - + args = (uid, dstfolder, statusfolder)) + thread.setDaemon(1) + thread.start() + threads.append(thread) + else: + self.copymessageto(uid, dstfolder, statusfolder, + register = 0) + except OfflineImapError, e: + if e.severity > OfflineImapError.ERROR.Message: + raise # buble severe errors up + self.ui.error(e, exc_info()[2]) + except Exception, e: + self.ui.error(e, "Copying message %s [acc: %s]:\n %s" %\ + (uid, self.getaccountname(), + traceback.format_exc())) + raise #raise on unknown errors, so we can fix those for thread in threads: thread.join() @@ -421,8 +425,12 @@ class BaseFolder(object): action(dstfolder, statusfolder) except (KeyboardInterrupt): raise - except: - self.ui.warn("ERROR attempting to sync flags " \ - + "for account " + self.getaccountname() \ - + ":" + traceback.format_exc()) - raise + except OfflineImap, e: + if e.severity > OfflineImapError.ERROR.FOLDER: + raise + self.ui.error(e, exc_info()[2]) + except Exception, e: + self.ui.error(e, msg = "ERROR attempting to sync folder %s " + "[acc: %s]:\n %s" (self, self.getaccountname(), + traceback.format_exc())) + raise # raise unknown Exceptions so we can fix them