mhlib.py revision 3db163aa1952a53712d402f8f460228dde1192ce
154f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum"""MH interface -- purely object-oriented (well, almost) 254f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum 354f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van RossumExecutive summary: 454f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum 554f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossumimport mhlib 654f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum 754f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossummh = mhlib.MH() # use default mailbox directory and profile 854f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossummh = mhlib.MH(mailbox) # override mailbox location (default from profile) 954f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossummh = mhlib.MH(mailbox, profile) # override mailbox and profile 1054f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum 1154f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossummh.error(format, ...) # print error message -- can be overridden 1254f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossums = mh.getprofile(key) # profile entry (None if not set) 1354f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossumpath = mh.getpath() # mailbox pathname 1454f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossumname = mh.getcontext() # name of current folder 1554f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossummh.setcontext(name) # set name of current folder 1654f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum 1754f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossumlist = mh.listfolders() # names of top-level folders 1854f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossumlist = mh.listallfolders() # names of all folders, including subfolders 1954f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossumlist = mh.listsubfolders(name) # direct subfolders of given folder 2054f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossumlist = mh.listallsubfolders(name) # all subfolders of given folder 2154f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum 2254f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossummh.makefolder(name) # create new folder 2354f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossummh.deletefolder(name) # delete folder -- must have no subfolders 2454f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum 2554f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossumf = mh.openfolder(name) # new open folder object 2654f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum 2754f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossumf.error(format, ...) # same as mh.error(format, ...) 2854f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossumpath = f.getfullname() # folder's full pathname 2954f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossumpath = f.getsequencesfilename() # full pathname of folder's sequences file 3054f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossumpath = f.getmessagefilename(n) # full pathname of message n in folder 3154f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum 3254f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossumlist = f.listmessages() # list of messages in folder (as numbers) 3354f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossumn = f.getcurrent() # get current message 3454f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossumf.setcurrent(n) # set current message 3554f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossumlist = f.parsesequence(seq) # parse msgs syntax into list of messages 3654f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossumn = f.getlast() # get last message (0 if no messagse) 3754f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossumf.setlast(n) # set last message (internal use only) 3854f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum 3954f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossumdict = f.getsequences() # dictionary of sequences in folder {name: list} 4054f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossumf.putsequences(dict) # write sequences back to folder 4154f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum 4254f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossumf.createmessage(n, fp) # add message from file f as number n 4354f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossumf.removemessages(list) # remove messages in list from folder 4454f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossumf.refilemessages(list, tofolder) # move messages in list to other folder 4554f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossumf.movemessage(n, tofolder, ton) # move one message to a given destination 4654f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossumf.copymessage(n, tofolder, ton) # copy one message to a given destination 4754f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum 4854f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossumm = f.openmessage(n) # new open message object (costs a file descriptor) 4954f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossumm is a derived class of mimetools.Message(rfc822.Message), with: 5054f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossums = m.getheadertext() # text of message's headers 5154f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossums = m.getheadertext(pred) # text of message's headers, filtered by pred 5254f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossums = m.getbodytext() # text of message's body, decoded 5354f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossums = m.getbodytext(0) # text of message's body, not decoded 5454f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum""" 5554f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum 56560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# XXX To do, functionality: 57560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# - annotate messages 584fe6caaaf082429e778a09043f6dce78b253b701Guido van Rossum# - send messages 59560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# 6040b2cfb3f39e3db4c5f04f0545f9af7d299c2f4fGuido van Rossum# XXX To do, organization: 61560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# - move IntSet to separate file 62560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# - move most Message functionality to module mimetools 63560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum 64560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum 65560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# Customizable defaults 66560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum 67560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van RossumMH_PROFILE = '~/.mh_profile' 68560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van RossumPATH = '~/Mail' 69560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van RossumMH_SEQUENCES = '.mh_sequences' 70560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van RossumFOLDER_PROTECT = 0700 71560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum 72560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum 73560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# Imported modules 74560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum 75560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossumimport os 76508a092e2e44332c2f1ac381bdb956e3b94e0cc2Guido van Rossumimport sys 77560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossumfrom stat import ST_NLINK 789694fcab5332f27dc28b195ba1391e5491d2eaefGuido van Rossumimport re 79560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossumimport mimetools 80560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossumimport multifile 8140b2cfb3f39e3db4c5f04f0545f9af7d299c2f4fGuido van Rossumimport shutil 827cfd31ee8a1447e177f129c4e3f1bfd937528043Guido van Rossumfrom bisect import bisect 83560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum 8417ab123cf1f0597e7e257c1ce83a6e87b85ffd7bSkip Montanaro__all__ = ["MH","Error","Folder","Message"] 85560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum 86560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# Exported constants 87560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum 88ffdc48f45c7f942192ad19c26766207826ba99fdFred Drakeclass Error(Exception): 89ffdc48f45c7f942192ad19c26766207826ba99fdFred Drake pass 90560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum 91560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum 92560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossumclass MH: 9354f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum """Class representing a particular collection of folders. 9454f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum Optional constructor arguments are the pathname for the directory 9554f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum containing the collection, and the MH profile to use. 9654f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum If either is omitted or empty a default is used; the default 9754f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum directory is taken from the MH profile if it is specified there.""" 98560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum 990c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum def __init__(self, path = None, profile = None): 10054f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum """Constructor.""" 10145e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if not profile: profile = MH_PROFILE 10245e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum self.profile = os.path.expanduser(profile) 10345e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if not path: path = self.getprofile('Path') 10445e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if not path: path = PATH 10545e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if not os.path.isabs(path) and path[0] != '~': 10645e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum path = os.path.join('~', path) 10745e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum path = os.path.expanduser(path) 10845e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if not os.path.isdir(path): raise Error, 'MH() path not found' 10945e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum self.path = path 1100c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum 1110c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum def __repr__(self): 11254f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum """String representation.""" 11345e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum return 'MH(%s, %s)' % (`self.path`, `self.profile`) 1140c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum 1150c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum def error(self, msg, *args): 11654f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum """Routine to print an error. May be overridden by a derived class.""" 11745e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum sys.stderr.write('MH error: %s\n' % (msg % args)) 1180c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum 1190c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum def getprofile(self, key): 12054f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum """Return a profile entry, None if not found.""" 12145e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum return pickline(self.profile, key) 1220c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum 1230c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum def getpath(self): 12454f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum """Return the path (the name of the collection's directory).""" 12545e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum return self.path 1260c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum 1270c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum def getcontext(self): 12854f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum """Return the name of the current folder.""" 12945e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum context = pickline(os.path.join(self.getpath(), 'context'), 13045e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum 'Current-Folder') 13145e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if not context: context = 'inbox' 13245e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum return context 1330c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum 1340c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum def setcontext(self, context): 13554f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum """Set the name of the current folder.""" 13645e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum fn = os.path.join(self.getpath(), 'context') 13745e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum f = open(fn, "w") 13845e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum f.write("Current-Folder: %s\n" % context) 13945e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum f.close() 1400c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum 1410c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum def listfolders(self): 14254f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum """Return the names of the top-level folders.""" 14345e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum folders = [] 14445e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum path = self.getpath() 14545e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum for name in os.listdir(path): 14645e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum fullname = os.path.join(path, name) 14745e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if os.path.isdir(fullname): 14845e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum folders.append(name) 14945e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum folders.sort() 15045e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum return folders 1510c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum 1520c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum def listsubfolders(self, name): 15354f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum """Return the names of the subfolders in a given folder 15454f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum (prefixed with the given folder name).""" 15545e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum fullname = os.path.join(self.path, name) 15645e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum # Get the link count so we can avoid listing folders 15745e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum # that have no subfolders. 15845e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum st = os.stat(fullname) 15945e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum nlinks = st[ST_NLINK] 16045e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if nlinks <= 2: 16145e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum return [] 16245e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum subfolders = [] 16345e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum subnames = os.listdir(fullname) 16445e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum for subname in subnames: 16545e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum fullsubname = os.path.join(fullname, subname) 16645e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if os.path.isdir(fullsubname): 16745e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum name_subname = os.path.join(name, subname) 16845e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum subfolders.append(name_subname) 16945e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum # Stop looking for subfolders when 17045e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum # we've seen them all 17145e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum nlinks = nlinks - 1 17245e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if nlinks <= 2: 17345e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum break 17445e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum subfolders.sort() 17545e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum return subfolders 1760c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum 1770c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum def listallfolders(self): 17854f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum """Return the names of all folders and subfolders, recursively.""" 17945e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum return self.listallsubfolders('') 1800c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum 1810c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum def listallsubfolders(self, name): 18254f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum """Return the names of subfolders in a given folder, recursively.""" 18345e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum fullname = os.path.join(self.path, name) 18445e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum # Get the link count so we can avoid listing folders 18545e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum # that have no subfolders. 18645e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum st = os.stat(fullname) 18745e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum nlinks = st[ST_NLINK] 18845e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if nlinks <= 2: 18945e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum return [] 19045e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum subfolders = [] 19145e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum subnames = os.listdir(fullname) 19245e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum for subname in subnames: 19345e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if subname[0] == ',' or isnumeric(subname): continue 19445e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum fullsubname = os.path.join(fullname, subname) 19545e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if os.path.isdir(fullsubname): 19645e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum name_subname = os.path.join(name, subname) 19745e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum subfolders.append(name_subname) 19845e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if not os.path.islink(fullsubname): 19945e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum subsubfolders = self.listallsubfolders( 20045e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum name_subname) 20145e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum subfolders = subfolders + subsubfolders 20245e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum # Stop looking for subfolders when 20345e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum # we've seen them all 20445e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum nlinks = nlinks - 1 20545e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if nlinks <= 2: 20645e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum break 20745e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum subfolders.sort() 20845e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum return subfolders 2090c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum 2100c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum def openfolder(self, name): 21154f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum """Return a new Folder object for the named folder.""" 21245e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum return Folder(self, name) 2130c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum 2140c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum def makefolder(self, name): 21554f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum """Create a new folder (or raise os.error if it cannot be created).""" 21645e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum protect = pickline(self.profile, 'Folder-Protect') 21745e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if protect and isnumeric(protect): 21866d9919cab903e152d206b7edb853799eca564d0Eric S. Raymond mode = int(protect, 8) 21945e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum else: 22045e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum mode = FOLDER_PROTECT 22145e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum os.mkdir(os.path.join(self.getpath(), name), mode) 2220c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum 2230c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum def deletefolder(self, name): 22454f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum """Delete a folder. This removes files in the folder but not 22554f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum subdirectories. Raise os.error if deleting the folder itself fails.""" 22645e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum fullname = os.path.join(self.getpath(), name) 22745e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum for subname in os.listdir(fullname): 22845e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum fullsubname = os.path.join(fullname, subname) 22945e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum try: 23045e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum os.unlink(fullsubname) 23145e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum except os.error: 23245e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum self.error('%s not deleted, continuing...' % 23345e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum fullsubname) 23445e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum os.rmdir(fullname) 235560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum 236560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum 2379694fcab5332f27dc28b195ba1391e5491d2eaefGuido van Rossumnumericprog = re.compile('^[1-9][0-9]*$') 238560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossumdef isnumeric(str): 2399694fcab5332f27dc28b195ba1391e5491d2eaefGuido van Rossum return numericprog.match(str) is not None 240560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum 241560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossumclass Folder: 24254f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum """Class representing a particular folder.""" 243560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum 2440c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum def __init__(self, mh, name): 24554f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum """Constructor.""" 24645e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum self.mh = mh 24745e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum self.name = name 24845e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if not os.path.isdir(self.getfullname()): 24945e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum raise Error, 'no folder %s' % name 2500c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum 2510c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum def __repr__(self): 25254f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum """String representation.""" 25345e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum return 'Folder(%s, %s)' % (`self.mh`, `self.name`) 2540c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum 2550c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum def error(self, *args): 25654f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum """Error message handler.""" 25745e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum apply(self.mh.error, args) 2580c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum 2590c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum def getfullname(self): 26054f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum """Return the full pathname of the folder.""" 26145e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum return os.path.join(self.mh.path, self.name) 2620c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum 2630c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum def getsequencesfilename(self): 26454f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum """Return the full pathname of the folder's sequences file.""" 26545e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum return os.path.join(self.getfullname(), MH_SEQUENCES) 2660c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum 2670c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum def getmessagefilename(self, n): 26854f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum """Return the full pathname of a message in the folder.""" 26945e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum return os.path.join(self.getfullname(), str(n)) 2700c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum 2710c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum def listsubfolders(self): 27254f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum """Return list of direct subfolders.""" 27345e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum return self.mh.listsubfolders(self.name) 2740c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum 2750c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum def listallsubfolders(self): 27654f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum """Return list of all subfolders.""" 27745e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum return self.mh.listallsubfolders(self.name) 2780c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum 2790c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum def listmessages(self): 28054f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum """Return the list of messages currently present in the folder. 28154f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum As a side effect, set self.last to the last message (or 0).""" 28245e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum messages = [] 28345e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum match = numericprog.match 28445e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum append = messages.append 28545e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum for name in os.listdir(self.getfullname()): 286d9d2625dbd284f91df2beab22f385e0f0bf60cabGuido van Rossum if match(name): 28745e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum append(name) 28866d9919cab903e152d206b7edb853799eca564d0Eric S. Raymond messages = map(int, messages) 28945e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum messages.sort() 29045e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if messages: 29145e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum self.last = messages[-1] 29245e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum else: 29345e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum self.last = 0 29445e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum return messages 295560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum 2960c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum def getsequences(self): 29754f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum """Return the set of sequences for the folder.""" 29845e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum sequences = {} 29945e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum fullname = self.getsequencesfilename() 30045e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum try: 30145e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum f = open(fullname, 'r') 30245e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum except IOError: 30345e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum return sequences 30445e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum while 1: 30545e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum line = f.readline() 30645e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if not line: break 30766d9919cab903e152d206b7edb853799eca564d0Eric S. Raymond fields = line.split(':') 3088152d32375c40bba9ccbe43b780ebe96d9617781Fred Drake if len(fields) != 2: 30945e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum self.error('bad sequence in %s: %s' % 31066d9919cab903e152d206b7edb853799eca564d0Eric S. Raymond (fullname, line.strip())) 31166d9919cab903e152d206b7edb853799eca564d0Eric S. Raymond key = fields[0].strip() 31266d9919cab903e152d206b7edb853799eca564d0Eric S. Raymond value = IntSet(fields[1].strip(), ' ').tolist() 31345e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum sequences[key] = value 31445e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum return sequences 3150c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum 3160c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum def putsequences(self, sequences): 31754f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum """Write the set of sequences back to the folder.""" 31845e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum fullname = self.getsequencesfilename() 31945e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum f = None 32045e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum for key in sequences.keys(): 32145e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum s = IntSet('', ' ') 32245e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum s.fromlist(sequences[key]) 32345e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if not f: f = open(fullname, 'w') 32445e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum f.write('%s: %s\n' % (key, s.tostring())) 32545e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if not f: 32645e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum try: 32745e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum os.unlink(fullname) 32845e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum except os.error: 32945e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum pass 33045e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum else: 33145e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum f.close() 332560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum 3330c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum def getcurrent(self): 334ffdc48f45c7f942192ad19c26766207826ba99fdFred Drake """Return the current message. Raise Error when there is none.""" 33545e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum seqs = self.getsequences() 33645e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum try: 33745e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum return max(seqs['cur']) 33845e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum except (ValueError, KeyError): 33945e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum raise Error, "no cur message" 3400c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum 3410c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum def setcurrent(self, n): 34254f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum """Set the current message.""" 34345e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum updateline(self.getsequencesfilename(), 'cur', str(n), 0) 3440c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum 3450c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum def parsesequence(self, seq): 34654f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum """Parse an MH sequence specification into a message list. 34754f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum Attempt to mimic mh-sequence(5) as close as possible. 34854f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum Also attempt to mimic observed behavior regarding which 34954f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum conditions cause which error messages.""" 35045e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum # XXX Still not complete (see mh-format(5)). 35145e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum # Missing are: 35245e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum # - 'prev', 'next' as count 35345e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum # - Sequence-Negation option 35445e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum all = self.listmessages() 35545e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum # Observed behavior: test for empty folder is done first 35645e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if not all: 35745e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum raise Error, "no messages in %s" % self.name 35845e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum # Common case first: all is frequently the default 35945e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if seq == 'all': 36045e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum return all 36145e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum # Test for X:Y before X-Y because 'seq:-n' matches both 36266d9919cab903e152d206b7edb853799eca564d0Eric S. Raymond i = seq.find(':') 36345e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if i >= 0: 36445e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum head, dir, tail = seq[:i], '', seq[i+1:] 36545e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if tail[:1] in '-+': 36645e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum dir, tail = tail[:1], tail[1:] 36745e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if not isnumeric(tail): 36845e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum raise Error, "bad message list %s" % seq 36945e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum try: 37066d9919cab903e152d206b7edb853799eca564d0Eric S. Raymond count = int(tail) 37145e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum except (ValueError, OverflowError): 37245e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum # Can't use sys.maxint because of i+count below 37345e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum count = len(all) 37445e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum try: 37545e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum anchor = self._parseindex(head, all) 37645e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum except Error, msg: 37745e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum seqs = self.getsequences() 37845e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if not seqs.has_key(head): 37945e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if not msg: 38045e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum msg = "bad message list %s" % seq 38145e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum raise Error, msg, sys.exc_info()[2] 38245e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum msgs = seqs[head] 38345e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if not msgs: 38445e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum raise Error, "sequence %s empty" % head 38545e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if dir == '-': 38645e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum return msgs[-count:] 38745e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum else: 38845e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum return msgs[:count] 38945e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum else: 39045e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if not dir: 39145e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if head in ('prev', 'last'): 39245e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum dir = '-' 39345e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if dir == '-': 39445e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum i = bisect(all, anchor) 39545e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum return all[max(0, i-count):i] 39645e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum else: 39745e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum i = bisect(all, anchor-1) 39845e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum return all[i:i+count] 39945e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum # Test for X-Y next 40066d9919cab903e152d206b7edb853799eca564d0Eric S. Raymond i = seq.find('-') 40145e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if i >= 0: 40245e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum begin = self._parseindex(seq[:i], all) 40345e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum end = self._parseindex(seq[i+1:], all) 40445e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum i = bisect(all, begin-1) 40545e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum j = bisect(all, end) 40645e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum r = all[i:j] 40745e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if not r: 40845e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum raise Error, "bad message list %s" % seq 40945e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum return r 41045e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum # Neither X:Y nor X-Y; must be a number or a (pseudo-)sequence 41145e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum try: 41245e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum n = self._parseindex(seq, all) 41345e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum except Error, msg: 41445e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum seqs = self.getsequences() 41545e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if not seqs.has_key(seq): 41645e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if not msg: 41745e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum msg = "bad message list %s" % seq 41845e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum raise Error, msg 41945e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum return seqs[seq] 42045e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum else: 42145e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if n not in all: 42245e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if isnumeric(seq): 42345e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum raise Error, "message %d doesn't exist" % n 42445e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum else: 42545e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum raise Error, "no %s message" % seq 42645e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum else: 42745e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum return [n] 4280c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum 4290c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum def _parseindex(self, seq, all): 43054f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum """Internal: parse a message number (or cur, first, etc.).""" 43145e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if isnumeric(seq): 43245e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum try: 43366d9919cab903e152d206b7edb853799eca564d0Eric S. Raymond return int(seq) 43445e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum except (OverflowError, ValueError): 43545e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum return sys.maxint 43645e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if seq in ('cur', '.'): 43745e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum return self.getcurrent() 43845e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if seq == 'first': 43945e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum return all[0] 44045e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if seq == 'last': 44145e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum return all[-1] 44245e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if seq == 'next': 44345e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum n = self.getcurrent() 44445e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum i = bisect(all, n) 44545e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum try: 44645e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum return all[i] 44745e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum except IndexError: 44845e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum raise Error, "no next message" 44945e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if seq == 'prev': 45045e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum n = self.getcurrent() 45145e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum i = bisect(all, n-1) 45245e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if i == 0: 45345e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum raise Error, "no prev message" 45445e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum try: 45545e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum return all[i-1] 45645e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum except IndexError: 45745e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum raise Error, "no prev message" 45845e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum raise Error, None 4590c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum 4600c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum def openmessage(self, n): 46154f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum """Open a message -- returns a Message object.""" 46245e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum return Message(self, n) 4630c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum 4640c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum def removemessages(self, list): 46554f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum """Remove one or more messages -- may raise os.error.""" 46645e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum errors = [] 46745e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum deleted = [] 46845e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum for n in list: 46945e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum path = self.getmessagefilename(n) 47045e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum commapath = self.getmessagefilename(',' + str(n)) 47145e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum try: 47245e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum os.unlink(commapath) 47345e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum except os.error: 47445e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum pass 47545e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum try: 47645e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum os.rename(path, commapath) 47745e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum except os.error, msg: 47845e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum errors.append(msg) 47945e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum else: 48045e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum deleted.append(n) 48145e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if deleted: 48245e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum self.removefromallsequences(deleted) 48345e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if errors: 48445e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if len(errors) == 1: 48545e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum raise os.error, errors[0] 48645e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum else: 48745e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum raise os.error, ('multiple errors:', errors) 4880c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum 4890c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum def refilemessages(self, list, tofolder, keepsequences=0): 49054f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum """Refile one or more messages -- may raise os.error. 49154f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum 'tofolder' is an open folder object.""" 49245e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum errors = [] 49345e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum refiled = {} 49445e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum for n in list: 49545e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum ton = tofolder.getlast() + 1 49645e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum path = self.getmessagefilename(n) 49745e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum topath = tofolder.getmessagefilename(ton) 49845e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum try: 49945e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum os.rename(path, topath) 50045e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum except os.error: 50145e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum # Try copying 50245e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum try: 50345e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum shutil.copy2(path, topath) 50445e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum os.unlink(path) 50545e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum except (IOError, os.error), msg: 50645e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum errors.append(msg) 50745e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum try: 50845e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum os.unlink(topath) 50945e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum except os.error: 51045e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum pass 51145e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum continue 51245e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum tofolder.setlast(ton) 51345e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum refiled[n] = ton 51445e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if refiled: 51545e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if keepsequences: 51645e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum tofolder._copysequences(self, refiled.items()) 51745e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum self.removefromallsequences(refiled.keys()) 51845e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if errors: 51945e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if len(errors) == 1: 52045e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum raise os.error, errors[0] 52145e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum else: 52245e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum raise os.error, ('multiple errors:', errors) 5230c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum 5240c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum def _copysequences(self, fromfolder, refileditems): 52554f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum """Helper for refilemessages() to copy sequences.""" 52645e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum fromsequences = fromfolder.getsequences() 52745e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum tosequences = self.getsequences() 52845e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum changed = 0 52945e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum for name, seq in fromsequences.items(): 53045e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum try: 53145e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum toseq = tosequences[name] 53245e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum new = 0 5333db163aa1952a53712d402f8f460228dde1192ceunknown except KeyError: 53445e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum toseq = [] 53545e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum new = 1 53645e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum for fromn, ton in refileditems: 53745e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if fromn in seq: 53845e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum toseq.append(ton) 53945e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum changed = 1 54045e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if new and toseq: 54145e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum tosequences[name] = toseq 54245e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if changed: 54345e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum self.putsequences(tosequences) 5440c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum 5450c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum def movemessage(self, n, tofolder, ton): 54654f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum """Move one message over a specific destination message, 54754f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum which may or may not already exist.""" 54845e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum path = self.getmessagefilename(n) 54945e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum # Open it to check that it exists 55045e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum f = open(path) 55145e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum f.close() 55245e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum del f 55345e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum topath = tofolder.getmessagefilename(ton) 55445e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum backuptopath = tofolder.getmessagefilename(',%d' % ton) 55545e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum try: 55645e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum os.rename(topath, backuptopath) 55745e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum except os.error: 55845e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum pass 55945e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum try: 56045e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum os.rename(path, topath) 56145e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum except os.error: 56245e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum # Try copying 56345e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum ok = 0 56445e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum try: 56545e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum tofolder.setlast(None) 56645e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum shutil.copy2(path, topath) 56745e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum ok = 1 56845e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum finally: 56945e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if not ok: 57045e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum try: 57145e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum os.unlink(topath) 57245e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum except os.error: 57345e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum pass 57445e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum os.unlink(path) 57545e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum self.removefromallsequences([n]) 5760c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum 5770c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum def copymessage(self, n, tofolder, ton): 57854f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum """Copy one message over a specific destination message, 57954f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum which may or may not already exist.""" 58045e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum path = self.getmessagefilename(n) 58145e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum # Open it to check that it exists 58245e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum f = open(path) 58345e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum f.close() 58445e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum del f 58545e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum topath = tofolder.getmessagefilename(ton) 58645e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum backuptopath = tofolder.getmessagefilename(',%d' % ton) 58745e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum try: 58845e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum os.rename(topath, backuptopath) 58945e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum except os.error: 59045e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum pass 59145e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum ok = 0 59245e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum try: 59345e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum tofolder.setlast(None) 59445e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum shutil.copy2(path, topath) 59545e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum ok = 1 59645e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum finally: 59745e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if not ok: 59845e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum try: 59945e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum os.unlink(topath) 60045e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum except os.error: 60145e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum pass 6020c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum 6034e5cbcf5afa411d654983cd4cf8c99739b935b26Guido van Rossum def createmessage(self, n, txt): 60454f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum """Create a message, with text from the open file txt.""" 60545e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum path = self.getmessagefilename(n) 60645e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum backuppath = self.getmessagefilename(',%d' % n) 60745e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum try: 60845e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum os.rename(path, backuppath) 60945e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum except os.error: 61045e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum pass 61145e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum ok = 0 61245e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum BUFSIZE = 16*1024 61345e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum try: 61445e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum f = open(path, "w") 61545e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum while 1: 61645e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum buf = txt.read(BUFSIZE) 61745e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if not buf: 61845e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum break 61945e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum f.write(buf) 62045e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum f.close() 62145e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum ok = 1 62245e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum finally: 62345e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if not ok: 62445e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum try: 62545e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum os.unlink(path) 62645e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum except os.error: 62745e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum pass 6284e5cbcf5afa411d654983cd4cf8c99739b935b26Guido van Rossum 6290c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum def removefromallsequences(self, list): 6307e47402264cf87b9bbb61fc9ff610af08add7c7bThomas Wouters """Remove one or more messages from all sequences (including last) 63154f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum -- but not from 'cur'!!!""" 63245e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if hasattr(self, 'last') and self.last in list: 63345e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum del self.last 63445e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum sequences = self.getsequences() 63545e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum changed = 0 63645e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum for name, seq in sequences.items(): 63745e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if name == 'cur': 63845e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum continue 63945e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum for n in list: 64045e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if n in seq: 64145e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum seq.remove(n) 64245e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum changed = 1 64345e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if not seq: 64445e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum del sequences[name] 64545e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if changed: 64645e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum self.putsequences(sequences) 6470c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum 6480c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum def getlast(self): 64954f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum """Return the last message number.""" 65045e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if not hasattr(self, 'last'): 65145e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum messages = self.listmessages() 65245e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum return self.last 6530c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum 6540c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum def setlast(self, last): 65554f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum """Set the last message number.""" 65645e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if last is None: 65745e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if hasattr(self, 'last'): 65845e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum del self.last 65945e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum else: 66045e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum self.last = last 661560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum 662560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossumclass Message(mimetools.Message): 663560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum 6640c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum def __init__(self, f, n, fp = None): 66554f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum """Constructor.""" 66645e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum self.folder = f 66745e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum self.number = n 66845e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if not fp: 66945e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum path = f.getmessagefilename(n) 67045e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum fp = open(path, 'r') 67145e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum mimetools.Message.__init__(self, fp) 6720c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum 6730c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum def __repr__(self): 67454f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum """String representation.""" 67545e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum return 'Message(%s, %s)' % (repr(self.folder), self.number) 6760c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum 6770c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum def getheadertext(self, pred = None): 67854f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum """Return the message's header text as a string. If an 67954f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum argument is specified, it is used as a filter predicate to 68054f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum decide which headers to return (its argument is the header 68154f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum name converted to lower case).""" 68245e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if not pred: 68366d9919cab903e152d206b7edb853799eca564d0Eric S. Raymond return ''.join(self.headers) 68445e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum headers = [] 68545e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum hit = 0 68645e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum for line in self.headers: 6876e025bcde84ec358734a116cefcc4acc357ee0b1Eric S. Raymond if not line[0].isspace(): 68866d9919cab903e152d206b7edb853799eca564d0Eric S. Raymond i = line.find(':') 68945e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if i > 0: 69066d9919cab903e152d206b7edb853799eca564d0Eric S. Raymond hit = pred(line[:i].lower()) 69145e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if hit: headers.append(line) 692c9838f9fcbf138b454606d606f7b53a5be21b5aeEric S. Raymond return ''.join(headers) 6930c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum 6940c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum def getbodytext(self, decode = 1): 69554f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum """Return the message's body text as string. This undoes a 69654f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum Content-Transfer-Encoding, but does not interpret other MIME 69754f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum features (e.g. multipart messages). To suppress decoding, 69854f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum pass 0 as an argument.""" 69945e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum self.fp.seek(self.startofbody) 70045e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum encoding = self.getencoding() 7014fe6caaaf082429e778a09043f6dce78b253b701Guido van Rossum if not decode or encoding in ('', '7bit', '8bit', 'binary'): 70245e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum return self.fp.read() 70345e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum from StringIO import StringIO 70445e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum output = StringIO() 70545e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum mimetools.decode(self.fp, output, encoding) 70645e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum return output.getvalue() 7070c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum 7080c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum def getbodyparts(self): 70954f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum """Only for multipart messages: return the message's body as a 71054f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum list of SubMessage objects. Each submessage object behaves 71154f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum (almost) as a Message object.""" 71245e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if self.getmaintype() != 'multipart': 71345e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum raise Error, 'Content-Type is not multipart/*' 71445e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum bdry = self.getparam('boundary') 71545e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if not bdry: 71645e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum raise Error, 'multipart/* without boundary param' 71745e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum self.fp.seek(self.startofbody) 71845e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum mf = multifile.MultiFile(self.fp) 71945e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum mf.push(bdry) 72045e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum parts = [] 72145e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum while mf.next(): 72245e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum n = str(self.number) + '.' + `1 + len(parts)` 72345e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum part = SubMessage(self.folder, n, mf) 72445e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum parts.append(part) 72545e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum mf.pop() 72645e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum return parts 7270c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum 7280c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum def getbody(self): 72954f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum """Return body, either a string or a list of messages.""" 73045e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if self.getmaintype() == 'multipart': 73145e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum return self.getbodyparts() 73245e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum else: 73345e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum return self.getbodytext() 734560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum 735560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum 736560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossumclass SubMessage(Message): 737560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum 7380c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum def __init__(self, f, n, fp): 73954f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum """Constructor.""" 74045e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum Message.__init__(self, f, n, fp) 74145e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if self.getmaintype() == 'multipart': 74245e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum self.body = Message.getbodyparts(self) 74345e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum else: 74445e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum self.body = Message.getbodytext(self) 7454fe6caaaf082429e778a09043f6dce78b253b701Guido van Rossum self.bodyencoded = Message.getbodytext(self, decode=0) 74645e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum # XXX If this is big, should remember file pointers 747560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum 7480c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum def __repr__(self): 74954f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum """String representation.""" 75045e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum f, n, fp = self.folder, self.number, self.fp 75145e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum return 'SubMessage(%s, %s, %s)' % (f, n, fp) 752560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum 7534fe6caaaf082429e778a09043f6dce78b253b701Guido van Rossum def getbodytext(self, decode = 1): 7544fe6caaaf082429e778a09043f6dce78b253b701Guido van Rossum if not decode: 7554fe6caaaf082429e778a09043f6dce78b253b701Guido van Rossum return self.bodyencoded 75645e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if type(self.body) == type(''): 75745e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum return self.body 758560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum 7590c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum def getbodyparts(self): 76045e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if type(self.body) == type([]): 76145e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum return self.body 762560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum 7630c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum def getbody(self): 76445e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum return self.body 765560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum 766560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum 767560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossumclass IntSet: 76854f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum """Class implementing sets of integers. 76954f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum 77054f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum This is an efficient representation for sets consisting of several 77154f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum continuous ranges, e.g. 1-100,200-400,402-1000 is represented 77254f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum internally as a list of three pairs: [(1,100), (200,400), 77354f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum (402,1000)]. The internal representation is always kept normalized. 77454f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum 77554f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum The constructor has up to three arguments: 77654f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum - the string used to initialize the set (default ''), 77754f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum - the separator between ranges (default ',') 77854f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum - the separator between begin and end of a range (default '-') 77954f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum The separators must be strings (not regexprs) and should be different. 78054f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum 78154f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum The tostring() function yields a string that can be passed to another 78254f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum IntSet constructor; __repr__() is a valid IntSet constructor itself. 78354f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum """ 78454f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum 78554f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum # XXX The default begin/end separator means that negative numbers are 78654f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum # not supported very well. 78754f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum # 78854f22ed30bab2e64909ba2d79205cb4b87c69db2Guido van Rossum # XXX There are currently no operations to remove set elements. 789560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum 7900c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum def __init__(self, data = None, sep = ',', rng = '-'): 79145e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum self.pairs = [] 79245e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum self.sep = sep 79345e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum self.rng = rng 79445e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if data: self.fromstring(data) 7950c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum 7960c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum def reset(self): 79745e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum self.pairs = [] 7980c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum 7990c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum def __cmp__(self, other): 80045e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum return cmp(self.pairs, other.pairs) 8010c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum 8020c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum def __hash__(self): 80345e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum return hash(self.pairs) 8040c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum 8050c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum def __repr__(self): 80645e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum return 'IntSet(%s, %s, %s)' % (`self.tostring()`, 80745e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum `self.sep`, `self.rng`) 8080c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum 8090c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum def normalize(self): 81045e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum self.pairs.sort() 81145e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum i = 1 81245e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum while i < len(self.pairs): 81345e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum alo, ahi = self.pairs[i-1] 81445e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum blo, bhi = self.pairs[i] 81545e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if ahi >= blo-1: 81645e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum self.pairs[i-1:i+1] = [(alo, max(ahi, bhi))] 81745e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum else: 81845e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum i = i+1 8190c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum 8200c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum def tostring(self): 82145e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum s = '' 82245e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum for lo, hi in self.pairs: 82345e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if lo == hi: t = `lo` 82445e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum else: t = `lo` + self.rng + `hi` 82545e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if s: s = s + (self.sep + t) 82645e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum else: s = t 82745e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum return s 8280c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum 8290c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum def tolist(self): 83045e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum l = [] 83145e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum for lo, hi in self.pairs: 83245e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum m = range(lo, hi+1) 83345e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum l = l + m 83445e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum return l 8350c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum 8360c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum def fromlist(self, list): 83745e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum for i in list: 83845e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum self.append(i) 8390c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum 8400c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum def clone(self): 84145e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum new = IntSet() 84245e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum new.pairs = self.pairs[:] 84345e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum return new 8440c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum 8450c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum def min(self): 84645e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum return self.pairs[0][0] 8470c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum 8480c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum def max(self): 84945e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum return self.pairs[-1][-1] 8500c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum 8510c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum def contains(self, x): 85245e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum for lo, hi in self.pairs: 85345e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if lo <= x <= hi: return 1 85445e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum return 0 8550c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum 8560c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum def append(self, x): 85745e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum for i in range(len(self.pairs)): 85845e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum lo, hi = self.pairs[i] 85945e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if x < lo: # Need to insert before 86045e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if x+1 == lo: 86145e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum self.pairs[i] = (x, hi) 86245e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum else: 86345e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum self.pairs.insert(i, (x, x)) 86445e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if i > 0 and x-1 == self.pairs[i-1][1]: 86545e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum # Merge with previous 86645e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum self.pairs[i-1:i+1] = [ 86745e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum (self.pairs[i-1][0], 86845e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum self.pairs[i][1]) 86945e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum ] 87045e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum return 87145e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if x <= hi: # Already in set 87245e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum return 87345e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum i = len(self.pairs) - 1 87445e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if i >= 0: 87545e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum lo, hi = self.pairs[i] 87645e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if x-1 == hi: 87745e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum self.pairs[i] = lo, x 87845e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum return 87945e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum self.pairs.append((x, x)) 8800c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum 8810c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum def addpair(self, xlo, xhi): 88245e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if xlo > xhi: return 88345e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum self.pairs.append((xlo, xhi)) 88445e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum self.normalize() 8850c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum 8860c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum def fromstring(self, data): 88745e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum new = [] 88866d9919cab903e152d206b7edb853799eca564d0Eric S. Raymond for part in data.split(self.sep): 88945e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum list = [] 89066d9919cab903e152d206b7edb853799eca564d0Eric S. Raymond for subp in part.split(self.rng): 89166d9919cab903e152d206b7edb853799eca564d0Eric S. Raymond s = subp.strip() 89266d9919cab903e152d206b7edb853799eca564d0Eric S. Raymond list.append(int(s)) 89345e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if len(list) == 1: 89445e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum new.append((list[0], list[0])) 89545e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum elif len(list) == 2 and list[0] <= list[1]: 89645e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum new.append((list[0], list[1])) 89745e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum else: 89845e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum raise ValueError, 'bad data passed to IntSet' 89945e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum self.pairs = self.pairs + new 90045e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum self.normalize() 901560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum 902560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum 903560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# Subroutines to read/write entries in .mh_profile and .mh_sequences 904560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum 905560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossumdef pickline(file, key, casefold = 1): 9060c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum try: 90745e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum f = open(file, 'r') 9080c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum except IOError: 90945e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum return None 9109694fcab5332f27dc28b195ba1391e5491d2eaefGuido van Rossum pat = re.escape(key) + ':' 9119694fcab5332f27dc28b195ba1391e5491d2eaefGuido van Rossum prog = re.compile(pat, casefold and re.IGNORECASE) 9120c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum while 1: 91345e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum line = f.readline() 91445e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if not line: break 91545e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if prog.match(line): 91645e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum text = line[len(key)+1:] 91745e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum while 1: 91845e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum line = f.readline() 9196e025bcde84ec358734a116cefcc4acc357ee0b1Eric S. Raymond if not line or not line[0].isspace(): 92045e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum break 92145e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum text = text + line 92266d9919cab903e152d206b7edb853799eca564d0Eric S. Raymond return text.strip() 9230c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum return None 924560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum 925560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossumdef updateline(file, key, value, casefold = 1): 9260c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum try: 92745e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum f = open(file, 'r') 92845e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum lines = f.readlines() 92945e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum f.close() 9300c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum except IOError: 93145e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum lines = [] 9329694fcab5332f27dc28b195ba1391e5491d2eaefGuido van Rossum pat = re.escape(key) + ':(.*)\n' 9339694fcab5332f27dc28b195ba1391e5491d2eaefGuido van Rossum prog = re.compile(pat, casefold and re.IGNORECASE) 9340c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum if value is None: 93545e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum newline = None 9360c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum else: 93745e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum newline = '%s: %s\n' % (key, value) 9380c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum for i in range(len(lines)): 93945e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum line = lines[i] 94045e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if prog.match(line): 94145e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if newline is None: 94245e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum del lines[i] 94345e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum else: 94445e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum lines[i] = newline 94545e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum break 9460c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum else: 94745e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum if newline is not None: 94845e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum lines.append(newline) 9490c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum tempfile = file + "~" 9500c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum f = open(tempfile, 'w') 9510c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum for line in lines: 95245e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum f.write(line) 9530c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum f.close() 9540c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum os.rename(tempfile, file) 955560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum 956560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum 957560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum# Test program 958560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum 959560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossumdef test(): 9600c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum global mh, f 9610c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum os.system('rm -rf $HOME/Mail/@test') 9620c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum mh = MH() 9630c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum def do(s): print s; print eval(s) 9640c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum do('mh.listfolders()') 9650c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum do('mh.listallfolders()') 9660c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum testfolders = ['@test', '@test/test1', '@test/test2', 96745e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum '@test/test1/test11', '@test/test1/test12', 96845e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum '@test/test1/test11/test111'] 9690c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum for t in testfolders: do('mh.makefolder(%s)' % `t`) 9700c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum do('mh.listsubfolders(\'@test\')') 9710c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum do('mh.listallsubfolders(\'@test\')') 9720c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum f = mh.openfolder('@test') 9730c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum do('f.listsubfolders()') 9740c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum do('f.listallsubfolders()') 9750c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum do('f.getsequences()') 9760c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum seqs = f.getsequences() 9770c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum seqs['foo'] = IntSet('1-10 12-20', ' ').tolist() 9780c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum print seqs 9790c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum f.putsequences(seqs) 9800c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum do('f.getsequences()') 9810c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum testfolders.reverse() 9820c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum for t in testfolders: do('mh.deletefolder(%s)' % `t`) 9830c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum do('mh.getcontext()') 9840c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum context = mh.getcontext() 9850c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum f = mh.openfolder(context) 9860c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum do('f.getcurrent()') 9870c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum for seq in ['first', 'last', 'cur', '.', 'prev', 'next', 98845e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum 'first:3', 'last:3', 'cur:3', 'cur:-3', 98945e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum 'prev:3', 'next:3', 99045e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum '1:3', '1:-3', '100:3', '100:-3', '10000:3', '10000:-3', 99145e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum 'all']: 99245e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum try: 99345e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum do('f.parsesequence(%s)' % `seq`) 99445e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum except Error, msg: 99545e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum print "Error:", msg 99645e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum stuff = os.popen("pick %s 2>/dev/null" % `seq`).read() 99766d9919cab903e152d206b7edb853799eca564d0Eric S. Raymond list = map(int, stuff.split()) 99845e2fbc2e70ef28b1f0327207f33dab3a4e825c5Guido van Rossum print list, "<-- pick" 9990c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum do('f.listmessages()') 1000560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum 1001560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossum 1002560131328c9ddfa25555048b1bf4d760c02e1dd7Guido van Rossumif __name__ == '__main__': 10030c5e049c751c45aca40e26e3f1f76597798b86d9Guido van Rossum test() 1004