[PATCH 1/2] properly check whether dstfolder supports labels

Abdo Roig-Maranges abdo.roig at gmail.com
Mon Apr 6 22:05:27 BST 2015


This adds a storeslabels() method to check whether a folder supports
labels, and checks it in copymessageto and copymessageto_labels,
replacing an exception-based check.
---
 offlineimap/folder/Base.py              |   4 +
 offlineimap/folder/Gmail.py             | 112 ++++++++---------
 offlineimap/folder/GmailMaildir.py      | 207 ++++++++++++++++----------------
 offlineimap/folder/LocalStatus.py       |   4 +
 offlineimap/folder/LocalStatusSQLite.py |   6 +-
 5 files changed, 172 insertions(+), 161 deletions(-)

diff --git a/offlineimap/folder/Base.py b/offlineimap/folder/Base.py
index 5d81af4..e54b1bc 100644
--- a/offlineimap/folder/Base.py
+++ b/offlineimap/folder/Base.py
@@ -134,6 +134,10 @@ class BaseFolder(object):
 
         return 1
 
+    def storeslabels(self):
+        """Should be true for any backend that is able to store message labels."""
+        return False
+
     def getvisiblename(self):
         """The nametrans-transposed name of the folder's name."""
 
diff --git a/offlineimap/folder/Gmail.py b/offlineimap/folder/Gmail.py
index 1afbe47..d8502c7 100644
--- a/offlineimap/folder/Gmail.py
+++ b/offlineimap/folder/Gmail.py
@@ -64,6 +64,11 @@ class GmailFolder(IMAPFolder):
         self.ignorelabels = set([l for l in re.split(r'\s*,\s*', ignorelabels) if len(l)])
 
 
+    def storeslabels(self):
+        """Should be true for any backend that is able to store message labels."""
+        return True
+
+
     def getmessage(self, uid):
         """Retrieve message with UID from the IMAP server (incl body).  Also
            gets Gmail labels and embeds them into the message.
@@ -288,17 +293,11 @@ class GmailFolder(IMAPFolder):
         # first copy the message
         super(GmailFolder, self).copymessageto(uid, dstfolder, statusfolder, register)
 
-        # sync labels and mtime now when the message is new (the embedded labels are up to date)
-        # otherwise we may be spending time for nothing, as they will get updated on a later pass.
-        if realcopy and self.synclabels:
-            try:
-                mtime = dstfolder.getmessagemtime(uid)
-                labels = dstfolder.getmessagelabels(uid)
-                statusfolder.savemessagelabels(uid, labels, mtime=mtime)
-
-            # dstfolder is not GmailMaildir.
-            except NotImplementedError:
-                return
+        # Store the labels and mtime on the statusfolder.
+        # It checks that target folder supports labels.
+        if realcopy and self.synclabels and dstfolder.storeslabels():
+            labels = dstfolder.getmessagelabels(uid)
+            statusfolder.savemessagelabels(uid, labels, mtime=dstfolder.getmessagemtime(uid))
 
     def syncmessagesto_labels(self, dstfolder, statusfolder):
         """Pass 4: Label Synchronization (Gmail only)
@@ -315,56 +314,57 @@ class GmailFolder(IMAPFolder):
         # the fastest thing in the world though...
         uidlist = []
 
+        # check that target folder supports labels.
+        if not dstfolder.storeslabels():
+            self.ui.warn("Repository '%s' does not support labels."% dstfolder.repository.name)
+            return
+
         # filter the uids (fast)
-        try:
-            for uid in self.getmessageuidlist():
-                # bail out on CTRL-C or SIGTERM
-                if offlineimap.accounts.Account.abort_NOW_signal.is_set():
-                    break
+        for uid in self.getmessageuidlist():
+            # bail out on CTRL-C or SIGTERM
+            if offlineimap.accounts.Account.abort_NOW_signal.is_set():
+                break
+
+            # Ignore messages with negative UIDs missed by pass 1 and
+            # don't do anything if the message has been deleted remotely
+            if uid < 0 or not dstfolder.uidexists(uid):
+                continue
 
-                # Ignore messages with negative UIDs missed by pass 1 and
-                # don't do anything if the message has been deleted remotely
-                if uid < 0 or not dstfolder.uidexists(uid):
-                    continue
+            selflabels = self.getmessagelabels(uid) - self.ignorelabels
 
-                selflabels = self.getmessagelabels(uid) - self.ignorelabels
+            if statusfolder.uidexists(uid):
+                statuslabels = statusfolder.getmessagelabels(uid) - self.ignorelabels
+            else:
+                statuslabels = set()
 
-                if statusfolder.uidexists(uid):
-                    statuslabels = statusfolder.getmessagelabels(uid) - self.ignorelabels
-                else:
-                    statuslabels = set()
+            if selflabels != statuslabels:
+                uidlist.append(uid)
 
-                if selflabels != statuslabels:
-                    uidlist.append(uid)
+        # now sync labels (slow)
+        mtimes = {}
+        labels = {}
+        for i, uid in enumerate(uidlist):
+            # bail out on CTRL-C or SIGTERM
+            if offlineimap.accounts.Account.abort_NOW_signal.is_set():
+                break
 
-            # now sync labels (slow)
-            mtimes = {}
-            labels = {}
-            for i, uid in enumerate(uidlist):
-                # bail out on CTRL-C or SIGTERM
-                if offlineimap.accounts.Account.abort_NOW_signal.is_set():
-                    break
+            selflabels = self.getmessagelabels(uid) - self.ignorelabels
 
-                selflabels = self.getmessagelabels(uid) - self.ignorelabels
+            if statusfolder.uidexists(uid):
+                statuslabels = statusfolder.getmessagelabels(uid) - self.ignorelabels
+            else:
+                statuslabels = set()
 
-                if statusfolder.uidexists(uid):
-                    statuslabels = statusfolder.getmessagelabels(uid) - self.ignorelabels
-                else:
-                    statuslabels = set()
-
-                if selflabels != statuslabels:
-                    self.ui.settinglabels(uid, i+1, len(uidlist), sorted(selflabels), dstfolder)
-                    if self.repository.account.dryrun:
-                        continue #don't actually add in a dryrun
-                    dstfolder.savemessagelabels(uid, selflabels, ignorelabels = self.ignorelabels)
-                    mtime = dstfolder.getmessagemtime(uid)
-                    mtimes[uid] = mtime
-                    labels[uid] = selflabels
-
-            # Update statusfolder in a single DB transaction. It is safe, as if something fails,
-            # statusfolder will be updated on the next run.
-            statusfolder.savemessageslabelsbulk(labels)
-            statusfolder.savemessagesmtimebulk(mtimes)
-
-        except NotImplementedError:
-            self.ui.warn("Can't sync labels. You need to configure a local repository of type GmailMaildir")
+            if selflabels != statuslabels:
+                self.ui.settinglabels(uid, i+1, len(uidlist), sorted(selflabels), dstfolder)
+                if self.repository.account.dryrun:
+                    continue #don't actually add in a dryrun
+                dstfolder.savemessagelabels(uid, selflabels, ignorelabels = self.ignorelabels)
+                mtime = dstfolder.getmessagemtime(uid)
+                mtimes[uid] = mtime
+                labels[uid] = selflabels
+
+        # Update statusfolder in a single DB transaction. It is safe, as if something fails,
+        # statusfolder will be updated on the next run.
+        statusfolder.savemessageslabelsbulk(labels)
+        statusfolder.savemessagesmtimebulk(mtimes)
diff --git a/offlineimap/folder/GmailMaildir.py b/offlineimap/folder/GmailMaildir.py
index a8b607a..84d82c9 100644
--- a/offlineimap/folder/GmailMaildir.py
+++ b/offlineimap/folder/GmailMaildir.py
@@ -39,6 +39,12 @@ class GmailMaildirFolder(MaildirFolder):
         if self.synclabels:
             self.syncmessagesto_passes.append(('syncing labels', self.syncmessagesto_labels))
 
+
+    def storeslabels(self):
+        """Should be true for any backend that is able to store message labels."""
+        return True
+
+
     def quickchanged(self, statusfolder):
         """Returns True if the Maildir has changed. Checks uids, flags and mtimes"""
 
@@ -201,18 +207,11 @@ class GmailMaildirFolder(MaildirFolder):
         # first copy the message
         super(GmailMaildirFolder, self).copymessageto(uid, dstfolder, statusfolder, register)
 
-        # sync labels and mtime now when the message is new (the embedded labels are up to date,
-        # and have already propagated to the remote server.
-        # for message which already existed on the remote, this is useless, as later the labels may
-        # get updated.
-        if realcopy and self.synclabels:
-            try:
-                labels = dstfolder.getmessagelabels(uid)
-                statusfolder.savemessagelabels(uid, labels, mtime=self.getmessagemtime(uid))
-
-            # dstfolder is not GmailMaildir.
-            except NotImplementedError:
-                return
+        # Store the labels and mtime on the statusfolder.
+        # It checks that target folder supports labels.
+        if realcopy and self.synclabels and dstfolder.storeslabels():
+            labels = dstfolder.getmessagelabels(uid)
+            statusfolder.savemessagelabels(uid, labels, mtime=self.getmessagemtime(uid))
 
     def syncmessagesto_labels(self, dstfolder, statusfolder):
         """Pass 4: Label Synchronization (Gmail only)
@@ -234,95 +233,95 @@ class GmailMaildirFolder(MaildirFolder):
         dellabellist = {}
         uidlist = []
 
-        try:
-            # filter uids (fast)
-            for uid in self.getmessageuidlist():
-                # bail out on CTRL-C or SIGTERM
-                if offlineimap.accounts.Account.abort_NOW_signal.is_set():
-                    break
-
-                # Ignore messages with negative UIDs missed by pass 1 and
-                # don't do anything if the message has been deleted remotely
-                if uid < 0 or not dstfolder.uidexists(uid):
-                    continue
-
-                selfmtime = self.getmessagemtime(uid)
-
-                if statusfolder.uidexists(uid):
-                    statusmtime = statusfolder.getmessagemtime(uid)
-                else:
-                    statusmtime = 0
-
-                if selfmtime > statusmtime:
-                    uidlist.append(uid)
-
-
-            self.ui.collectingdata(uidlist, self)
-            # This can be slow if there is a lot of modified files
-            for uid in uidlist:
-                # bail out on CTRL-C or SIGTERM
-                if offlineimap.accounts.Account.abort_NOW_signal.is_set():
-                    break
-
-                selflabels = self.getmessagelabels(uid)
-
-                if statusfolder.uidexists(uid):
-                    statuslabels = statusfolder.getmessagelabels(uid)
-                else:
-                    statuslabels = set()
-
-                addlabels = selflabels - statuslabels
-                dellabels = statuslabels - selflabels
-
-                for lb in addlabels:
-                    if not lb in addlabellist:
-                        addlabellist[lb] = []
-                    addlabellist[lb].append(uid)
-
-                for lb in dellabels:
-                    if not lb in dellabellist:
-                        dellabellist[lb] = []
-                    dellabellist[lb].append(uid)
-
-            for lb, uids in addlabellist.items():
-                # bail out on CTRL-C or SIGTERM
-                if offlineimap.accounts.Account.abort_NOW_signal.is_set():
-                    break
-
-                self.ui.addinglabels(uids, lb, dstfolder)
-                if self.repository.account.dryrun:
-                    continue #don't actually add in a dryrun
-                dstfolder.addmessageslabels(uids, set([lb]))
-                statusfolder.addmessageslabels(uids, set([lb]))
-
-            for lb, uids in dellabellist.items():
-                # bail out on CTRL-C or SIGTERM
-                if offlineimap.accounts.Account.abort_NOW_signal.is_set():
-                    break
-
-                self.ui.deletinglabels(uids, lb, dstfolder)
-                if self.repository.account.dryrun:
-                    continue #don't actually remove in a dryrun
-                dstfolder.deletemessageslabels(uids, set([lb]))
-                statusfolder.deletemessageslabels(uids, set([lb]))
-
-            # Update mtimes on StatusFolder. It is done last to be safe. If something els fails
-            # and the mtime is not updated, the labels will still be synced next time.
-            mtimes = {}
-            for uid in uidlist:
-                # bail out on CTRL-C or SIGTERM
-                if offlineimap.accounts.Account.abort_NOW_signal.is_set():
-                    break
-
-                if self.repository.account.dryrun:
-                    continue #don't actually update statusfolder
-
-                filename = self.messagelist[uid]['filename']
-                filepath = os.path.join(self.getfullname(), filename)
-                mtimes[uid] = long(os.stat(filepath).st_mtime)
-
-            # finally update statusfolder in a single DB transaction
-            statusfolder.savemessagesmtimebulk(mtimes)
-
-        except NotImplementedError:
-            self.ui.warn("Can't sync labels. You need to configure a remote repository of type Gmail.")
+        # check that target folder supports labels.
+        if not dstfolder.storeslabels():
+            self.ui.warn("Repository '%s' does not support labels."% dstfolder.repository.name)
+            return
+
+        # filter uids (fast)
+        for uid in self.getmessageuidlist():
+            # bail out on CTRL-C or SIGTERM
+            if offlineimap.accounts.Account.abort_NOW_signal.is_set():
+                break
+
+            # Ignore messages with negative UIDs missed by pass 1 and
+            # don't do anything if the message has been deleted remotely
+            if uid < 0 or not dstfolder.uidexists(uid):
+                continue
+
+            selfmtime = self.getmessagemtime(uid)
+
+            if statusfolder.uidexists(uid):
+                statusmtime = statusfolder.getmessagemtime(uid)
+            else:
+                statusmtime = 0
+
+            if selfmtime > statusmtime:
+                uidlist.append(uid)
+
+        self.ui.collectingdata(uidlist, self)
+        # This can be slow if there is a lot of modified files
+        for uid in uidlist:
+            # bail out on CTRL-C or SIGTERM
+            if offlineimap.accounts.Account.abort_NOW_signal.is_set():
+                break
+
+            selflabels = self.getmessagelabels(uid)
+
+            if statusfolder.uidexists(uid):
+                statuslabels = statusfolder.getmessagelabels(uid)
+            else:
+                statuslabels = set()
+
+            addlabels = selflabels - statuslabels
+            dellabels = statuslabels - selflabels
+
+            for lb in addlabels:
+                if not lb in addlabellist:
+                    addlabellist[lb] = []
+                addlabellist[lb].append(uid)
+
+            for lb in dellabels:
+                if not lb in dellabellist:
+                    dellabellist[lb] = []
+                dellabellist[lb].append(uid)
+
+        for lb, uids in addlabellist.items():
+            # bail out on CTRL-C or SIGTERM
+            if offlineimap.accounts.Account.abort_NOW_signal.is_set():
+                break
+
+            self.ui.addinglabels(uids, lb, dstfolder)
+            if self.repository.account.dryrun:
+                continue #don't actually add in a dryrun
+            dstfolder.addmessageslabels(uids, set([lb]))
+            statusfolder.addmessageslabels(uids, set([lb]))
+
+        for lb, uids in dellabellist.items():
+            # bail out on CTRL-C or SIGTERM
+            if offlineimap.accounts.Account.abort_NOW_signal.is_set():
+                break
+
+            self.ui.deletinglabels(uids, lb, dstfolder)
+            if self.repository.account.dryrun:
+                continue #don't actually remove in a dryrun
+            dstfolder.deletemessageslabels(uids, set([lb]))
+            statusfolder.deletemessageslabels(uids, set([lb]))
+
+        # Update mtimes on StatusFolder. It is done last to be safe. If something els fails
+        # and the mtime is not updated, the labels will still be synced next time.
+        mtimes = {}
+        for uid in uidlist:
+            # bail out on CTRL-C or SIGTERM
+            if offlineimap.accounts.Account.abort_NOW_signal.is_set():
+                break
+
+            if self.repository.account.dryrun:
+                continue #don't actually update statusfolder
+
+            filename = self.messagelist[uid]['filename']
+            filepath = os.path.join(self.getfullname(), filename)
+            mtimes[uid] = long(os.stat(filepath).st_mtime)
+
+        # finally update statusfolder in a single DB transaction
+        statusfolder.savemessagesmtimebulk(mtimes)
diff --git a/offlineimap/folder/LocalStatus.py b/offlineimap/folder/LocalStatus.py
index 78f2134..2c3d9bb 100644
--- a/offlineimap/folder/LocalStatus.py
+++ b/offlineimap/folder/LocalStatus.py
@@ -43,6 +43,10 @@ class LocalStatusFolder(BaseFolder):
     def storesmessages(self):
         return 0
 
+    def storeslabels(self):
+        """Should be true for any backend that is able to store message labels."""
+        return True
+
     def isnewfolder(self):
         return not os.path.exists(self.filename)
 
diff --git a/offlineimap/folder/LocalStatusSQLite.py b/offlineimap/folder/LocalStatusSQLite.py
index 64adcd1..d0e897f 100644
--- a/offlineimap/folder/LocalStatusSQLite.py
+++ b/offlineimap/folder/LocalStatusSQLite.py
@@ -97,6 +97,10 @@ class LocalStatusSQLiteFolder(BaseFolder):
     def storesmessages(self):
         return False
 
+    def storeslabels(self):
+        """Should be true for any backend that is able to store message labels."""
+        return True
+
     def getfullname(self):
         return self.filename
 
@@ -338,7 +342,7 @@ class LocalStatusSQLiteFolder(BaseFolder):
     def savemessageslabelsbulk(self, labels):
         """
         Saves labels from a dictionary in a single database operation.
-        
+
         """
         data = [(', '.join(sorted(l)), uid) for uid, l in labels.items()]
         self.__sql_write('UPDATE status SET labels=? WHERE id=?', data, executemany=True)
-- 
2.3.5





More information about the OfflineIMAP-project mailing list