Rewrite parsesequence() to emulate MH without invoking pick.
Test it extensively by using pick.
This commit is contained in:
parent
5e92affc54
commit
7cfd31ee8a
170
Lib/mhlib.py
170
Lib/mhlib.py
|
@ -78,6 +78,7 @@ import string
|
||||||
import mimetools
|
import mimetools
|
||||||
import multifile
|
import multifile
|
||||||
import shutil
|
import shutil
|
||||||
|
from bisect import bisect
|
||||||
|
|
||||||
|
|
||||||
# Exported constants
|
# Exported constants
|
||||||
|
@ -332,49 +333,131 @@ class Folder:
|
||||||
|
|
||||||
# Return the current message. Raise KeyError when there is none
|
# Return the current message. Raise KeyError when there is none
|
||||||
def getcurrent(self):
|
def getcurrent(self):
|
||||||
return min(self.getsequences()['cur'])
|
seqs = self.getsequences()
|
||||||
|
try:
|
||||||
|
return max(seqs['cur'])
|
||||||
|
except (ValueError, KeyError):
|
||||||
|
raise Error, "no cur message"
|
||||||
|
|
||||||
# Set the current message
|
# Set the current message
|
||||||
def setcurrent(self, n):
|
def setcurrent(self, n):
|
||||||
updateline(self.getsequencesfilename(), 'cur', str(n), 0)
|
updateline(self.getsequencesfilename(), 'cur', str(n), 0)
|
||||||
|
|
||||||
# Parse an MH sequence specification into a message list.
|
# Parse an MH sequence specification into a message list.
|
||||||
|
# Attempt to mimic mh-sequence(5) as close as possible.
|
||||||
|
# Also attempt to mimic observed behavior regarding which
|
||||||
|
# conditions cause which error messages
|
||||||
def parsesequence(self, seq):
|
def parsesequence(self, seq):
|
||||||
if seq == "all":
|
# XXX Still not complete (see mh-format(5)).
|
||||||
return self.listmessages()
|
# Missing are:
|
||||||
try:
|
# - 'prev', 'next' as count
|
||||||
n = string.atoi(seq, 10)
|
# - Sequence-Negation option
|
||||||
except string.atoi_error:
|
all = self.listmessages()
|
||||||
n = 0
|
# Observed behavior: test for empty folder is done first
|
||||||
if n > 0:
|
if not all:
|
||||||
return [n]
|
raise Error, "no messages in %s" % self.name
|
||||||
if regex.match("^last:", seq) >= 0:
|
# Common case first: all is frequently the default
|
||||||
|
if seq == 'all':
|
||||||
|
return all
|
||||||
|
# Test for X:Y before X-Y because 'seq:-n' matches both
|
||||||
|
i = string.find(seq, ':')
|
||||||
|
if i >= 0:
|
||||||
|
head, dir, tail = seq[:i], '', seq[i+1:]
|
||||||
|
if tail[:1] in '-+':
|
||||||
|
dir, tail = tail[:1], tail[1:]
|
||||||
|
if not isnumeric(tail):
|
||||||
|
raise Error, "bad message list %s" % seq
|
||||||
|
try:
|
||||||
|
count = string.atoi(tail)
|
||||||
|
except (ValueError, OverflowError):
|
||||||
|
# Can't use sys.maxint because of i+count below
|
||||||
|
count = len(all)
|
||||||
|
try:
|
||||||
|
anchor = self._parseindex(head, all)
|
||||||
|
except Error, msg:
|
||||||
|
seqs = self.getsequences()
|
||||||
|
if not seqs.has_key(head):
|
||||||
|
if not msg:
|
||||||
|
msg = "bad message list %s" % seq
|
||||||
|
raise Error, msg, sys.exc_traceback
|
||||||
|
msgs = seqs[head]
|
||||||
|
if not msgs:
|
||||||
|
raise Error, "sequence %s empty" % head
|
||||||
|
if dir == '-':
|
||||||
|
return msgs[-count:]
|
||||||
|
else:
|
||||||
|
return msgs[:count]
|
||||||
|
else:
|
||||||
|
if not dir:
|
||||||
|
if head in ('prev', 'last'):
|
||||||
|
dir = '-'
|
||||||
|
if dir == '-':
|
||||||
|
i = bisect(all, anchor)
|
||||||
|
return all[max(0, i-count):i]
|
||||||
|
else:
|
||||||
|
i = bisect(all, anchor-1)
|
||||||
|
return all[i:i+count]
|
||||||
|
# Test for X-Y next
|
||||||
|
i = string.find(seq, '-')
|
||||||
|
if i >= 0:
|
||||||
|
begin = self._parseindex(seq[:i], all)
|
||||||
|
end = self._parseindex(seq[i+1:], all)
|
||||||
|
i = bisect(all, begin-1)
|
||||||
|
j = bisect(all, end)
|
||||||
|
r = all[i:j]
|
||||||
|
if not r:
|
||||||
|
raise Error, "bad message list %s" % seq
|
||||||
|
return r
|
||||||
|
# Neither X:Y nor X-Y; must be a number or a (pseudo-)sequence
|
||||||
try:
|
try:
|
||||||
n = string.atoi(seq[5:])
|
n = self._parseindex(seq, all)
|
||||||
except string.atoi_error:
|
except Error, msg:
|
||||||
n = 0
|
seqs = self.getsequences()
|
||||||
if n > 0:
|
if not seqs.has_key(seq):
|
||||||
return self.listmessages()[-n:]
|
if not msg:
|
||||||
if regex.match("^first:", seq) >= 0:
|
msg = "bad message list %s" % seq
|
||||||
try:
|
raise Error, msg
|
||||||
n = string.atoi(seq[6:])
|
return seqs[seq]
|
||||||
except string.atoi_error:
|
else:
|
||||||
n = 0
|
if n not in all:
|
||||||
if n > 0:
|
if isnumeric(seq):
|
||||||
return self.listmessages()[:n]
|
raise Error, \
|
||||||
dict = self.getsequences()
|
"message %d doesn't exist" % n
|
||||||
if dict.has_key(seq):
|
else:
|
||||||
return dict[seq]
|
raise Error, "no %s message" % seq
|
||||||
context = self.mh.getcontext()
|
else:
|
||||||
# Complex syntax -- use pick
|
return [n]
|
||||||
pipe = os.popen("pick +%s %s 2>/dev/null" % (self.name, seq))
|
|
||||||
data = pipe.read()
|
# Internal: parse a message number (or cur, first, etc.)
|
||||||
sts = pipe.close()
|
def _parseindex(self, seq, all):
|
||||||
self.mh.setcontext(context)
|
if isnumeric(seq):
|
||||||
if sts:
|
try:
|
||||||
raise Error, "unparseable sequence %s" % `seq`
|
return string.atoi(seq)
|
||||||
items = string.split(data)
|
except (OverflowError, ValueError):
|
||||||
return map(string.atoi, items)
|
return sys.maxint
|
||||||
|
if seq in ('cur', '.'):
|
||||||
|
return self.getcurrent()
|
||||||
|
if seq == 'first':
|
||||||
|
return all[0]
|
||||||
|
if seq == 'last':
|
||||||
|
return all[-1]
|
||||||
|
if seq == 'next':
|
||||||
|
n = self.getcurrent()
|
||||||
|
i = bisect(all, n)
|
||||||
|
try:
|
||||||
|
return all[i]
|
||||||
|
except IndexError:
|
||||||
|
raise Error, "no next message"
|
||||||
|
if seq == 'prev':
|
||||||
|
n = self.getcurrent()
|
||||||
|
i = bisect(all, n-1)
|
||||||
|
if i == 0:
|
||||||
|
raise Error, "no prev message"
|
||||||
|
try:
|
||||||
|
return all[i-1]
|
||||||
|
except IndexError:
|
||||||
|
raise Error, "no prev message"
|
||||||
|
raise Error, None
|
||||||
|
|
||||||
# Open a message -- returns a Message object
|
# Open a message -- returns a Message object
|
||||||
def openmessage(self, n):
|
def openmessage(self, n):
|
||||||
|
@ -704,8 +787,7 @@ class IntSet:
|
||||||
alo, ahi = self.pairs[i-1]
|
alo, ahi = self.pairs[i-1]
|
||||||
blo, bhi = self.pairs[i]
|
blo, bhi = self.pairs[i]
|
||||||
if ahi >= blo-1:
|
if ahi >= blo-1:
|
||||||
self.pairs[i-1:i+1] = [
|
self.pairs[i-1:i+1] = [(alo, max(ahi, bhi))]
|
||||||
(alo, max(ahi, bhi))]
|
|
||||||
else:
|
else:
|
||||||
i = i+1
|
i = i+1
|
||||||
|
|
||||||
|
@ -883,8 +965,20 @@ def test():
|
||||||
do('mh.getcontext()')
|
do('mh.getcontext()')
|
||||||
context = mh.getcontext()
|
context = mh.getcontext()
|
||||||
f = mh.openfolder(context)
|
f = mh.openfolder(context)
|
||||||
do('f.listmessages()')
|
|
||||||
do('f.getcurrent()')
|
do('f.getcurrent()')
|
||||||
|
for seq in ['first', 'last', 'cur', '.', 'prev', 'next',
|
||||||
|
'first:3', 'last:3', 'cur:3', 'cur:-3',
|
||||||
|
'prev:3', 'next:3',
|
||||||
|
'1:3', '1:-3', '100:3', '100:-3', '10000:3', '10000:-3',
|
||||||
|
'all']:
|
||||||
|
try:
|
||||||
|
do('f.parsesequence(%s)' % `seq`)
|
||||||
|
except Error, msg:
|
||||||
|
print "Error:", msg
|
||||||
|
stuff = os.popen("pick %s 2>/dev/null" % `seq`).read()
|
||||||
|
list = map(string.atoi, string.split(stuff))
|
||||||
|
print list, "<-- pick"
|
||||||
|
do('f.listmessages()')
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
Loading…
Reference in New Issue