[Python-checkins] python/nondist/sandbox/mailbox mailbox.py, NONE, 1.1 test_mailbox.py, NONE, 1.1 docs.tex, NONE, 1.1 libmailbox.tex, 1.1, 1.2

gregorykjohnson@users.sourceforge.net gregorykjohnson at users.sourceforge.net
Tue Jul 26 22:47:51 CEST 2005


Update of /cvsroot/python/python/nondist/sandbox/mailbox
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv3755
Modified Files:
	libmailbox.tex 
Added Files:
	mailbox.py test_mailbox.py docs.tex 
Log Message:
Maildir, with tests.
--- NEW FILE: mailbox.py ---
#! /usr/bin/env python
"""Read/write support for Maildir, mbox, MH, Babyl, and MMDF mailboxes."""
import os
import time
import socket
import errno
import stat
import copy
import email
import email.Message
import email.Generator
import rfc822
__all__ = [ 'open', 'Mailbox', 'Maildir', 'mbox', 'MH', 'Babyl', 'MMDF',
 'Message', 'MaildirMessage', 'mboxMessage', 'MHMessage',
 'BabylMessage', 'MMDFMessage' ]
def open(path, format=None, factory=None):
 """Open a mailbox at the given path and return a Mailbox instance.""" 
class Mailbox:
 """A group of messages in a particular place."""
 def __init__(self, path, factory=None):
 """Initialize a Mailbox instance."""
 self._path = os.path.abspath(path)
 self._factory = factory
 def add(self, message):
 """Add message and return assigned key."""
 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"
 def __delitem__(self, key):
 self.remove(key)
 def discard(self, key):
 """If the keyed message exists, remove it."""
 try:
 self.remove(key)
 except KeyError:
 pass
 def __setitem__(self, key, message):
 """Replace the keyed message; raise KeyError if it doesn't exist."""
 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."""
 try:
 return self.__getitem__(key)
 except KeyError:
 return default
 def __getitem__(self, key):
 """Return the keyed message; raise KeyError if it doesn't exist."""
 if self._factory is None:
 return self.get_message(key)
 else:
 return self._factory(self.get_file(key))
 def get_message(self, key):
 """Return a Message representation or raise a KeyError."""
 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"
 def get_file(self, key):
 """Return a file-like representation or raise a KeyError."""
 raise NotImplementedError, "Method must be implemented by subclass"
 def iterkeys(self):
 """Return an iterator over keys."""
 raise NotImplementedError, "Method must be implemented by subclass"
 def keys(self):
 """Return a list of keys."""
 return list(self.iterkeys())
 def itervalues(self):
 """Return an iterator over all messages."""
 for key in self.iterkeys():
 try:
 value = self[key]
 except KeyError:
 continue
 yield value
 def __iter__(self):
 return self.itervalues()
 def values(self):
 """Return a list of messages. Memory intensive."""
 return list(self.itervalues())
 def iteritems(self):
 """Return an iterator over (key, message) tuples."""
 for key in self.iterkeys():
 try:
 value = self[key]
 except KeyError:
 continue
 yield (key, value)
 def items(self):
 """Return a list of (key, message) tuples. Memory intensive."""
 return list(self.iteritems())
 def has_key(self, key):
 """Return True if the keyed message exists, False otherwise."""
 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"
 def clear(self):
 """Delete all messages."""
 for key in self.iterkeys():
 self.discard(key)
 def pop(self, key, default=None):
 """Delete the keyed message and return it, or default."""
 try:
 result = self[key]
 except KeyError:
 return default
 self.discard(key)
 return result
 def popitem(self):
 """Delete an arbitrary (key, message) pair and return it."""
 for key in self.iterkeys():
 return (key, self.pop(key)) # This is only run once.
 else:
 raise KeyError, "No messages in mailbox" 
 def update(self, arg=None):
 """Change the messages that correspond to certain keys."""
 if hasattr(arg, 'items'):
 source = arg.items()
 else:
 source = arg
 bad_key = False
 for key, message in source:
 try:
 self[key] = message
 except KeyError:
 bad_key = True
 if bad_key:
 raise KeyError, "No message with key(s)"
 def flush(self):
 """Write any pending changes to disk."""
 raise NotImplementedError, "Method must be implemented by subclass"
 def _dump_message(self, message, target):
 """Dump message contents to target file."""
 if isinstance(message, email.Message.Message):
 generator = email.Generator.Generator(target, False, 0)
 generator.flatten(message)
 elif isinstance(message, str):
 target.write(message)
 elif hasattr(message, 'read'):
 while True:
 buffer = message.read(4096) # Buffer size is arbitrary.
 if buffer == "":
 break
 target.write(buffer)
 else:
 raise TypeError, "Invalid message type"
class Maildir(Mailbox):
 """A qmail-style Maildir mailbox."""
 def __init__(self, dirname, factory=rfc822.Message):
 """Initialize a Maildir instance."""
 Mailbox.__init__(self, dirname, factory)
 if not os.path.exists(self._path):
 os.mkdir(self._path, 0700)
 os.mkdir(os.path.join(self._path, "tmp"), 0700)
 os.mkdir(os.path.join(self._path, "new"), 0700)
 os.mkdir(os.path.join(self._path, "cur"), 0700)
 self._toc = {}
 def add(self, message):
 """Add message and return assigned key."""
 tmp_file = self._create_tmp()
 try:
 self._dump_message(message, tmp_file)
 finally:
 tmp_file.close()
 if isinstance(message, MaildirMessage):
 subdir = message.get_subdir()
 suffix = ':' + message.get_info()
 if suffix == ':':
 suffix = ''
 else:
 subdir = 'new'
 suffix = ''
 uniq = os.path.basename(tmp_file.name).split(':')[0]
 dest = os.path.join(self._path, subdir, uniq + suffix)
 os.rename(tmp_file.name, dest)
 return uniq
 def remove(self, key):
 """Remove the keyed message; raise KeyError if it doesn't exist."""
 os.remove(os.path.join(self._path, self._lookup(key)))
 def discard(self, key):
 """If the keyed message exists, remove it."""
 # This overrides an inapplicable implementation in the superclass.
 try:
 self.remove(key)
 except KeyError:
 pass
 except OSError, e:
 if errno == errno.ENOENT:
 pass
 else:
 raise 
 def __setitem__(self, key, message):
 """Replace the keyed message; raise KeyError if it doesn't exist."""
 old_subpath = self._lookup(key)
 temp_key = self.add(message)
 temp_subpath = self._lookup(temp_key)
 if isinstance(message, MaildirMessage):
 # temp's subdir and suffix were specified by message.
 dominant_subpath = temp_subpath
 else:
 # temp's subdir and suffix were defaults from add().
 dominant_subpath = old_subpath
 subdir = os.path.split(dominant_subpath)[0]
 if dominant_subpath.find(':') != -1:
 suffix = ':' + dominant_subpath.split(':')[-1]
 else:
 suffix = ''
 self.discard(key)
 os.rename(os.path.join(self._path, temp_subpath),
 os.path.join(self._path, subdir, key + suffix))
 def get_message(self, key):
 """Return a Message representation or raise a KeyError."""
 subpath = self._lookup(key)
 f = file(os.path.join(self._path, subpath), 'r')
 try:
 msg = MaildirMessage(f)
 finally:
 f.close()
 subdir, name = os.path.split(subpath)
 if subdir == 'cur':
 msg.set_subdir('cur')
 if name.find(':') != -1:
 msg.set_info(name.split(':')[-1])
 return msg
 def get_string(self, key):
 """Return a string representation or raise a KeyError."""
 f = file(os.path.join(self._path, self._lookup(key)), 'r')
 try:
 return f.read()
 finally:
 f.close()
 def get_file(self, key):
 """Return a file-like representation or raise a KeyError."""
 f = file(os.path.join(self._path, self._lookup(key)), 'r')
 return _ProxyFile(f)
 def iterkeys(self):
 """Return an iterator over keys."""
 self._refresh()
 for key in self._toc:
 try:
 self._lookup(key)
 except KeyError:
 continue
 yield key
 def has_key(self, key):
 """Return True if the keyed message exists, False otherwise."""
 self._refresh()
 return key in self._toc
 def __len__(self):
 """Return a count of messages in the mailbox."""
 self._refresh()
 return len(self._toc)
 def flush(self):
 """Write any pending changes to disk."""
 return # Maildir changes are always written immediately.
 def list_folders(self):
 """Return a list of folder names."""
 result = []
 for entry in os.listdir(self._path):
 if len(entry) > 1 and entry[0] == '.' and \
 os.path.isdir(os.path.join(self._path, entry)):
 result.append(entry[1:])
 return result
 def open_folder(self, name):
 """Return a Maildir for the named folder, creating it if necessary."""
 path = os.path.join(self._path, '.' + name)
 maildirfolder_path = os.path.join(path, 'maildirfolder')
 result = Maildir(path)
 if not os.path.exists(maildirfolder_path):
 os.mknod(maildirfolder_path, 0600 | stat.S_IFREG)
 return result
 def remove_folder(self, name):
 """Delete the named folder, which must be empty."""
 path = os.path.join(self._path, '.' + name)
 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
 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)
 for root, dirs, files in os.walk(path, topdown=False):
 for entry in files:
 os.remove(os.path.join(root, entry))
 for entry in dirs:
 os.rmdir(os.path.join(root, entry))
 os.rmdir(path)
 def clean(self):
 """Delete old files in "tmp"."""
 now = time.time()
 for entry in os.listdir(os.path.join(self._path, 'tmp')):
 path = os.path.join(self._path, 'tmp', entry)
 if now - os.stat(path).st_atime > 129600: # 60 * 60 * 36
 os.remove(path)
 _count = 1 # This is used to generate unique file names.
 def _create_tmp(self):
 """Create a file in the tmp subdirectory and open and return it."""
 now = time.time()
 hostname = socket.gethostname()
 if '/' in hostname:
 hostname = hostname.replace('/', r'057円')
 if ':' in hostname:
 hostname = hostname.replace(':', r'072円')
 uniq = "%s.M%sP%sQ%s.%s" % (int(now), int(now % 1 * 1e6), os.getpid(),
 self._count, hostname)
 path = os.path.join(self._path, 'tmp', uniq)
 try:
 os.stat(path)
 except OSError, e:
 if e.errno == errno.ENOENT:
 self._count += 1
 return file(path, 'w+')
 else:
 raise
 else:
 raise Error, "Name clash prevented file creation: '%s'" % path
 def _refresh(self):
 """Update table of contents mapping."""
 self._toc = {}
 for subdir in ('new', 'cur'):
 for entry in os.listdir(os.path.join(self._path, subdir)):
 uniq = entry.split(':')[0]
 self._toc[uniq] = os.path.join(subdir, entry)
 def _lookup(self, key):
 """Use TOC to return subpath for given key, or raise a KeyError."""
 try:
 if os.path.exists(os.path.join(self._path, self._toc[key])):
 return self._toc[key]
 except KeyError:
 pass
 self._refresh()
 try:
 return self._toc[key]
 except KeyError:
 raise KeyError, "No message with key '%s'" % key
class Message(email.Message.Message):
 """Message with mailbox-format-specific properties."""
 def __init__(self, message=None):
 """Initialize a Message instance."""
 if isinstance(message, email.Message.Message):
 self._become_message(copy.deepcopy(message))
 elif isinstance(message, str):
 self._become_message(email.message_from_string(message))
 elif hasattr(message, "read"):
 self._become_message(email.message_from_file(message))
 elif message is None:
 email.Message.Message.__init__(self)
 else:
 raise TypeError, "Invalid message type"
 def _become_message(self, message):
 """Assume the non-format-specific state of message."""
 for name in ('_headers', '_unixfrom', '_payload', '_charset',
 'preamble', 'epilogue', 'defects', '_default_type'):
 self.__dict__[name] = message.__dict__[name]
 def _explain_to(self, message):
 """Copy format-specific state to message insofar as possible."""
 if isinstance(message, Message):
 return # There's nothing format-specific to explain.
 else:
 raise TypeError, "Cannot convert to specified type"
class MaildirMessage(Message):
 """Message with Maildir-specific properties."""
 def __init__(self, message=None):
 """Initialize a MaildirMessage instance."""
 Message.__init__(self, message)
 self._subdir = 'new'
 self._info = ''
 if isinstance(message, Message):
 message._explain_to(self)
 def get_subdir(self):
 """Return 'new' or 'cur'."""
 return self._subdir
 def set_subdir(self, subdir):
 """Set subdir to 'new' or 'cur'."""
 if subdir == 'new' or subdir == 'cur':
 self._subdir = subdir
 else:
 raise ValueError, "subdir must be 'new' or 'cur'"
 def get_flags(self):
 """Return as a string the flags that are set."""
 if len(self._info) > 2 and self._info[:2] == '2,':
 return self._info[2:]
 else:
 return ""
 def set_flags(self, flags):
 """Set the given flags and unset all others."""
 if self.get_subdir() == 'new':
 self.set_subdir('cur')
 self._info = '2,' + ''.join(sorted(flags))
 def add_flags(self, flags):
 """Set the given flags without changing others.""" 
 self.set_flags(''.join(set(self.get_flags()) | set(flags)))
 def remove_flags(self, flags):
 """Unset the given string flags (if set) without changing other."""
 if self.get_flags() != "":
 self.set_flags(''.join(set(self.get_flags()) - set(flags)))
 def get_info(self):
 """Get the message's "info" as a string."""
 return self._info
 def set_info(self, info):
 """Set the message's "info" string."""
 if self.get_subdir() == 'new':
 self.set_subdir('cur')
 if isinstance(info, str):
 self._info = info
 else:
 raise TypeError, "info must be a string"
 def _explain_to(self, message):
 """Copy Maildir-specific state to message insofar as possible."""
 if isinstance(message, Message):
 return
 # XXX convert to other formats as needed
 else:
 raise TypeError, "Cannot convert to specified type"
class _ProxyFile:
 """A read-only wrapper of a file."""
 def __init__(self, f, pos=None):
 """Initialize a _ProxyFile."""
 self._file = f
 if pos is None:
 self._pos = f.tell()
 else:
 self._pos = pos
 def read(self, size=None):
 """Read bytes."""
 return self._read(size, self._file.read)
 def readline(self, size=None):
 """Read a line."""
 return self._read(size, self._file.readline)
 def readlines(self, sizehint=None):
 """Read multiple lines."""
 result = []
 for line in self:
 result.append(line)
 if sizehint is not None:
 sizehint -= len(line)
 if sizehint <= 0:
 break
 return result
 def __iter__(self):
 """Iterate over lines."""
 return iter(self.readline, "")
 def tell(self):
 """Return the position."""
 return self._pos
 def seek(self, offset, whence=0):
 """Change position."""
 if whence == 1:
 self._file.seek(self._pos)
 self._file.seek(offset, whence)
 self._pos = self._file.tell()
 def close(self):
 """Close the file."""
 del self._file
 def _read(self, size, read_method):
 """Read size bytes using read_method."""
 if size is None:
 size = -1
 self._file.seek(self._pos)
 result = read_method(size)
 self._pos = self._file.tell()
 return result
class _PartialFile(_ProxyFile):
 """A read-only wrapper of part of a file."""
 def __init__(self, f, start=None, stop=None):
 """Initialize a _PartialFile."""
 _ProxyFile.__init__(self, f, start)
 self._start = start
 self._stop = stop
 def tell(self):
 """Return the position with respect to start."""
 return _ProxyFile.tell(self) - self._start
 def seek(self, offset, whence=0):
 """Change position, possibly with respect to start or stop."""
 if whence == 0:
 self._pos = self._start
 whence = 1
 elif whence == 2:
 self._pos = self._stop
 whence = 1
 _ProxyFile.seek(self, offset, whence)
 def _read(self, size, read_method):
 """Read size bytes using read_method, honoring start and stop."""
 remaining = self._stop - self._pos
 if remaining <= 0:
 return ''
 if size is None or size < 0 or size > remaining:
 size = remaining
 return _ProxyFile._read(self, size, read_method)
class Error(Exception):
 """Raised for module-specific errors."""
--- NEW FILE: test_mailbox.py ---
import os
import time
import stat
import socket
import email
import email.Message
import rfc822
import re
import StringIO
from test import test_support
import unittest
import mailbox
class TestBase(unittest.TestCase):
 def _check_sample(self, msg):
 # Inspect a mailbox.Message representation of the sample message
 self.assert_(isinstance(msg, email.Message.Message))
[...985 lines suppressed...]
 "From":""""Gregory K. Johnson" <gkj at gregorykjohnson.com>""",
 "To":"gkj at gregorykjohnson.com",
 "Subject":"Sample message",
 "Mime-Version":"1.0",
 "Content-Type":"""multipart/mixed; boundary="NMuMz9nt05w80d4+\"""",
 "Content-Disposition":"inline",
 "User-Agent": "Mutt/1.5.9i" }
_sample_payloads = ("This is a sample message.\n\n-- \nGregory K. Johnson\n",
"H4sICM2D1UIAA3RleHQAC8nILFYAokSFktSKEoW0zJxUPa7wzJIMhZLyfIWczLzUYj0uAHTs\n3FYlAAAA\n")
def test_main():
 test_support.run_unittest(TestMaildir, TestMessage, TestMaildirMessage,
 TestMessageConversion, TestProxyFile,
 TestPartialFile)
if __name__ == '__main__':
 test_main()
--- NEW FILE: docs.tex ---
% Compile using mkhowto, e.g.:
% ../../../dist/src/Doc/tools/mkhowto --html docs.tex
\documentclass{manual}
\title{\module{mailbox} Module Documentation}
\begin{document}
\maketitle
\tableofcontents
\input{libmailbox}
\end{document}
Index: libmailbox.tex
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/mailbox/libmailbox.tex,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -d -r1.1 -r1.2
--- libmailbox.tex	9 Jul 2005 20:15:25 -0000	1.1
+++ libmailbox.tex	26 Jul 2005 20:47:48 -0000	1.2
@@ -41,48 +41,53 @@
 \subsection{\class{Mailbox} objects}
 \label{mailbox-objects}
 
+\begin{classdesc*}{Mailbox}
+Represents a mailbox, which may be inspected and modified.
+\end{classdesc*}
+
 The \class{Mailbox} interface is dictionary-like, with small keys
 corresponding to messages. Keys are issued by the \class{Mailbox} instance
 with which they will be used and are only meaningful to that \class{Mailbox}
-instance. (\strong{Implementation note:} \class{mbox}, \class{MH},
-\class{Babyl}, and \class{MMDF} instances use integers as keys, and
-\class{Maildir} instances uses short strings.) A key continues to identify a
-message even if the corresponding message is modified by replacing it with
-another message. Because keys are issued by a \class{Mailbox} instance rather
+instance. A key continues to identify a message even if the corresponding
+message is modified, such as by replacing it with another message. Messages may
+be added to a \class{Mailbox} instance using the set-like method
+\method{add()}. Because keys are issued by a \class{Mailbox} instance rather
 than being chosen, the conventional method for adding an item to a mapping
-(assigning a value to a new key) cannot be used. Instead, messages may be
-added to a \class{Mailbox} instance using the set-like method \method{add()}.
-For uniformity, \method{remove()} and \method{discard()} are also available.
+(assigning a value to a new key) cannot be used. (\strong{Implementation note:}
+\class{mbox}, \class{MH}, \class{Babyl}, and \class{MMDF} instances use
+integers as keys, and \class{Maildir} instances use short strings.)
 
-\class{Mailbox} semantics differ from dictionary semantics in important ways.
-Each time a message is requested, a new message representation (typically a
-\class{Message} instance) is generated based upon the current state of the
-underlying message. Similarly, when a message representation is assigned into
-a \class{Mailbox} instance's mapping, the message representation's contents
-are copied into the mailbox. In neither case is a reference to the message
-representation kept by the \class{Mailbox} instance.
+\class{Mailbox} interface semantics differ from dictionary semantics in some
+ways. Each time a message is requested, a new message representation (typically
+a \class{Message} instance) is generated based upon the current state of the
+underlying message. The \class{Mailbox} instance does not reuse this
+representation or keep a reference to it. Similarly, when a message
+representation is assigned into a \class{Mailbox} instance's mapping, the
+message representation's contents are copied into the mailbox. In neither case
+is a reference to the message representation kept by the \class{Mailbox}
+instance.
 
-Also unlike dictionaries, the default \class{Mailbox} iterator iterates over
-message representations, not keys. Moreover, modification of a mailbox during
-iteration is safe and well-defined. Messages added to the mailbox after an
-iterator is created will not be seen by the iterator, and messages removed
-from the mailbox before the iterator yields them will be silently skipped. All
-three kinds of iterator---key, value, and item---have this behavior.
+The default \class{Mailbox} iterator iterates over message representations, not
+keys as dictionaries do. Moreover, modification of a mailbox during iteration
+is safe and well-defined. Messages added to the mailbox after an iterator is
+created will not be seen by the iterator. Messages removed from the mailbox
+before the iterator yields them will be silently skipped, though using a key
+from an iterator may result in a \exception{KeyError} exception if the
+corresponding message is subsequently removed.
 
-\class{Mailbox} itself is not intended to be instantiated. Rather, it is
-intended to define an interface and to be inherited from by format-specific
-subclasses. You may directly instantiate a subclass, or alternatively you may
-use the module-level \function{open()} function to instantiate the appropriate
-subclass.
+\class{Mailbox} itself is intended to define an interface and to be inherited
+from by format-specific subclasses but is not intended to be instantiated.
+Instead, you may directly instantiate a subclass or use the module-level
+\function{open()} function to do so.
 
-\begin{funcdesc}{open}{path\optional{, format, factory}}
+\begin{funcdesc}{open}{path\optional{, format\optional{, factory}}}
 Instantiate and return an instance of the appropriate \class{Mailbox} subclass
 for the mailbox at \var{path}. The mailbox is created if it does not already
 exist.
 
 If \var{format} is specified, it should be one of "Maildir", "mbox", "MH",
 "Babyl", or "MMDF" (case insensitive). If \var{format} is not specified, then
-the format of the mailbox will be automatically detected. Automatic detection
+the format of the mailbox is automatically detected. Automatic detection
 involves inspecting the mailbox at \var{path} if such a mailbox exists or if
 no such mailbox exists inspecting \var{path} itself and assuming Maildir
 format if \var{path} ends with the system's path separator (e.g., "/" or
@@ -93,7 +98,7 @@
 representation. If \var{factory} is not specified, \class{Message} instances
 are used to represent messages.
 
-For historic reasons, some subclasses of \class{Mailbox} take instantiation
+For historical reasons, some subclasses of \class{Mailbox} take instantiation
 arguments with different purposes, names, or default values. The
 \function{open()} function is intended to present a convenient, uniform
 interface for \class{Mailbox} instantiation while maintaining backward
@@ -110,126 +115,101 @@
 \class{email.Message.Message} instance, a string, or a file-like object. If
 \var{message} is an instance of the appropriate format-specific
 \class{Message} subclass (e.g., if it's an \class{mboxMessage} instance and
-this is an \class{mbox} instance), its format-specific information will be
-used. Otherwise, reasonable defaults for format-specific information will be
-used.
+this is an \class{mbox} instance), its format-specific information is used.
+Otherwise, reasonable defaults for format-specific information are used.
 \end{methoddesc}
 
 \begin{methoddesc}[Mailbox]{remove}{key}
-Delete the message corresponding to \var{key} from the mailbox. Raise a
-\exception{KeyError} exception if no such message exists.
+\methodline{__delitem__}{key}
+\methodline{discard}{key}
+Delete the message corresponding to \var{key} from the mailbox.
 
-The behavior of \method{discard()} may be preferred to that of
-\method{remove()} if the underlying mailbox format supports concurrent
-modification by other processes, as is the case for Maildir.
+If no such message exists, a \exception{KeyError} exception is raised if the
+method was called as \method{remove()} or \method{__delitem__()} and no
+exception is raised if the method was called as \method{discard()}. The
+behavior of \method{discard()} may be preferred if the underlying mailbox
+format supports concurrent modification by other processes.
 \end{methoddesc}
 
-\begin{methoddesc}[Mailbox]{discard}{key}
-Delete the message corresponding to \var{key} from the mailbox, or do nothing
-if no such message exists.
-\end{methoddesc}
+\begin{methoddesc}[Mailbox]{__setitem__}{key, message}
+Replace the message corresponding to \var{key} with the message represented by
+\var{message}. Raise a \exception{KeyError} exception if no message already
+corresponds to \var{key}.
 
-\begin{methoddesc}[Mailbox]{iterkeys}{}
-Return an iterator over all keys.
+As with \method{add()}, parameter \var{message} may be a \class{Message}
+instance, an \class{email.Message.Message} instance, a string, or a file-like
+object. If \var{message} is an instance of the appropriate format-specific
+\class{Message} subclass (e.g., if it's an \class{mboxMessage} instance and
+this is an \class{mbox} instance), its format-specific information is used.
+Otherwise, the format-specific information of the message that currently
+corresponds to \var{key} is left unchanged. 
 \end{methoddesc}
 
-\begin{methoddesc}[Mailbox]{keys}{}
-Return a list of all keys.
+\begin{methoddesc}[Mailbox]{iterkeys}{}
+\methodline{keys}{}
+Return an iterator over all keys if called as \method{iterkeys()} or return a
+list of keys if called as \method{keys()}.
 \end{methoddesc}
 
 \begin{methoddesc}[Mailbox]{itervalues}{}
-Return an iterator over representations of all messages. The messages are
-represented as \class{Message} instances unless a custom message factory was
-specified when the \class{Mailbox} instance was initialized.
-\end{methoddesc}
-
-\begin{methoddesc}[Mailbox]{values}{}
-Return a list of representations of all messages. The messages are represented
-as \class{Message} instances unless a custom message factory was specified
-when the \class{Mailbox} instance was initialized. \warning{This may require a
-large amount of memory.}
+\methodline{__iter__}{}
+\methodline{values}{}
+Return an iterator over representations of all messages if called as
+\method{itervalues()} or \method{__iter__()} or return a list of such
+representations if called as \method{values()}. The messages are represented as
+\class{Message} instances unless a custom message factory was specified when
+the \class{Mailbox} instance was initialized. \note{The behavior of
+\method{__iter__()} is unlike that of dictionaries, which iterate over keys.}
 \end{methoddesc}
 
 \begin{methoddesc}[Mailbox]{iteritems}{}
+\methodline{items}{}
 Return an iterator over (\var{key}, \var{message}) pairs, where \var{key} is a
-key and \var{message} is a message representation. The messages are
-represented as \class{Message} instances unless a custom message factory was
-specified when the \class{Mailbox} instance was initialized.
-\end{methoddesc}
-
-\begin{methoddesc}[Mailbox]{items}{}
-Return a list of (\var{key}, \var{message}) pairs, where \var{key} is a key
-and \var{message} is a message representation. The messages are represented as
-\class{Message} instances unless a custom message factory was specified when
-the \class{Mailbox} instance was initialized. \warning{This may require a
-large amount of memory.}
+key and \var{message} is a message representation, if called as
+\method{iteritems()} or return a list of such pairs if called as
+\method{items()}. The messages are represented as \class{Message} instances
+unless a custom message factory was specified when the \class{Mailbox} instance
+was initialized.
 \end{methoddesc}
 
 \begin{methoddesc}[Mailbox]{get}{key\optional{, default=None}}
-Return a representation of the message corresponding to \var{key}, or
-\var{default} if no such message exists. The message is represented as a
-\class{Message} instance unless a custom message factory was specified when
-the \class{Mailbox} instance was initialized.
+\methodline{__getitem__}{key}
+Return a representation of the message corresponding to \var{key}. If no such
+message exists, \var{default} is returned if the method was called as
+\method{get()} and a \exception{KeyError} exception is raised if the method was
+called as \method{__getitem__()}. The message is represented as a
+\class{Message} instance unless a custom message factory was specified when the
+\class{Mailbox} instance was initialized.
 \end{methoddesc}
 
-\begin{methoddesc}[Mailbox]{get_message}{key\optional{, default=None}}
+\begin{methoddesc}[Mailbox]{get_message}{key}
 Return a \class{Message} representation of the message corresponding to
-\var{key}, or \var{default} if no such message exists.
+\var{key}, or raise a \exception{KeyError} exception if no such message
+exists.
 \end{methoddesc}
 
-\begin{methoddesc}[Mailbox]{get_string}{key\optional{, default=None}}
+\begin{methoddesc}[Mailbox]{get_string}{key}
 Return a string representation of the message corresponding to \var{key}, or
-\var{default} if no such message exists.
+raise a \exception{KeyError} exception if no such message exists.
 \end{methoddesc}
 
-\begin{methoddesc}[Mailbox]{get_file}{key\optional{, default=None}}
+\begin{methoddesc}[Mailbox]{get_file}{key}
 Return a file-like representation of the message corresponding to \var{key},
-or \var{default} if no such message exists.
+or raise a \exception{KeyError} exception if no such message exists. This file
+should be closed once it is no longer needed.
 
 \note{Unlike other representations of messages, file-like representations are
 not independent of the \class{Mailbox} instance that created them or of the
-underlying mailbox. If the mailbox is modified, the file-like representation
-may become unusable. More specific documentation is provided by each
-subclass.}
+underlying mailbox, although their exact behavior is mailbox-format dependent.
+More specific documentation is provided by each subclass.}
 \end{methoddesc}
 
 \begin{methoddesc}[Mailbox]{has_key}{key}
+\methodline{__contains__}{}
 Return \code{True} if \var{key} corresponds to a message, \code{False}
 otherwise.
 \end{methoddesc}
 
-\begin{methoddesc}[Mailbox]{__iter__}{}
-Synonymous with \method{itervalues()}. \note{This is unlike the default
-iteration behavior of dictionaries.}
-\end{methoddesc}
-
-\begin{methoddesc}[Mailbox]{__getitem__}{key}
-Like \method{get()}, except raises a \exception{KeyError} exception if the
-message corresponding to \var{key} does not exist.
-\end{methoddesc}
-
-\begin{methoddesc}[Mailbox]{__setitem__}{key, message}
-Replace the message corresponding to \var{key} with the message represented by
-\var{message}. Raise a \exception{KeyError} exception if no message already
-corresponds to the given key.
-
-As with \method{add()}, parameter \var{message} may be a \class{Message}
-instance, an \class{email.Message.Message} instance, a string, or a file-like
-object. If \var{message} is an instance of the appropriate format-specific
-\class{Message} subclass (e.g., if it's an \class{mboxMessage} instance and
-this is an \class{mbox} instance), its format-specific information will be
-used. Otherwise, reasonable defaults for format-specific information will be
-used. 
-\end{methoddesc}
-
-\begin{methoddesc}[Mailbox]{__delitem__}{key}
-Synonymous with \method{remove()}.
-\end{methoddesc}
-
-\begin{methoddesc}[Mailbox]{__contains__}{key}
-Synonymous with \method{has_key()}.
-\end{methoddesc}
-
 \begin{methoddesc}[Mailbox]{__len__}{}
 Return a count of messages in the mailbox.
 \end{methoddesc}
@@ -240,34 +220,125 @@
 
 \begin{methoddesc}[Mailbox]{pop}{key\optional{, default}}
 Return a representation of the message corresponding to \var{key} and delete
-the message. If no such message exists, return \var{default} if it was
-supplied or if \var{default} was not supplied raise a \exception{KeyError}
-exception. The message is represented as a \class{Message} instance unless a
-custom message factory was specified when the \class{Mailbox} instance was
-initialized.
+the message. If no such message exists, return \var{default} if it was supplied
+or else raise a \exception{KeyError} exception. The message is represented as a
+\class{Message} instance unless a custom message factory was specified when the
+\class{Mailbox} instance was initialized.
 \end{methoddesc}
 
 \begin{methoddesc}[Mailbox]{popitem}{}
 Return an arbitrary (\var{key}, \var{message}) pair, where \var{key} is a key
 and \var{message} is a message representation, and delete the corresponding
-message from the mailbox. If the mailbox is empty, raise a
-\exception{KeyError} exception. The message is represented as a
-\class{Message} instance unless a custom message factory was specified when
-the \class{Mailbox} instance was initialized.
+message. If the mailbox is empty, raise a \exception{KeyError} exception. The
+message is represented as a \class{Message} instance unless a custom message
+factory was specified when the \class{Mailbox} instance was initialized.
 \end{methoddesc}
 
 \begin{methoddesc}[Mailbox]{update}{arg}
-If \var{arg} is a mailbox-like \var{key}-to-\var{message} mapping or an
-iterable of (\var{key}, \var{message}) pairs, update the mailbox so that, for
+Parameter \var{arg} should be a \var{key}-to-\var{message} mapping or an
+iterable of (\var{key}, \var{message}) pairs. Updates the mailbox so that, for
 each given \var{key} and \var{message}, the message corresponding to \var{key}
-is set to \var{message} as if by using \method{__setitem__()}. \note{Keyword
-arguments are not supported, unlike with dictionaries.}
+is set to \var{message} as if by using \method{__setitem__()}. Each \var{key}
+must already correspond to a message in the mailbox or a \exception{KeyError}
+exception will be raised. \note{Unlike with dictionaries, keyword arguments
+are not supported.}
+\end{methoddesc}
+
+\begin{methoddesc}[Mailbox]{flush}{}
+Write any pending changes to the filesystem. For some \class{Mailbox}
+subclasses, this is done automatically and calling \method{flush()} has no
+effect. More specific documentation is provided by each subclass.
 \end{methoddesc}
 
 
 \subsubsection{\class{Maildir}}
 \label{mailbox-maildir}
 
+\begin{classdesc}{Maildir}{dirname\optional{, factory}}
+A subclass of \class{Mailbox} for mailboxes in Maildir format. Parameter
+\var{dirname} is the path to the mailbox. Parameter \var{factory} has the same
+meaning as with the module-level \method{open()} function. For historical
+reasons, \var{factory} defaults to \class{rfc822.Message}.
+\end{classdesc}
+
+Maildir is a directory-based mailbox format invented for the qmail MTA and now
+widely supported by other programs. Messages in a Maildir mailbox are stored
+in separate files within a shared directory structure. This structure allows
+Maildir mailboxes to be accessed and modified by multiple unrelated programs
+without data corruption, so file locking is unnecessary.
+
+Folders, as introduced by the Courier MTA, are supported. Each folder is
+itself a Maildir mailbox and is represented as a \class{Maildir} instance. Any
+subdirectory of the main Maildir directory is considered a folder if
+\character{.} is the first character in its name. Folder names are represented
+without the leading dot. For example, "Sent.2005.07" would be the name of a
+folder implemented with a directory called ".Sent.2005.07" on the filesystem.
+
+\class{Maildir} instances have all of the methods of \class{Mailbox} in
+addition to the following:
+
+\begin{methoddesc}[Maildir]{list_folders}{}
+Return a list of the names of all folders. If there are no folders, the empty
+list is returned.
+\end{methoddesc}
+
+\begin{methoddesc}[Maildir]{open_folder}{name}
+Return a \class{Maildir} instance representing the folder whose name is
+\var{name}. The folder will be created if it does not exist.
+\end{methoddesc}
+
+\begin{methoddesc}[Maildir]{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.
+\end{methoddesc}
+
+\begin{methoddesc}[Maildir]{clean}{}
+Delete temporary files from the mailbox that have not been accessed in the
+last 36 hours. The Maildir specification says that mail-reading programs
+should do this occassionally.
+\end{methoddesc}
+
+\warning{Three \class{Maildir} methods---\method{add()},
+\method{__setitem__()}, and \method{update()}---generate unique file names
+based upon the current process ID. When using multiple threads, undetected
+clashes may occur and cause corruption of the mailbox unless threads are
+coordinated to avoid using these methods to manipulate the same mailbox
+simultaneously.}
+
+Some \class{Mailbox} methods implemented by \class{Maildir} deserve special
+remarks: 
+
+\begin{methoddesc}[Maildir]{add}{message}
+\methodline[Maildir]{__setitem__}{key, message}
+\methodline[Maildir]{update}{arg}
+\warning{These methods generate unique file names based upon the current
+process ID. When using multiple threads, undetected name clashes may occur and
+cause corruption of the mailbox unless threads are coordinated to avoid using
+these methods to manipulate the same mailbox simultaneously.}
+\end{methoddesc}
+
+\begin{methoddesc}[Maildir]{flush}{}
+All changes to Maildir mailboxes are immediately applied. This method does
+nothing.
+\end{methoddesc}
+
+\begin{methoddesc}[Maildir]{get_file}{key}
+Depending upon the host platform, it may not be possible to use a
+\class{Maildir} instance to modify or remove the underlying message while the
+returned file remains open.
+\end{methoddesc}
+
+\begin{seealso}
+ \seelink{http://www.qmail.org/man/man5/maildir.html}{maildir man page from
+ qmail}{The original specification of the format.}
+ \seelink{http://cr.yp.to/proto/maildir.html}{Using maildir format}{Notes
+ on Maildir by it's inventor. Includes an updated name-creation scheme and
+ details on "info" semantics.}
+ \seelink{http://www.courier-mta.org/?maildir.html}{maildir man page from
+ Courier}{Another specification of the format. Describes a common extension
+ for supporting folders.}
+\end{seealso}
 
 \subsubsection{\class{mbox}}
 \label{mailbox-mbox}
@@ -288,10 +359,132 @@
 \subsection{\class{Message} objects}
 \label{mailbox-message-objects}
 
+The \class{Message} class is an extension of a class of the same name from the
+\module{email.Message} module. In addition, subclasses of \class{Message}
+support mailbox-format-specific state and behavior.
+
+\begin{classdesc}{Message}{\optional{message}}
+Represents a message with mailbox-format-specific properties.
+
+If \var{message} is omitted, the new instance is created in a default, empty
+state. If \var{message} is an \class{email.Message.Message} instance, its
+contents are copied, converting any format-specific information insofar as
+possible if \var{message} is a \class{Message} instance. If \var{message} is a
+string or a file, it should contain an \rfc{2822}-compliant message, which is
+read and parsed.
+\end{classdesc}
+
+The format-specific state and behaviors offered by subclasses vary, but in
+general it is only the properties that are not specific to a particular
+mailbox that are supported (although presumably the properties are specific to
+a particular mailbox format). For example, file offsets for single-file mailbox
+formats and file names for directory-based mailbox formats are not retained,
+but state such as whether a message has been read or marked as important by the
+user is.
+
+In some situations, the time and memory overhead involved in generating
+\class{Message} representations might not not justified. For such situations,
+\class{Mailbox} instances also offer string and file-like representations, and
+a custom message factory may be specified when a \class{Mailbox} instance is
+initialized. There is no requirement to use the \class{Message} class to
+represent messages from a mailbox.
+
+All of the \class{email.Message.Message} class's methods and members are
+supported by \class{Message}, and subclasses of \class{Message} provide many
+additional format-specific methods. Some functionality supported by all
+\class{Message} subclasses is accessible via the following methods:
+
+XXX
 
 \subsubsection{\class{MaildirMessage}}
 \label{mailbox-maildirmessage}
 
+\begin{classdesc}{MaildirMessage}{\optional{message}}
+Represents a message with Maildir-specific behaviors. Parameter \var{message}
+has the same meaning as with the \class{Message} constructor.
+\end{classdesc}
+
+Maildir messages are stored in individual files, in either the \file{new} or
+the \file{cur} subdirectory of the Maildir. Typically, messages are delivered
+to the \file{new} subdirectory and then moved to the \file{cur} subdirectory
+as soon as the user's mail reading application detects them. Each message in
+\file{cur} has an "info" section added to its file name to store information
+about its state. The "info" section may take one of two forms: it may contain
+a standardized list of flags or it may contain so-called experimental
+information.
+
+Standard flags for Maildir messages are as follows:
+
+\begin{tableiii}{l|l|l}{textrm}{Flag}{Meaning}{Explanation}
+\lineiii{D}{Draft}{Under composition}
+\lineiii{F}{Flagged}{Marked by the user as important}
+\lineiii{P}{Passed}{Forwarded, resent, or bounced by the user}
+\lineiii{R}{Replied}{Responded to}
+\lineiii{S}{Seen}{Read by the user}
+\lineiii{T}{Trashed}{Marked for subsequent deletion}
+\end{tableiii}
+
+\class{MaildirMessage} instances offer the following methods:
+
+\begin{methoddesc}[MaildirMessage]{get_subdir}{}
+Return either "new" (if the message should be stored in the \file{new}
+subdirectory) or "cur" (if the message should be stored in the \file{cur}
+subdirectory). \note{A Maildir message typically moves from \file{new} to
+\file{cur} when an MUA first detects it, but most MUAs refer to a message as
+new if it is unseen, i.e., if \code{"S" in get_flags()} is \code{False}.}
+\end{methoddesc}
+
+\begin{methoddesc}[MaildirMessage]{set_subdir}{subdir}
+Set the subdirectory the message should be stored in. Parameter \var{subdir}
+must be either "new" or "cur".
+\end{methoddesc}
+
+\begin{methoddesc}[MaildirMessage]{get_flags}{}
+Return a string specifying the flags that are currently set. If the message
+complies with the standard Maildir format, the result is the concatenation in
+alphabetical order of zero or one occurrence of each of \character{D},
+\character{F}, \character{P}, \character{R}, \character{S}, and \character{T}.
+The empty string is returned if no flags are set, if the message is new, or if
+"info" contains experimental semantics.
+\end{methoddesc}
+
+\begin{methoddesc}[MaildirMessage]{set_flags}{flags}
+Set the flags specified by \var{flags} and unset all others. Parameter
+\var{flags} should be the concatenation in any order of zero or more
+occurrences of each of \character{D}, \character{F}, \character{P},
+\character{R}, \character{S}, and \character{T}. If the message is new, it
+is made non-new. The current "info" is overwritten whether or not it contains
+experimental information instead of flags.
+\end{methoddesc}
+
+\begin{methoddesc}[MaildirMessage]{add_flags}{flags}
+Set the flags specified by \var{flags} (if they are not set), and don't change
+other flags. Parameter \var{flags} should be the concatenation in any order of
+zero or more occurrences of each of \character{D}, \character{F},
+\character{P}, \character{R}, \character{S}, and \character{T}. If the message
+is new, it is made non-new. If "info" contains experimental information rather
+than flags, the "info" will be overwritten.
+\end{methoddesc}
+
+\begin{methoddesc}[MaildirMessage]{remove_flags}{flags}
+Unset the flags specified by \var{flags} (if they are set), and don't change
+other flags. Parameter \var{flags} should be the concatenation in any order of
+zero or more occurrences of each of \character{D}, \character{F},
+\character{P}, \character{R}, \character{S}, and \character{T}. If the message
+is new, it remains so. If "info" contains experimental information rather than
+flags, the current "info" is not modified.
+\end{methoddesc}
+
+\begin{methoddesc}[MaildirMessage]{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
+flags).
+\end{methoddesc}
+
+\begin{methoddesc}[MaildirMessage]{set_info}{info}
+Set "info" to \var{info}, which should be a string. If the message is new, it
+is made non-new.
+\end{methoddesc}
 
 \subsubsection{\class{mboxMessage}}
 \label{mailbox-mboxmessage}


More information about the Python-checkins mailing list

AltStyle によって変換されたページ (->オリジナル) /