Make flags a set rather than a list

As this is essentially what it is, a set of values. This allows as
to do set arithmetics to see, e.g. the intersection of 2 flag sets
rather than clunkily having to do:

for flag in newflags:
  if flag not in oldflags:
    oldflags.append(flag)

Also some more code documenting.

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-16 12:16:46 +02:00 committed by Nicolas Sebrecht
parent 373e7cdbc1
commit 466ded04d9
7 changed files with 89 additions and 64 deletions

View File

@ -22,6 +22,10 @@ import os.path
import re
from sys import exc_info
import traceback
try: # python 2.6 has set() built in
set
except NameError:
from sets import Set as set
class BaseFolder(object):
def __init__(self):
@ -189,12 +193,9 @@ class BaseFolder(object):
def addmessageflags(self, uid, flags):
"""Adds the specified flags to the message's flag set. If a given
flag is already present, it will not be duplicated."""
newflags = self.getmessageflags(uid)
for flag in flags:
if not flag in newflags:
newflags.append(flag)
newflags.sort()
flag is already present, it will not be duplicated.
:param flags: A set() of flags"""
newflags = self.getmessageflags(uid) | flags
self.savemessageflags(uid, newflags)
def addmessagesflags(self, uidlist, flags):
@ -204,11 +205,7 @@ class BaseFolder(object):
def deletemessageflags(self, uid, flags):
"""Removes each flag given from the message's flag set. If a given
flag is already removed, no action will be taken for that flag."""
newflags = self.getmessageflags(uid)
for flag in flags:
if flag in newflags:
newflags.remove(flag)
newflags.sort()
newflags = self.getmessageflags(uid) - flags
self.savemessageflags(uid, newflags)
def deletemessagesflags(self, uidlist, flags):
@ -362,8 +359,8 @@ class BaseFolder(object):
addflaglist = {}
delflaglist = {}
for uid in self.getmessageuidlist():
# Ignore messages with negative UIDs missed by pass 1
# also don't do anything if the message has been deleted remotely
# 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
@ -371,30 +368,31 @@ class BaseFolder(object):
statusflags = statusfolder.getmessageflags(uid)
#if we could not get message flags from LocalStatus, assume empty.
if statusflags is None:
statusflags = []
addflags = [x for x in selfflags if x not in statusflags]
statusflags = set()
addflags = selfflags - statusflags
delflags = statusflags - selfflags
for flag in addflags:
if not flag in addflaglist:
addflaglist[flag] = []
addflaglist[flag].append(uid)
delflags = [x for x in statusflags if x not in selfflags]
for flag in delflags:
if not flag in delflaglist:
delflaglist[flag] = []
delflaglist[flag].append(uid)
for flag in addflaglist.keys():
self.ui.addingflags(addflaglist[flag], flag, dstfolder)
dstfolder.addmessagesflags(addflaglist[flag], [flag])
statusfolder.addmessagesflags(addflaglist[flag], [flag])
for flag in delflaglist.keys():
self.ui.deletingflags(delflaglist[flag], flag, dstfolder)
dstfolder.deletemessagesflags(delflaglist[flag], [flag])
statusfolder.deletemessagesflags(delflaglist[flag], [flag])
for flag, uids in addflaglist.items():
self.ui.addingflags(uids, flag, dstfolder)
dstfolder.addmessagesflags(uids, set(flag))
statusfolder.addmessagesflags(uids, set(flag))
for flag,uids in delflaglist.items():
self.ui.deletingflags(uids, flag, dstfolder)
dstfolder.deletemessagesflags(uids, set(flag))
statusfolder.deletemessagesflags(uids, set(flag))
def syncmessagesto(self, dstfolder, statusfolder):
"""Syncs messages in this folder to the destination dstfolder.

View File

@ -21,7 +21,6 @@
from IMAP import IMAPFolder
from offlineimap import imaputil
from copy import copy
class GmailFolder(IMAPFolder):
@ -45,7 +44,7 @@ class GmailFolder(IMAPFolder):
def deletemessages_noconvert(self, uidlist):
uidlist = [uid for uid in uidlist if uid in self.messagelist]
if not len(uidlist):
return
return
if self.realdelete and not (self.getname() in self.real_delete_folders):
# IMAP expunge is just "remove label" in this folder,

View File

@ -24,6 +24,11 @@ import time
from copy import copy
from Base import BaseFolder
from offlineimap import imaputil, imaplibutil, OfflineImapError
try: # python 2.6 has set() built in
set
except NameError:
from sets import Set as set
class IMAPFolder(BaseFolder):
def __init__(self, imapserver, name, visiblename, accountname, repository):
@ -176,7 +181,8 @@ class IMAPFolder(BaseFolder):
finally:
self.imapserver.releaseconnection(imapobj)
for messagestr in response:
# Discard the message number.
# looks like: '1 (FLAGS (\\Seen Old) UID 4807)'
# Discard initial message number.
messagestr = messagestr.split(' ', 1)[1]
options = imaputil.flags2hash(messagestr)
if not options.has_key('UID'):
@ -652,23 +658,18 @@ class IMAPFolder(BaseFolder):
if not ('UID' in attributehash and 'FLAGS' in attributehash):
# Compensate for servers that don't return a UID attribute.
continue
lflags = attributehash['FLAGS']
flagstr = attributehash['FLAGS']
uid = long(attributehash['UID'])
self.messagelist[uid]['flags'] = imaputil.flagsimap2maildir(lflags)
self.messagelist[uid]['flags'] = imaputil.flagsimap2maildir(flagstr)
try:
needupdate.remove(uid)
except ValueError: # Let it slide if it's not in the list
pass
for uid in needupdate:
if operation == '+':
for flag in flags:
if not flag in self.messagelist[uid]['flags']:
self.messagelist[uid]['flags'].append(flag)
self.messagelist[uid]['flags'].sort()
self.messagelist[uid]['flags'] |= flags
elif operation == '-':
for flag in flags:
if flag in self.messagelist[uid]['flags']:
self.messagelist[uid]['flags'].remove(flag)
self.messagelist[uid]['flags'] -= flags
def deletemessage(self, uid):
self.deletemessages_noconvert([uid])
@ -682,7 +683,7 @@ class IMAPFolder(BaseFolder):
if not len(uidlist):
return
self.addmessagesflags_noconvert(uidlist, ['T'])
self.addmessagesflags_noconvert(uidlist, set('T'))
imapobj = self.imapserver.acquireconnection()
try:
try:

View File

@ -1,6 +1,5 @@
# Local status cache virtual folder
# Copyright (C) 2002 - 2008 John Goerzen
# <jgoerzen@complete.org>
# Copyright (C) 2002 - 2011 John Goerzen & contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@ -19,6 +18,10 @@
from Base import BaseFolder
import os
import threading
try: # python 2.6 has set() built in
set
except NameError:
from sets import Set as set
magicline = "OFFLINEIMAP LocalStatus CACHE DATA - DO NOT MODIFY - FORMAT 1"
@ -80,11 +83,12 @@ class LocalStatusFolder(BaseFolder):
try:
uid, flags = line.split(':')
uid = long(uid)
flags = set(flags)
except ValueError, e:
errstr = "Corrupt line '%s' in cache file '%s'" % (line, self.filename)
errstr = "Corrupt line '%s' in cache file '%s'" % \
(line, self.filename)
self.ui.warn(errstr)
raise ValueError(errstr)
flags = [x for x in flags]
self.messagelist[uid] = {'uid': uid, 'flags': flags}
file.close()
@ -95,8 +99,7 @@ class LocalStatusFolder(BaseFolder):
file.write(magicline + "\n")
for msg in self.messagelist.values():
flags = msg['flags']
flags.sort()
flags = ''.join(flags)
flags = ''.join(sorted(flags))
file.write("%s:%s\n" % (msg['uid'], flags))
file.flush()
if self.doautosave:

View File

@ -23,6 +23,11 @@ try:
except:
pass #fail only if needed later on, not on import
try: # python 2.6 has set() built in
set
except NameError:
from sets import Set as set
class LocalStatusSQLiteFolder(LocalStatusFolder):
"""LocalStatus backend implemented with an SQLite database
@ -127,7 +132,6 @@ class LocalStatusSQLiteFolder(LocalStatusFolder):
for line in file.xreadlines():
uid, flags = line.strip().split(':')
uid = long(uid)
flags = list(flags)
flags = ''.join(sorted(flags))
data.append((uid,flags))
self.connection.executemany('INSERT INTO status (id,flags) VALUES (?,?)',
@ -167,7 +171,7 @@ class LocalStatusSQLiteFolder(LocalStatusFolder):
self.messagelist = {}
cursor = self.connection.execute('SELECT id,flags from status')
for row in cursor:
flags = [x for x in row[1]]
flags = set(row[1])
self.messagelist[row[0]] = {'uid': row[0], 'flags': flags}
def save(self):

View File

@ -28,6 +28,11 @@ try:
except ImportError:
from md5 import md5
try: # python 2.6 has set() built in
set
except NameError:
from sets import Set as set
from offlineimap import OfflineImapError
uidmatchre = re.compile(',U=(\d+)')
@ -166,11 +171,12 @@ class MaildirFolder(BaseFolder):
nouidcounter -= 1
else:
uid = long(uidmatch.group(1))
#identify flags in the path name
flagmatch = self.flagmatchre.search(messagename)
flags = []
if flagmatch:
flags = [x for x in flagmatch.group(1)]
flags.sort()
flags = set(flagmatch.group(1))
else:
flags = set()
retval[uid] = {'uid': uid,
'flags': flags,
'filename': file}
@ -261,7 +267,7 @@ class MaildirFolder(BaseFolder):
if rtime != None:
os.utime(os.path.join(tmpdir, messagename), (rtime, rtime))
self.messagelist[uid] = {'uid': uid, 'flags': [],
self.messagelist[uid] = {'uid': uid, 'flags': set(),
'filename': os.path.join('tmp', messagename)}
# savemessageflags moves msg to 'cur' or 'new' as appropriate
self.savemessageflags(uid, flags)
@ -288,8 +294,7 @@ class MaildirFolder(BaseFolder):
infostr = infomatch.group(1)
newname = newname.split(self.infosep)[0] # Strip off the info string.
infostr = re.sub('2,[A-Z]*', '', infostr)
flags.sort()
infostr += '2,' + ''.join(flags)
infostr += '2,' + ''.join(sorted(flags))
newname += infostr
newfilename = os.path.join(dir_prefix, newname)

View File

@ -20,6 +20,11 @@ import re
import string
import types
from offlineimap.ui import getglobalui
try: # python 2.6 has set() built in
set
except NameError:
from sets import Set as set
quotere = re.compile('^("(?:[^"]|\\\\")*")')
def debug(*args):
@ -42,11 +47,21 @@ def dequote(string):
return string
def flagsplit(string):
"""Converts a string of IMAP flags to a list
:returns: E.g. '(\\Draft \\Deleted)' returns ['\\Draft','\\Deleted'].
(FLAGS (\\Seen Old) UID 4807) returns
['FLAGS,'(\\Seen Old)','UID', '4807']
"""
if string[0] != '(' or string[-1] != ')':
raise ValueError, "Passed string '%s' is not a flag list" % string
return imapsplit(string[1:-1])
def options2hash(list):
"""convert list [1,2,3,4,5,6] to {1:2, 3:4, 5:6}"""
# effectively this does dict(zip(l[::2],l[1::2])), however
# measurements seemed to have indicated that the manual variant is
# faster for mosly small lists.
retval = {}
counter = 0
while (counter < len(list)):
@ -55,8 +70,12 @@ def options2hash(list):
debug("options2hash returning:", retval)
return retval
def flags2hash(string):
return options2hash(flagsplit(string))
def flags2hash(flags):
"""Converts IMAP response string from eg IMAP4.fetch() to a hash.
E.g. '(FLAGS (\\Seen Old) UID 4807)' leads to
{'FLAGS': '(\\Seen Old)', 'UID': '4807'}"""
return options2hash(flagsplit(flags))
def imapsplit(imapstring):
"""Takes a string from an IMAP conversation and returns a list containing
@ -152,15 +171,16 @@ flagmap = [('\\Seen', 'S'),
('\\Draft', 'D')]
def flagsimap2maildir(flagstring):
retval = []
imapflaglist = [x.lower() for x in flagstring[1:-1].split()]
"""Convert string '(\\Draft \\Deleted)' into a flags set(DR)"""
retval = set()
imapflaglist = flagstring[1:-1].split()
for imapflag, maildirflag in flagmap:
if imapflag.lower() in imapflaglist:
retval.append(maildirflag)
retval.sort()
if imapflag in imapflaglist:
retval.add(maildirflag)
return retval
def flagsmaildir2imap(maildirflaglist):
"""Convert set of flags ([DR]) into a string '(\\Draft \\Deleted)'"""
retval = []
for imapflag, maildirflag in flagmap:
if maildirflag in maildirflaglist:
@ -198,8 +218,3 @@ def listjoin(list):
retval.append(getlist(start, end))
return ",".join(retval)