[Python-checkins] python/nondist/sandbox/mailbox mailbox.py, 1.9, 1.10 libmailbox.tex, 1.10, 1.11 test_mailbox.py, 1.7, 1.8

gregorykjohnson@users.sourceforge.net gregorykjohnson at users.sourceforge.net
Wed Aug 17 01:38:22 CEST 2005


Update of /cvsroot/python/python/nondist/sandbox/mailbox
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv2326

Modified Files:
	mailbox.py libmailbox.tex test_mailbox.py 
Log Message:
* Add delivery date support to MaildirMessage and Maildir, and support
  for conversion between delivery dates in MaildirMessage, mboxMessage,
  and MMDFMessage instances.
* Don't guess delivery dates from headers.
* Add full support for labels to Babyl.
* Small tweaks:
    * Remove outdated "start, stop, ..." mentions in comments.
    * Change exception raising style: use "Exception(...)" instead of
      "Exception, ..." in accordance with Raymond Hettinger's comment in
      the Python Tutorial and A.M. Kuchling's recent post on python-dev.
    * Use ": %s" consistently in exception messages, not " '%s'".


Index: mailbox.py
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/mailbox/mailbox.py,v
retrieving revision 1.9
retrieving revision 1.10
diff -u -d -r1.9 -r1.10
--- mailbox.py	16 Aug 2005 17:12:03 -0000	1.9
+++ mailbox.py	16 Aug 2005 23:38:11 -0000	1.10
@@ -4,6 +4,7 @@
 
 import os
 import time
+import calendar
 import socket
 import errno
 import stat
@@ -33,11 +34,11 @@
 
     def add(self, message):
         """Add message and return assigned key."""
-        raise NotImplementedError, "Method must be implemented by subclass"
+        raise NotImplementedError('Method must be implemented by subclass')
 
     def remove(self, key):
         """Remove the keyed message; raise KeyError if it doesn't exist."""
-        raise NotImplementedError, "Method must be implemented by subclass"
+        raise NotImplementedError('Method must be implemented by subclass')
 
     def __delitem__(self, key):
         self.remove(key)
@@ -51,7 +52,7 @@
 
     def __setitem__(self, key, message):
         """Replace the keyed message; raise KeyError if it doesn't exist."""
-        raise NotImplementedError, "Method must be implemented by subclass"
+        raise NotImplementedError('Method must be implemented by subclass')
 
     def get(self, key, default=None):
         """Return the keyed message, or default if it doesn't exist."""
@@ -69,19 +70,19 @@
 
     def get_message(self, key):
         """Return a Message representation or raise a KeyError."""
-        raise NotImplementedError, "Method must be implemented by subclass"
+        raise NotImplementedError('Method must be implemented by subclass')
 
     def get_string(self, key):
         """Return a string representation or raise a KeyError."""
-        raise NotImplementedError, "Method must be implemented by subclass"
+        raise NotImplementedError('Method must be implemented by subclass')
 
     def get_file(self, key):
         """Return a file-like representation or raise a KeyError."""
-        raise NotImplementedError, "Method must be implemented by subclass"
+        raise NotImplementedError('Method must be implemented by subclass')
 
     def iterkeys(self):
         """Return an iterator over keys."""
-        raise NotImplementedError, "Method must be implemented by subclass"
+        raise NotImplementedError('Method must be implemented by subclass')
 
     def keys(self):
         """Return a list of keys."""
@@ -118,14 +119,14 @@
 
     def has_key(self, key):
         """Return True if the keyed message exists, False otherwise."""
-        raise NotImplementedError, "Method must be implemented by subclass"
+        raise NotImplementedError('Method must be implemented by subclass')
 
     def __contains__(self, key):
         return self.has_key(key)
 
     def __len__(self):
         """Return a count of messages in the mailbox."""
-        raise NotImplementedError, "Method must be implemented by subclass"
+        raise NotImplementedError('Method must be implemented by subclass')
 
     def clear(self):
         """Delete all messages."""
@@ -146,7 +147,7 @@
         for key in self.iterkeys():
             return (key, self.pop(key))     # This is only run once.
         else:
-            raise KeyError, "No messages in mailbox" 
+            raise KeyError('No messages in mailbox')
 
     def update(self, arg=None):
         """Change the messages that correspond to certain keys."""
@@ -163,23 +164,23 @@
             except KeyError:
                 bad_key = True
         if bad_key:
-            raise KeyError, "No message with key(s)"
+            raise KeyError('No message with key(s)')
 
     def flush(self):
         """Write any pending changes to the disk."""
-        raise NotImplementedError, "Method must be implemented by subclass"
+        raise NotImplementedError('Method must be implemented by subclass')
 
     def lock(self):
         """Lock the mailbox."""
-        raise NotImplementedError, "Method must be implemented by subclass"
+        raise NotImplementedError('Method must be implemented by subclass')
 
     def unlock(self, f=None):
         """Unlock the mailbox if it is locked."""
-        raise NotImplementedError, "Method must be implemented by subclass"
+        raise NotImplementedError('Method must be implemented by subclass')
 
     def close(self):
         """Flush and close the mailbox."""
-        raise NotImplementedError, "Method must be implemented by subclass"
+        raise NotImplementedError('Method must be implemented by subclass')
 
     def _dump_message(self, message, target):
         """Dump message contents to target file."""
@@ -195,7 +196,7 @@
                     break
                 target.write(buffer)
         else:
-            raise TypeError, "Invalid message type"
+            raise TypeError('Invalid message type: %s' % type(message))
 
 
 class Maildir(Mailbox):
@@ -211,7 +212,7 @@
                 os.mkdir(os.path.join(self._path, 'new'), 0700)
                 os.mkdir(os.path.join(self._path, 'cur'), 0700)
             else:
-                raise NoSuchMailboxError, self._path
+                raise NoSuchMailboxError(self._path)
         self._toc = {}
 
     def add(self, message):
@@ -232,6 +233,8 @@
         uniq = os.path.basename(tmp_file.name).split(':')[0]
         dest = os.path.join(self._path, subdir, uniq + suffix)
         os.rename(tmp_file.name, dest)
+        if isinstance(message, MaildirMessage):
+            os.utime(dest, (os.path.getatime(dest), message.get_date()))
         return uniq
 
     def remove(self, key):
@@ -268,9 +271,11 @@
         else:
             suffix = ''
         self.discard(key)
-        os.rename(os.path.join(self._path, temp_subpath),
-                  os.path.join(self._path, subdir, key + suffix))
-        # XXX: the mtime should be reset to keep delivery date
+        new_path = os.path.join(self._path, subdir, key + suffix)
+        os.rename(os.path.join(self._path, temp_subpath), new_path)
+        if isinstance(message, MaildirMessage):
+            os.utime(new_path, (os.path.getatime(new_path),
+                                message.get_date()))
 
     def get_message(self, key):
         """Return a Message representation or raise a KeyError."""
@@ -284,6 +289,7 @@
         msg.set_subdir(subdir)
         if ':' in name:
             msg.set_info(name.split(':')[-1])
+        msg.set_date(os.path.getmtime(os.path.join(self._path, subpath)))
         return msg
 
     def get_string(self, key):
@@ -363,12 +369,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 NotEmptyError, "Folder '%s' contains message" % folder
+                raise NotEmptyError('Folder contains message(s): %s' % folder)
         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 NotEmptyError, "Folder '%s' contains subdirectory '%s'" \
-                              % (folder, entry)
+                raise NotEmptyError("Folder contains subdirectory '%s': %s" %
+                                    (folder, entry))
         for root, dirs, files in os.walk(path, topdown=False):
             for entry in files:
                 os.remove(os.path.join(root, entry))
@@ -406,8 +412,8 @@
             else:
                 raise
         else:
-            raise ExternalClashError, \
-                  "Name clash prevented file creation: '%s'" % path
+            raise ExternalClashError('Name clash prevented file creation: %s' %
+                                     path)
 
     def _refresh(self):
         """Update table of contents mapping."""
@@ -428,7 +434,7 @@
         try:
             return self._toc[key]
         except KeyError:
-            raise KeyError, "No message with key '%s'" % key
+            raise KeyError('No message with key: %s' % key)
 
 
 class _singlefileMailbox(Mailbox):
@@ -444,7 +450,7 @@
                 if create:
                     f = file(self._path, 'w+')
                 else:
-                    raise NoSuchMailboxError, self._path
+                    raise NoSuchMailboxError(self._path)
             elif e.errno == errno.EACCES:
                 f = file(self._path, 'r')
             else:
@@ -565,17 +571,17 @@
         self._file.close()
 
     def _lookup(self, key=None):
-        """Return (start, stop), possibly with more info, or raise KeyError."""
+        """Return (start, stop) or raise KeyError."""
         if self._toc is None:
             self._generate_toc()
         if key is not None:
             try:
                 return self._toc[key]
             except KeyError:
-                raise KeyError, "No message with key '%s'" % key
+                raise KeyError('No message with key: %s' % key)
 
     def _append_message(self, message):
-        """Append message to mailbox and return (start, stop, ...) offsets."""
+        """Append message to mailbox and return (start, stop) offsets."""
         self._file.seek(0, 2)
         self._pre_message_hook(self._file)
         offsets = self._install_message(message)
@@ -629,8 +635,7 @@
         elif isinstance(message, email.Message.Message):
             from_line = message.get_unixfrom()  # May be None.
         if from_line is None:
-            from_line = 'From MAILER-DAEMON %s' % \
-                        time.strftime('%a %b %d %H:%M:%S %Y', time.gmtime())
+            from_line = 'From MAILER-DAEMON %s' % time.asctime(time.gmtime())
         start = self._file.tell()
         self._file.write('%s%s' % (from_line, os.linesep))
         self._dump_message(message, self._file)
@@ -724,7 +729,7 @@
                 os.close(os.open(os.path.join(self._path, '.mh_sequences'),
                                  os.O_CREAT | os.O_EXCL | os.O_WRONLY, 0600))
             else:
-                raise NoSuchMailboxError, self._path
+                raise NoSuchMailboxError(self._path)
         self._locked = False
 
     def add(self, message):
@@ -757,7 +762,7 @@
             f = file(path, 'r+')
         except IOError, e:
             if e.errno == errno.ENOENT:
-                raise KeyError, "No message with key '%s'" % key
+                raise KeyError('No message with key: %s' % key)
             else:
                 raise
         try:
@@ -779,7 +784,7 @@
             f = file(path, 'r+')
         except IOError, e:
             if e.errno == errno.ENOENT:
-                raise KeyError, "No message with key '%s'" % key
+                raise KeyError('No message with key: %s' % key)
             else:
                 raise
         try:
@@ -805,7 +810,7 @@
                 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
+                raise KeyError('No message with key: %s' % key)
             else:
                 raise
         try:
@@ -832,7 +837,7 @@
                 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
+                raise KeyError('No message with key: %s' % key)
             else:
                 raise
         try:
@@ -852,7 +857,7 @@
             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
+                raise KeyError('No message with key: %s' % key)
             else:
                 raise
         return _ProxyFile(f)
@@ -919,8 +924,7 @@
         elif entries == []:
             pass
         else:
-            raise NotEmptyError, "Folder '%s' is not empty" % \
-                                 self._path
+            raise NotEmptyError('Folder not empty: %s' % self._path)
         os.rmdir(path)
 
     def get_sequences(self):
@@ -942,8 +946,8 @@
                     results[name] = [key for key in sorted(keys) \
                                          if key in all_keys]
                 except ValueError:
-                    raise FormatError, "Invalid sequence specification: " % \
-                                       "'%s'" % line.rstrip()
+                    raise FormatError('Invalid sequence specification: %s' %
+                                      line.rstrip())
         finally:
             f.close()
         return results
@@ -1032,11 +1036,38 @@
 class Babyl(_singlefileMailbox):
     """An Rmail-style Babyl mailbox."""
 
+    _special_labels = frozenset(('unseen', 'deleted', 'filed', 'answered',
+                                 'forwarded', 'edited', 'resent'))
+
+    def __init__(self, path, factory=None, create=True):
+        """Initialize a Babyl mailbox."""
+        _singlefileMailbox.__init__(self, path, factory, create)
+        self._labels = {}
+
+    def add(self, message):
+        """Add message and return assigned key."""
+        key = _singlefileMailbox.add(self, message)
+        if isinstance(message, BabylMessage):
+            self._labels[key] = message.get_labels()
+        return key
+
+    def remove(self, key):
+        """Remove the keyed message; raise KeyError if it doesn't exist."""
+        _singlefileMailbox.remove(self, key)
+        if key in self._labels:
+            del self._labels[key]
+
+    def __setitem__(self, key, message):
+        """Replace the keyed message; raise KeyError if it doesn't exist."""
+        _singlefileMailbox.__setitem__(self, key, message)
+        if isinstance(message, BabylMessage):
+            self._labels[key] = message.get_labels()
+
     def get_message(self, key):
         """Return a Message representation or raise a KeyError."""
         start, stop = self._lookup(key)
         self._file.seek(start)
-        self._file.readline()   # XXX: parse this '1,' line for labels
+        self._file.readline()   # Skip '1,' line specifying labels.
         original_headers = StringIO.StringIO()
         while True:
             line = self._file.readline()
@@ -1052,13 +1083,15 @@
         body = self._file.read(stop - self._file.tell())
         msg = BabylMessage(original_headers.getvalue() + body)
         msg.set_visible(visible_headers.getvalue())
+        if key in self._labels:
+            msg.set_labels(self._labels[key])
         return msg
 
     def get_string(self, key):
         """Return a string representation or raise a KeyError."""
         start, stop = self._lookup(key)
         self._file.seek(start)
-        self._file.readline()   # Skip '1,' line.
+        self._file.readline()   # Skip '1,' line specifying labels.
         original_headers = StringIO.StringIO()
         while True:
             line = self._file.readline()
@@ -1078,13 +1111,19 @@
 
     def get_labels(self):
         """Return a list of user-defined labels in the mailbox."""
-        raise NotImplementedError, 'Method not yet implemented'
+        self._lookup()
+        labels = set()
+        for label_list in self._labels.values():
+            labels.update(label_list)
+        labels.difference_update(self._special_labels)
+        return list(labels)
 
     def _generate_toc(self):
-        """Generate key-to-(start, stop, eooh, body) table of contents."""
+        """Generate key-to-(start, stop) table of contents."""
         starts, stops = [], []
         self._file.seek(0)
         next_pos = 0
+        label_lists = []
         while True:
             line_pos = next_pos
             line = self._file.readline()
@@ -1093,6 +1132,10 @@
                 if len(stops) < len(starts):
                     stops.append(line_pos - len(os.linesep))
                 starts.append(next_pos)
+                labels = [label.strip() for label
+                                        in self._file.readline()[1:].split(',')
+                                        if label.strip() != '']
+                label_lists.append(labels)
             elif line == '\037' or line == '\037' + os.linesep:
                 if len(stops) < len(starts):
                     stops.append(line_pos - len(os.linesep))
@@ -1100,12 +1143,13 @@
                 stops.append(line_pos - len(os.linesep))
                 break
         self._toc = dict(enumerate(zip(starts, stops)))
+        self._labels = dict(enumerate(label_lists))
         self._next_key = len(self._toc)
 
     def _pre_mailbox_hook(self, f):
         """Called before writing the mailbox to file f."""
-        f.write('BABYL OPTIONS:%sVersion: 5%s\037' % (os.linesep, os.linesep))
-        # XXX: write "Labels:" line too
+        f.write('BABYL OPTIONS:\nVersion: 5\nLabels:%s\n\037' %
+                ','.join(self.get_labels()))
 
     def _pre_message_hook(self, f):
         """Called before writing each message to file f."""
@@ -1116,9 +1160,25 @@
         f.write('\n\037')
 
     def _install_message(self, message):
-        """Write message contents and return (start, stop, ...)."""
+        """Write message contents and return (start, stop)."""
         start = self._file.tell()
-        self._file.write('1,,\n')   # XXX: check for labels and add them
+        if isinstance(message, BabylMessage):
+            special_labels = []
+            labels = []
+            for label in message.get_labels():
+                if label in self._special_labels:
+                    special_labels.append(label)
+                else:
+                    labels.append(label)
+            self._file.write('1')
+            for label in special_labels:
+                self._file.write(', ' + label)
+            self._file.write(',,')
+            for label in labels:
+                self._file.write(' ' + label + ',')
+            self._file.write('\n')
+        else:
+            self._file.write('1,,\n')
         if isinstance(message, email.Message.Message):
             pseudofile = StringIO.StringIO()
             ps_generator = email.Generator.Generator(pseudofile, False, 0)
@@ -1175,7 +1235,7 @@
                     break
                 self._file.write(buffer)
         else:
-            raise TypeError, "Invalid message type"
+            raise TypeError('Invalid message type: %s' % type(message))
         stop = self._file.tell()
         return (start, stop)
 
@@ -1196,7 +1256,7 @@
         elif message is None:
             email.Message.Message.__init__(self)
         else:
-            raise TypeError, "Invalid message type"
+            raise TypeError('Invalid message type: %s' % type(message))
 
     def _become_message(self, message):
         """Assume the non-format-specific state of message."""
@@ -1209,7 +1269,7 @@
         if isinstance(message, Message):
             return  # There's nothing format-specific to explain.
         else:
-            raise TypeError, "Cannot convert to specified type"
+            raise TypeError('Cannot convert to specified type')
 
 
 class MaildirMessage(Message):
@@ -1219,6 +1279,7 @@
         """Initialize a MaildirMessage instance."""
         self._subdir = 'new'
         self._info = ''
+        self._date = time.time()
         Message.__init__(self, message)
 
     def get_subdir(self):
@@ -1230,7 +1291,7 @@
         if subdir == 'new' or subdir == 'cur':
             self._subdir = subdir
         else:
-            raise ValueError, "subdir must be 'new' or 'cur'"
+            raise ValueError("subdir must be 'new' or 'cur': %s" % subdir)
 
     def get_flags(self):
         """Return as a string the flags that are set."""
@@ -1252,6 +1313,17 @@
         if self.get_flags() != '':
             self.set_flags(''.join(set(self.get_flags()) - set(flag)))
 
+    def get_date(self):
+        """Return delivery date of message, in seconds since the epoch."""
+        return self._date
+
+    def set_date(self, date):
+        """Set delivery date of message, in seconds since the epoch."""
+        try:
+            self._date = float(date)
+        except ValueError:
+            raise TypeError("can't convert to float: %s" % date)
+
     def get_info(self):
         """Get the message's "info" as a string."""
         return self._info
@@ -1261,13 +1333,14 @@
         if isinstance(info, str):
             self._info = info
         else:
-            raise TypeError, "info must be a string"
+            raise TypeError('info must be a string: %s' % type(info))
 
     def _explain_to(self, message):
         """Copy Maildir-specific state to message insofar as possible."""
         if isinstance(message, MaildirMessage):
             message.set_flags(self.get_flags())
             message.set_subdir(self.get_subdir())
+            message.set_date(self.get_date())
         elif isinstance(message, _mboxMMDFMessage):
             flags = set(self.get_flags())
             if 'S' in flags:
@@ -1280,6 +1353,7 @@
                 message.add_flag('F')
             if 'R' in flags:
                 message.add_flag('A')
+            message.set_from('MAILER-DAEMON', time.gmtime(self.get_date()))
         elif isinstance(message, MHMessage):
             flags = set(self.get_flags())
             if 'S' not in flags:
@@ -1301,7 +1375,8 @@
         elif isinstance(message, Message):
             pass
         else:
-            raise TypeError, "Cannot convert to specified type"
+            raise TypeError('Cannot convert to specified type: %s' %
+                            type(message))
 
 
 class _mboxMMDFMessage(Message):
@@ -1314,10 +1389,6 @@
             unixfrom = message.get_unixfrom()
             if unixfrom is not None and unixfrom[:5] == 'From ':
                 self.set_from(unixfrom[5:])
-            elif 'Return-Path' in message:
-                # XXX: generate "From " line from Return-Path: and Received:
-                pass
-
         Message.__init__(self, message)
 
     def get_from(self):
@@ -1329,7 +1400,7 @@
         if time_ is not None:
             if time_ is True:
                 time_ = time.gmtime()
-            from_ += time.strftime(" %a %b %d %H:%M:%S %Y", time_)
+            from_ += ' ' + time.asctime(time_)
         self._from = from_
 
     def get_flags(self):
@@ -1381,6 +1452,14 @@
                 message.add_flag('S')
             if 'D' in flags:
                 message.add_flag('T')
+            del message['status']
+            del message['x-status']
+            maybe_date = ' '.join(self.get_from().split()[-5:])
+            try:
+                message.set_date(calendar.timegm(time.strptime(maybe_date,
+                                                      '%a %b %d %H:%M:%S %Y')))
+            except ValueError, OverflowError:
+                pass
         elif isinstance(message, _mboxMMDFMessage):
             message.set_flags(self.get_flags())
             message.set_from(self.get_from())
@@ -1392,6 +1471,8 @@
                 message.add_sequence('replied')
             if 'F' in flags:
                 message.add_sequence('flagged')
+            del message['status']
+            del message['x-status']
         elif isinstance(message, BabylMessage):
             flags = set(self.get_flags())
             if 'R' not in flags:
@@ -1400,10 +1481,13 @@
                 message.add_label('deleted')
             if 'A' in flags:
                 message.add_label('answered')
+            del message['status']
+            del message['x-status']
         elif isinstance(message, Message):
             pass
         else:
-            raise TypeError, "Cannot convert to specified type"
+            raise TypeError('Cannot convert to specified type: %s' %
+                            type(message))
 
 
 class mboxMessage(_mboxMMDFMessage):
@@ -1432,7 +1516,7 @@
             if not sequence in self._sequences:
                 self._sequences.append(sequence)
         else:
-            raise TypeError, "sequence must be a string"
+            raise TypeError('sequence must be a string: %s' % type(sequence))
 
     def remove_sequence(self, sequence):
         """Remove sequence from the list of sequences including the message."""
@@ -1476,7 +1560,8 @@
         elif isinstance(message, Message):
             pass
         else:
-            raise TypeError, "Cannot convert to specified type"
+            raise TypeError('Cannot convert to specified type: %s' %
+                            type(message))
 
 
 class BabylMessage(Message):
@@ -1502,7 +1587,7 @@
             if label not in self._labels:
                 self._labels.append(label)
         else:
-            raise TypeError, "label must be a string"
+            raise TypeError('label must be a string: %s' % type(label))
 
     def remove_label(self, label):
         """Remove label from the list of labels on the message."""
@@ -1568,7 +1653,8 @@
         elif isinstance(message, Message):
             pass
         else:
-            raise TypeError, "Cannot convert to specified type"
+            raise TypeError('Cannot convert to specified type: %s' %
+                            type(message))
 
 
 class MMDFMessage(_mboxMMDFMessage):
@@ -1675,16 +1761,16 @@
                 fcntl.lockf(f, fcntl.LOCK_EX | fcntl.LOCK_NB)
             except IOError, e:
                 if e.errno == errno.EAGAIN:
-                    raise ExternalClashError, \
-                          "lockf: lock unavailable: %s" % f.name
+                    raise ExternalClashError('lockf: lock unavailable: %s' %
+                                             f.name)
                 else:
                     raise
             try:
                 fcntl.flock(f, fcntl.LOCK_EX | fcntl.LOCK_NB)
             except IOError, e:
                 if e.errno == errno.EWOULDBLOCK:
-                    raise ExternalClashError, \
-                          "flock: lock unavailable: %s" % f.name
+                    raise ExternalClashError('flock: lock unavailable: %s' %
+                                             f.name)
                 else:
                     raise
         if dotlock:
@@ -1707,8 +1793,8 @@
             except OSError, e:
                 if e.errno == errno.EEXIST:
                     os.remove(pre_lock)
-                    raise ExternalClashError, 'dot lock unavailable: %s' % \
-                                               f.name
+                    raise ExternalClashError('dot lock unavailable: %s' % 
+                                             f.name)
                 else:
                     raise
     except:

Index: libmailbox.tex
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/mailbox/libmailbox.tex,v
retrieving revision 1.10
retrieving revision 1.11
diff -u -d -r1.10 -r1.11
--- libmailbox.tex	16 Aug 2005 17:12:03 -0000	1.10
+++ libmailbox.tex	16 Aug 2005 23:38:11 -0000	1.11
@@ -720,6 +720,16 @@
 current "info" is not modified.
 \end{methoddesc}
 
+\begin{methoddesc}{get_date}{}
+Return the delivery date of the message as a floating-point number representing
+seconds since the epoch.
+\end{methoddesc}
+
+\begin{methoddesc}{set_date}{date}
+Set the delivery date of the message to \var{date}, a floating-point number
+representing seconds since the epoch.
+\end{methoddesc}
+
 \begin{methoddesc}{get_info}{}
 Return a string containing the "info" for a message. This is useful for
 accessing and modifying "info" that is experimental (i.e., not a list of
@@ -807,10 +817,11 @@
 
 \begin{methoddesc}{set_from}{from_\optional{, time_=None}}
 Set the "From~" line to \var{from_}, which should be specified without a
-leading "From~" or trailing newline. If \var{time_} is specified, it should be
-a \class{struct_time} or a tuple suitable for passing to
-\method{time.strftime()}; if \var{time_} is \code{True}, the result of
-\method{time.gmtime()} is used.
+leading "From~" or trailing newline. For convenience, \var{time_} may be
+specified and will be formatted appropriately and appended to \var{from_}. If
+\var{time_} is specified, it should be a \class{struct_time}, a tuple suitable
+for passing to \method{time.strftime()}, or \code{True} (to use
+\method{time.gmtime()}).
 \end{methoddesc}
 
 \begin{methoddesc}{get_flags}{}
@@ -840,7 +851,9 @@
 \end{methoddesc}
 
 When an \class{mboxMessage} instance is created based upon a
-\class{MaildirMessage} instance, the following conversions take place:
+\class{MaildirMessage} instance, a "From~" line is generated based upon the
+\class{MaildirMessage} instance's delivery date, and the following conversions
+take place:
 
 \begin{tableii}{l|l}{textrm}
     {Resulting state}{\class{MaildirMessage} state}
@@ -1097,10 +1110,11 @@
 
 \begin{methoddesc}{set_from}{from_\optional{, time_=None}}
 Set the "From~" line to \var{from_}, which should be specified without a
-leading "From~" or trailing newline. If \var{time_} is specified, it should be
-a \class{struct_time} or a tuple suitable for passing to
-\method{time.strftime()}; if \var{time_} is \code{True}, the result of
-\method{time.gmtime()} is used.
+leading "From~" or trailing newline. For convenience, \var{time_} may be
+specified to format a time appropriately and append it to \var{from_}. If
+\var{time_} is specified, it should be a \class{struct_time}, a tuple suitable
+for passing to \method{time.strftime()}, or \code{True} (to use
+\method{time.gmtime()}).
 \end{methoddesc}
 
 \begin{methoddesc}{get_flags}{}
@@ -1130,7 +1144,9 @@
 \end{methoddesc}
 
 When an \class{MMDFMessage} instance is created based upon a
-\class{MaildirMessage} instance, the following conversions take place:
+\class{MaildirMessage} instance, a "From~" line is generated based upon the
+\class{MaildirMessage} instance's delivery date, and the following conversions
+take place:
 
 \begin{tableii}{l|l}{textrm}
     {Resulting state}{\class{MaildirMessage} state}

Index: test_mailbox.py
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/mailbox/test_mailbox.py,v
retrieving revision 1.7
retrieving revision 1.8
diff -u -d -r1.7 -r1.8
--- test_mailbox.py	13 Aug 2005 22:41:33 -0000	1.7
+++ test_mailbox.py	16 Aug 2005 23:38:11 -0000	1.8
@@ -792,6 +792,13 @@
         self.assert_(msg.get_subdir() == 'new')
         self._check_sample(msg)
 
+    def test_date(self):
+        # Use get_date() and set_date()
+        msg = mailbox.MaildirMessage(_sample_message)
+        self.assert_(abs(msg.get_date() - time.time()) < 60)
+        msg.set_date(0.0)
+        self.assert_(msg.get_date() == 0.0)
+
     def test_info(self):
         # Use get_info() and set_info()
         msg = mailbox.MaildirMessage(_sample_message)
@@ -995,10 +1002,12 @@
         msg_maildir = mailbox.MaildirMessage(_sample_message)
         msg_maildir.set_flags('DFPRST')
         msg_maildir.set_subdir('cur')
+        date = msg_maildir.get_date()
         msg = mailbox.MaildirMessage(msg_maildir)
         self._check_sample(msg)
         self.assert_(msg.get_flags() == 'DFPRST')
         self.assert_(msg.get_subdir() == 'cur')
+        self.assert_(msg.get_date() == date)
 
     def test_maildir_to_mboxmmdf(self):
         # Convert MaildirMessage to mboxmessage and MMDFMessage
@@ -1006,9 +1015,13 @@
                  ('T', 'D'), ('DFPRST', 'RDFA'))
         for class_ in (mailbox.mboxMessage, mailbox.MMDFMessage):
             msg_maildir = mailbox.MaildirMessage(_sample_message)
+            msg_maildir.set_date(0.0)
             for setting, result in pairs:
                 msg_maildir.set_flags(setting)
-                self.assert_(class_(msg_maildir).get_flags() == result)
+                msg = class_(msg_maildir)
+                self.assert_(msg.get_flags() == result)
+                self.assert_(msg.get_from() == 'MAILER-DAEMON %s' %
+                             time.asctime(time.gmtime(0.0)))
             msg_maildir.set_subdir('cur')
             self.assert_(class_(msg_maildir).get_flags() == 'RODFA')
 
@@ -1039,12 +1052,14 @@
         # Convert mboxMessage and MMDFMessage to MaildirMessage
         for class_ in (mailbox.mboxMessage, mailbox.MMDFMessage):
             msg_mboxMMDF = class_(_sample_message)
+            msg_mboxMMDF.set_from('foo at bar', time.gmtime(0.0))
             pairs = (('R', 'S'), ('O', ''), ('D', 'T'), ('F', 'F'), ('A', 'R'),
                      ('RODFA', 'FRST'))
             for setting, result in pairs:
                 msg_mboxMMDF.set_flags(setting)
-                self.assert_(mailbox.MaildirMessage(msg_mboxMMDF).get_flags() \
-                             == result)
+                msg = mailbox.MaildirMessage(msg_mboxMMDF)
+                self.assert_(msg.get_flags() == result)
+                self.assert_(msg.get_date() == 0.0, msg.get_date())
             msg_mboxMMDF.set_flags('O')
             self.assert_(mailbox.MaildirMessage(msg_mboxMMDF).get_subdir() == \
                          'cur')



More information about the Python-checkins mailing list