accounts.py: Use ui.error when raising exceptions

Rather than using ui.warn, use ui.error() which outputs Exceptions to
the error log, saving them to a stack, so we get notified again at the
end of the sync run.

Signed-off-by: Sebastian Spaeth <Sebastian@SSpaeth.de>
Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
This commit is contained in:
Sebastian Spaeth 2011-08-11 12:22:35 +02:00 committed by Nicolas Sebrecht
parent f937a86571
commit b47bc44783
3 changed files with 95 additions and 80 deletions

View File

@ -13,6 +13,10 @@ others.
New Features 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 Changes
------- -------

View File

@ -22,6 +22,7 @@ from offlineimap.threadutil import InstanceLimitedThread
from subprocess import Popen, PIPE from subprocess import Popen, PIPE
from threading import Event from threading import Event
import os import os
from sys import exc_info
import traceback import traceback
def getaccountlist(customconfig): def getaccountlist(customconfig):
@ -178,16 +179,16 @@ class SyncableAccount(Account):
except (KeyboardInterrupt, SystemExit): except (KeyboardInterrupt, SystemExit):
raise raise
except OfflineImapError, e: except OfflineImapError, e:
self.ui.warn(e.reason)
# Stop looping and bubble up Exception if needed. # Stop looping and bubble up Exception if needed.
if e.severity >= OfflineImapError.ERROR.REPO: if e.severity >= OfflineImapError.ERROR.REPO:
if looping: if looping:
looping -= 1 looping -= 1
if e.severity >= OfflineImapError.ERROR.CRITICAL: if e.severity >= OfflineImapError.ERROR.CRITICAL:
raise raise
except: self.ui.error(e, exc_info()[2])
self.ui.warn("Error occured attempting to sync account "\ except Exception, e:
"'%s':\n%s"% (self, traceback.format_exc())) self.ui.error(e, msg = "While attempting to sync "
"account %s:\n %s"% (self, traceback.format_exc()))
else: else:
# after success sync, reset the looping counter to 3 # after success sync, reset the looping counter to 3
if self.refreshperiod: if self.refreshperiod:
@ -276,8 +277,10 @@ class SyncableAccount(Account):
r = p.communicate() r = p.communicate()
self.ui.callhook("Hook stdout: %s\nHook stderr:%s\n" % r) self.ui.callhook("Hook stdout: %s\nHook stderr:%s\n" % r)
self.ui.callhook("Hook return code: %d" % p.returncode) self.ui.callhook("Hook return code: %d" % p.returncode)
except: except (KeyboardInterrupt, SystemExit):
self.ui.warn("Exception occured while calling hook") raise
except Exception, e:
self.ui.error(e, exc_info()[2], msg = "Calling hook")
def syncfolder(accountname, remoterepos, remotefolder, localrepos, def syncfolder(accountname, remoterepos, remotefolder, localrepos,
@ -366,9 +369,9 @@ def syncfolder(accountname, remoterepos, remotefolder, localrepos,
if e.severity > OfflineImapError.ERROR.FOLDER: if e.severity > OfflineImapError.ERROR.FOLDER:
raise raise
else: else:
ui.warn("Aborting folder sync '%s' [acc: '%s']\nReason was: %s" %\ ui.error(e, exc_info()[2], msg = "Aborting folder sync '%s' "
(localfolder.name, accountname, e.reason)) "[acc: '%s']" % (localfolder, accountname))
except: except Exception, e:
ui.warn("ERROR in syncfolder for %s folder %s: %s" % \ ui.error(e, msg = "ERROR in syncfolder for %s folder %s: %s" % \
(accountname,remotefolder.getvisiblename(), (accountname,remotefolder.getvisiblename(),
traceback.format_exc())) traceback.format_exc()))

View File

@ -19,6 +19,7 @@ from offlineimap import threadutil
from offlineimap.ui import getglobalui from offlineimap.ui import getglobalui
import os.path import os.path
import re import re
from sys import exc_info
import traceback import traceback
class BaseFolder(object): class BaseFolder(object):
@ -229,62 +230,55 @@ class BaseFolder(object):
# synced to the status cache. This is only a problem with # synced to the status cache. This is only a problem with
# self.getmessage(). So, don't call self.getmessage unless # self.getmessage(). So, don't call self.getmessage unless
# really needed. # really needed.
try: if register: # output that we start a new thread
if register: # output that we start a new thread self.ui.registerthread(self.getaccountname())
self.ui.registerthread(self.getaccountname())
message = None message = None
flags = self.getmessageflags(uid) flags = self.getmessageflags(uid)
rtime = self.getmessagetime(uid) rtime = self.getmessagetime(uid)
if uid > 0 and dstfolder.uidexists(uid): if uid > 0 and dstfolder.uidexists(uid):
# dst has message with that UID already, only update status # dst has message with that UID already, only update status
statusfolder.savemessage(uid, None, flags, rtime) statusfolder.savemessage(uid, None, flags, rtime)
return return
self.ui.copyingmessage(uid, self, [dstfolder]) self.ui.copyingmessage(uid, self, [dstfolder])
# If any of the destinations actually stores the message body, # If any of the destinations actually stores the message body,
# load it up. # load it up.
if dstfolder.storesmessages(): if dstfolder.storesmessages():
message = self.getmessage(uid)
message = self.getmessage(uid) #Succeeded? -> IMAP actually assigned a UID. If newid
#Succeeded? -> IMAP actually assigned a UID. If newid #remained negative, no server was willing to assign us an
#remained negative, no server was willing to assign us an #UID. If newid is 0, saving succeeded, but we could not
#UID. If newid is 0, saving succeeded, but we could not #retrieve the new UID. Ignore message in this case.
#retrieve the new UID. Ignore message in this case. newuid = dstfolder.savemessage(uid, message, flags, rtime)
newuid = dstfolder.savemessage(uid, message, flags, rtime) if newuid > 0:
if newuid > 0: if newuid != uid:
if newuid != uid: # Got new UID, change the local uid.
# Got new UID, change the local uid. #TODO: Maildir could do this with a rename rather than
#TODO: Maildir could do this with a rename rather than #load/save/del operation, IMPLEMENT a changeuid()
#load/save/del operation, IMPLEMENT a changeuid() #function or so.
#function or so. self.savemessage(newuid, message, flags, rtime)
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.deletemessage(uid) self.deletemessage(uid)
else: uid = newuid
raise UserWarning("Trying to save msg (uid %d) on folder " # Save uploaded status in the statusfolder
"%s returned invalid uid %d" % \ statusfolder.savemessage(uid, message, flags, rtime)
(uid,
dstfolder.getvisiblename(), elif newuid == 0:
newuid)) # Message was stored to dstfolder, but we can't find it's UID
except (KeyboardInterrupt): # This means we can't link current message to the one created
raise # in IMAP. So we just delete local message and on next run
except: # we'll sync it back
self.ui.warn("ERROR attempting to copy message " + str(uid) \ # XXX This could cause infinite loop on syncing between two
+ " for account " + self.getaccountname() + ":" \ # IMAP servers ...
+ traceback.format_exc()) self.deletemessage(uid)
raise 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): def syncmessagesto_copy(self, dstfolder, statusfolder):
"""Pass1: Copy locally existing messages not on the other side """Pass1: Copy locally existing messages not on the other side
@ -303,20 +297,30 @@ class BaseFolder(object):
statusfolder.uidexists(uid), statusfolder.uidexists(uid),
self.getmessageuidlist()) self.getmessageuidlist())
for uid in copylist: for uid in copylist:
if self.suggeststhreads(): try:
self.waitforthread() if self.suggeststhreads():
thread = threadutil.InstanceLimitedThread(\ self.waitforthread()
self.getcopyinstancelimit(), thread = threadutil.InstanceLimitedThread(\
target = self.copymessageto, self.getcopyinstancelimit(),
name = "Copy message %d from %s" % (uid, target = self.copymessageto,
name = "Copy message %d from %s" % (uid,
self.getvisiblename()), self.getvisiblename()),
args = (uid, dstfolder, statusfolder)) args = (uid, dstfolder, statusfolder))
thread.setDaemon(1) thread.setDaemon(1)
thread.start() thread.start()
threads.append(thread) threads.append(thread)
else: else:
self.copymessageto(uid, dstfolder, statusfolder, register = 0) 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: for thread in threads:
thread.join() thread.join()
@ -421,8 +425,12 @@ class BaseFolder(object):
action(dstfolder, statusfolder) action(dstfolder, statusfolder)
except (KeyboardInterrupt): except (KeyboardInterrupt):
raise raise
except: except OfflineImap, e:
self.ui.warn("ERROR attempting to sync flags " \ if e.severity > OfflineImapError.ERROR.FOLDER:
+ "for account " + self.getaccountname() \ raise
+ ":" + traceback.format_exc()) self.ui.error(e, exc_info()[2])
raise 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