[Python-checkins] python/nondist/sandbox/mailbox mailbox.py, 1.4, 1.5 test_mailbox.py, 1.4, 1.5 libmailbox.tex, 1.5, 1.6
gregorykjohnson@users.sourceforge.net
gregorykjohnson at users.sourceforge.net
Tue Aug 2 23:46:16 CEST 2005
Update of /cvsroot/python/python/nondist/sandbox/mailbox
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv29669
Modified Files:
mailbox.py test_mailbox.py libmailbox.tex
Log Message:
* Implement MH (except sequences)
* Use mailbox.Error instead of IOError for non-empty folder removal
Index: mailbox.py
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/mailbox/mailbox.py,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -d -r1.4 -r1.5
--- mailbox.py 2 Aug 2005 19:18:53 -0000 1.4
+++ mailbox.py 2 Aug 2005 21:46:13 -0000 1.5
@@ -341,12 +341,12 @@
for entry in os.listdir(os.path.join(path, 'new')) + \
os.listdir(os.path.join(path, 'cur')):
if len(entry) < 1 or entry[0] != '.':
- raise IOError, "Folder '%s' contains message" % name
+ raise Error, "Folder '%s' contains message" % name
for entry in os.listdir(path):
if entry != 'new' and entry != 'cur' and entry != 'tmp' and \
os.path.isdir(os.path.join(path, entry)):
- raise IOError, "Folder '%s' contains subdirectory '%s'" % \
- (name, entry)
+ raise Error, "Folder '%s' contains subdirectory '%s'" % \
+ (name, entry)
for root, dirs, files in os.walk(path, topdown=False):
for entry in files:
os.remove(os.path.join(root, entry))
@@ -674,6 +674,176 @@
self._next_key = len(self._toc)
+class MH(Mailbox):
+ """An MH mailbox."""
+
+ def __init__(self, path, factory=None):
+ """Initialize an MH instance."""
+ Mailbox.__init__(self, path, factory)
+ if not os.path.exists(self._path):
+ os.mkdir(self._path, 0700)
+
+ def add(self, message):
+ """Add message and return assigned key."""
+ keys = self.keys()
+ if len(keys) == 0:
+ new_key = 1
+ else:
+ new_key = max(keys) + 1
+ fd = os.open(os.path.join(self._path, str(new_key)),
+ os.O_CREAT | os.O_EXCL | os.O_WRONLY)
+ f = os.fdopen(fd, 'w')
+ try:
+ try:
+ dotlock_inode = _lock_file(f)
+ try:
+ self._dump_message(message, f)
+ # XXX: update sequences based on message
+ f.flush()
+ finally:
+ _unlock_file(f, dotlock_inode)
+ finally:
+ f.close()
+ except:
+ os.remove(os.path.join(self._path, str(new_key)))
+ raise
+ return new_key
+
+ def remove(self, key):
+ """Remove the keyed message; raise KeyError if it doesn't exist."""
+ try:
+ os.remove(os.path.join(self._path, str(key)))
+ except OSError, e:
+ if e.errno == errno.ENOENT:
+ raise KeyError, "No message with key '%s'" % key
+ else:
+ raise
+
+ def __setitem__(self, key, message):
+ """Replace the keyed message; raise KeyError if it doesn't exist."""
+ try:
+ f_r = file(os.path.join(self._path, str(key)), 'r+')
+ except IOError, e:
+ if e.errno == errno.ENOENT:
+ raise KeyError, "No message with key '%s'" % key
+ else:
+ raise
+ try:
+ dotlock_inode = _lock_file(f_r)
+ try:
+ f_w = file(os.path.join(self._path, str(key)), 'w')
+ try:
+ self._dump_message(message, f_w)
+ finally:
+ f_w.close()
+ finally:
+ _unlock_file(f_r, dotlock_inode)
+ finally:
+ f_r.close()
+
+ def get_message(self, key):
+ """Return a Message representation or raise a KeyError."""
+ try:
+ f = file(os.path.join(self._path, str(key)), 'r')
+ except IOError, e:
+ if e.errno == errno.ENOENT:
+ raise KeyError, "No message with key '%s'" % key
+ else:
+ raise
+ try:
+ msg = MHMessage(f)
+ finally:
+ f.close()
+ # XXX: set sequences on msg
+ return msg
+
+ def get_string(self, key):
+ """Return a string representation or raise a KeyError."""
+ try:
+ f = file(os.path.join(self._path, str(key)), 'r')
+ except IOError, e:
+ if e.errno == errno.ENOENT:
+ raise KeyError, "No message with key '%s'" % key
+ else:
+ raise
+ try:
+ return f.read()
+ finally:
+ f.close()
+
+ def get_file(self, key):
+ """Return a file-like representation or raise a KeyError."""
+ try:
+ f = file(os.path.join(self._path, str(key)), 'r')
+ except IOError, e:
+ if e.errno == errno.ENOENT:
+ raise KeyError, "No message with key '%s'" % key
+ else:
+ raise
+ return _ProxyFile(f)
+
+ def iterkeys(self):
+ """Return an iterator over keys."""
+ for x in sorted(int(x) for x in os.listdir(self._path) if x.isdigit()):
+ yield int(x)
+
+ def has_key(self, key):
+ """Return True if the keyed message exists, False otherwise."""
+ return os.path.exists(os.path.join(self._path, str(key)))
+
+ def __len__(self):
+ """Return a count of messages in the mailbox."""
+ return len(list(self.iterkeys()))
+
+ def flush(self):
+ """Write any pending changes to the disk."""
+ return
+
+ def close(self):
+ """Flush and close the mailbox."""
+ return
+
+ def list_folders(self):
+ """Return a list of folders in this mailbox."""
+ result = []
+ for entry in os.listdir(self._path):
+ if os.path.isdir(os.path.join(self._path, entry)):
+ result.append(entry)
+ return result
+
+ def open_folder(self, name):
+ """Return an MH instance for folder name, creating it if necessary."""
+ return MH(os.path.join(self._path, name))
+
+ def remove_folder(self, name):
+ """Delete folder name."""
+ path = os.path.join(self._path, name)
+ entries = os.listdir(path)
+ if entries == ['.mh_sequences']:
+ os.remove(os.path.join(path, '.mh_sequences'))
+ elif entries == []:
+ pass
+ else:
+ raise Error, "Folder '%s' is not empty" % self._path
+ os.rmdir(path)
+
+ def list_sequences(self, name):
+ """Return a list of the names of sequences defined in the mailbox."""
+ raise NotImplementedError, 'Method not yet implemented'
+
+ def get_sequence(self, name):
+ """Return a list of the keys in sequence 'name'."""
+ raise NotImplementedError, 'Method not yet implemented'
+
+ def set_sequence(self, name):
+ """Set the keys in sequence 'name'."""
+ raise NotImplementedError, 'Method not yet implemented'
+
+ def pack(self):
+ """Eliminate numbering gaps in message names. Invalidates keys."""
+ raise NotImplementedError, 'Method not yet implemented'
+
+
class Message(email.Message.Message):
"""Message with mailbox-format-specific properties."""
Index: test_mailbox.py
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/mailbox/test_mailbox.py,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -d -r1.4 -r1.5
--- test_mailbox.py 2 Aug 2005 19:18:53 -0000 1.4
+++ test_mailbox.py 2 Aug 2005 21:46:13 -0000 1.5
@@ -670,6 +670,11 @@
_factory = lambda self, path, factory=None: mailbox.MMDF(path, factory)
+class TestMH(TestMailbox):
+
+ _factory = lambda self, path, factory=None: mailbox.MH(path, factory)
+
+
class TestMessage(TestBase):
_factory = mailbox.Message # Overridden by subclasses to reuse tests
@@ -1440,9 +1445,10 @@
def test_main():
- tests = (TestMaildir, TestMbox, TestMMDF, TestMessage, TestMaildirMessage,
- TestMboxMessage, TestMHMessage, TestBabylMessage, TestMMDFMessage,
- TestMessageConversion, TestProxyFile, TestPartialFile)
+ tests = (TestMaildir, TestMbox, TestMMDF, TestMH, TestMessage,
+ TestMaildirMessage, TestMboxMessage, TestMHMessage,
+ TestBabylMessage, TestMMDFMessage, TestMessageConversion,
+ TestProxyFile, TestPartialFile)
test_support.run_unittest(*tests)
Index: libmailbox.tex
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/mailbox/libmailbox.tex,v
retrieving revision 1.5
retrieving revision 1.6
diff -u -d -r1.5 -r1.6
--- libmailbox.tex 2 Aug 2005 19:18:54 -0000 1.5
+++ libmailbox.tex 2 Aug 2005 21:46:13 -0000 1.6
@@ -291,8 +291,8 @@
\begin{methoddesc}{remove_folder}{name}
Delete the folder whose name is \var{name}. If the folder contains any
-messages, an \exception{IOError} exception will be raised and the folder will
-not be deleted.
+messages, a \exception{mailbox.Error} exception will be raised and the folder
+will not be deleted.
\end{methoddesc}
\begin{methoddesc}{clean}{}
@@ -404,13 +404,18 @@
\dfn{folders}) in addition to messages. Folders may be nested indefinitely.
MH mailboxes support \dfn{sequences}, which are named lists used to logically
-group messages without moving them to sub-folders. Some mail reading programs
+group messages without moving them to sub-folders. Sequences are defined in a
+file called \file{.mh_sequences} in each folder. Some mail reading programs
(although not the standard \program{mh} and \program{nmh} implementations) use
sequences to the same end as flags are used in other formats: unread messages
are added to the "unseen" sequence, replied-to messages are added to the
"replied" sequence, and important messages are added upon request to the
"flagged" sequence.
+\class{MH} manipulates MH mailboxes, but it does not attempt to emulate
+\program{mh}. In particular, it does not access or modify \file{context} or
+\file{.mh_profile} files.
+
\class{MH} instances have all of the methods of \class{Mailbox} in addition to
the following:
@@ -426,8 +431,8 @@
\begin{methoddesc}{remove_folder}{name}
Delete the folder whose name is \var{name}. If the folder contains any
-messages, an \exception{IOError} exception will be raised and the folder will
-not be deleted.
+messages, a \exception{mailbox.Error} exception will be raised and the folder
+will not be deleted.
\end{methoddesc}
\begin{methoddesc}{list_sequences}{}
@@ -468,14 +473,14 @@
nothing.
\end{methoddesc}
-\begin{methoddesc}{flush}{}
+\begin{methoddesc}{close}{}
\class{MH} instances do not keep any open files, so this method does nothing.
\end{methoddesc}
\begin{classdesc}{MH}{path\optional{, factory}}
A subclass of \class{Mailbox} for mailboxes in MH format. Parameters \var{path}
-and \var{factory} has the same meaning as with the module-level \method{open()}
-function.
+and \var{factory} have the same meaning as with the module-level
+\method{open()} function.
\end{classdesc}
\class{MH} instances have all of the methods of \class{Mailbox} in addition to
@@ -500,7 +505,7 @@
\begin{classdesc}{Babyl}{path\optional{, factory}}
A subclass of \class{Mailbox} for mailboxes in Babyl format. Parameters
-\var{path} and \var{factory} has the same meaning as with the module-level
+\var{path} and \var{factory} have the same meaning as with the module-level
\method{open()} function.
\end{classdesc}
@@ -531,7 +536,7 @@
\begin{classdesc}{MMDF}{path\optional{, factory}}
A subclass of \class{Mailbox} for mailboxes in MMDF format. Parameters
-\var{path} and \var{factory} has the same meaning as with the module-level
+\var{path} and \var{factory} have the same meaning as with the module-level
\method{open()} function.
\end{classdesc}
More information about the Python-checkins
mailing list