Merge email package 4.0 from the sandbox, including documentation, test cases,

and NEWS updates.
This commit is contained in:
Barry Warsaw 2006-03-18 15:41:53 +00:00
parent 9ae019bf5b
commit 40ef0067ad
44 changed files with 3821 additions and 468 deletions

View File

@ -1,83 +1,69 @@
#!/usr/bin/env python
"""Send the contents of a directory as a MIME message.
"""Send the contents of a directory as a MIME message."""
Usage: dirmail [options] from to [to ...]*
Options:
-h / --help
Print this message and exit.
-d directory
--directory=directory
Mail the contents of the specified directory, otherwise use the
current directory. Only the regular files in the directory are sent,
and we don't recurse to subdirectories.
`from' is the email address of the sender of the message.
`to' is the email address of the recipient of the message, and multiple
recipients may be given.
The email is sent by forwarding to your local SMTP server, which then does the
normal delivery process. Your local machine must be running an SMTP server.
"""
import sys
import os
import getopt
import sys
import smtplib
# For guessing MIME type based on file name extension
import mimetypes
from email import Encoders
from email.Message import Message
from email.MIMEAudio import MIMEAudio
from email.MIMEBase import MIMEBase
from email.MIMEMultipart import MIMEMultipart
from email.MIMEImage import MIMEImage
from email.MIMEText import MIMEText
from optparse import OptionParser
from email import encoders
from email.message import Message
from email.mime.audio import MIMEAudio
from email.mime.base import MIMEBase
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
COMMASPACE = ', '
def usage(code, msg=''):
print >> sys.stderr, __doc__
if msg:
print >> sys.stderr, msg
sys.exit(code)
def main():
try:
opts, args = getopt.getopt(sys.argv[1:], 'hd:', ['help', 'directory='])
except getopt.error, msg:
usage(1, msg)
parser = OptionParser(usage="""\
Send the contents of a directory as a MIME message.
dir = os.curdir
for opt, arg in opts:
if opt in ('-h', '--help'):
usage(0)
elif opt in ('-d', '--directory'):
dir = arg
if len(args) < 2:
usage(1)
sender = args[0]
recips = args[1:]
Usage: %prog [options]
Unless the -o option is given, the email is sent by forwarding to your local
SMTP server, which then does the normal delivery process. Your local machine
must be running an SMTP server.
""")
parser.add_option('-d', '--directory',
type='string', action='store',
help="""Mail the contents of the specified directory,
otherwise use the current directory. Only the regular
files in the directory are sent, and we don't recurse to
subdirectories.""")
parser.add_option('-o', '--output',
type='string', action='store', metavar='FILE',
help="""Print the composed message to FILE instead of
sending the message to the SMTP server.""")
parser.add_option('-s', '--sender',
type='string', action='store', metavar='SENDER',
help='The value of the From: header (required)')
parser.add_option('-r', '--recipient',
type='string', action='append', metavar='RECIPIENT',
default=[], dest='recipients',
help='A To: header value (at least one required)')
opts, args = parser.parse_args()
if not opts.sender or not opts.recipients:
parser.print_help()
sys.exit(1)
directory = opts.directory
if not directory:
directory = '.'
# Create the enclosing (outer) message
outer = MIMEMultipart()
outer['Subject'] = 'Contents of directory %s' % os.path.abspath(dir)
outer['To'] = COMMASPACE.join(recips)
outer['From'] = sender
outer['Subject'] = 'Contents of directory %s' % os.path.abspath(directory)
outer['To'] = COMMASPACE.join(opts.recipients)
outer['From'] = opts.sender
outer.preamble = 'You will not see this in a MIME-aware mail reader.\n'
# To guarantee the message ends with a newline
outer.epilogue = ''
for filename in os.listdir(dir):
path = os.path.join(dir, filename)
for filename in os.listdir(directory):
path = os.path.join(directory, filename)
if not os.path.isfile(path):
continue
# Guess the content type based on the file's extension. Encoding
@ -108,15 +94,20 @@ def main():
msg.set_payload(fp.read())
fp.close()
# Encode the payload using Base64
Encoders.encode_base64(msg)
encoders.encode_base64(msg)
# Set the filename parameter
msg.add_header('Content-Disposition', 'attachment', filename=filename)
outer.attach(msg)
# Now send the message
# Now send or store the message
composed = outer.as_string()
if opts.output:
fp = open(opts.output, 'w')
fp.write(composed)
fp.close()
else:
s = smtplib.SMTP()
s.connect()
s.sendmail(sender, recips, outer.as_string())
s.sendmail(opts.sender, opts.recipients, composed)
s.close()

View File

@ -2,8 +2,8 @@
import smtplib
# Here are the email package modules we'll need
from email.MIMEImage import MIMEImage
from email.MIMEMultipart import MIMEMultipart
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart
COMMASPACE = ', '
@ -15,8 +15,6 @@ msg['Subject'] = 'Our family reunion'
msg['From'] = me
msg['To'] = COMMASPACE.join(family)
msg.preamble = 'Our family reunion'
# Guarantees the message ends in a newline
msg.epilogue = ''
# Assume we know that the image files are all in PNG format
for file in pngfiles:

View File

@ -2,7 +2,7 @@
import smtplib
# Import the email modules we'll need
from email.MIMEText import MIMEText
from email.mime.text import MIMEText
# Open a plain text file for reading. For this example, assume that
# the text file contains only ASCII characters.

View File

@ -1,59 +1,44 @@
#!/usr/bin/env python
"""Unpack a MIME message into a directory of files.
"""Unpack a MIME message into a directory of files."""
Usage: unpackmail [options] msgfile
Options:
-h / --help
Print this message and exit.
-d directory
--directory=directory
Unpack the MIME message into the named directory, which will be
created if it doesn't already exist.
msgfile is the path to the file containing the MIME message.
"""
import sys
import os
import getopt
import sys
import email
import errno
import mimetypes
import email
def usage(code, msg=''):
print >> sys.stderr, __doc__
if msg:
print >> sys.stderr, msg
sys.exit(code)
from optparse import OptionParser
def main():
try:
opts, args = getopt.getopt(sys.argv[1:], 'hd:', ['help', 'directory='])
except getopt.error, msg:
usage(1, msg)
parser = OptionParser(usage="""\
Unpack a MIME message into a directory of files.
dir = os.curdir
for opt, arg in opts:
if opt in ('-h', '--help'):
usage(0)
elif opt in ('-d', '--directory'):
dir = arg
Usage: %prog [options] msgfile
""")
parser.add_option('-d', '--directory',
type='string', action='store',
help="""Unpack the MIME message into the named
directory, which will be created if it doesn't already
exist.""")
opts, args = parser.parse_args()
if not opts.directory:
parser.print_help()
sys.exit(1)
try:
msgfile = args[0]
except IndexError:
usage(1)
parser.print_help()
sys.exit(1)
try:
os.mkdir(dir)
os.mkdir(opts.directory)
except OSError, e:
# Ignore directory exists error
if e.errno <> errno.EEXIST: raise
if e.errno <> errno.EEXIST:
raise
fp = open(msgfile)
msg = email.message_from_file(fp)
@ -74,8 +59,8 @@ def main():
ext = '.bin'
filename = 'part-%03d%s' % (counter, ext)
counter += 1
fp = open(os.path.join(dir, filename), 'wb')
fp.write(part.get_payload(decode=1))
fp = open(os.path.join(opts.directory, filename), 'wb')
fp.write(part.get_payload(decode=True))
fp.close()

View File

@ -1,4 +1,4 @@
% Copyright (C) 2001-2004 Python Software Foundation
% Copyright (C) 2001-2006 Python Software Foundation
% Author: barry@python.org (Barry Warsaw)
\section{\module{email} ---
@ -18,10 +18,10 @@ subsumes most of the functionality in several older standard modules
such as \refmodule{rfc822}, \refmodule{mimetools},
\refmodule{multifile}, and other non-standard packages such as
\module{mimecntl}. It is specifically \emph{not} designed to do any
sending of email messages to SMTP (\rfc{2821}) servers; that is the
function of the \refmodule{smtplib} module. The \module{email}
package attempts to be as RFC-compliant as possible, supporting in
addition to \rfc{2822}, such MIME-related RFCs as
sending of email messages to SMTP (\rfc{2821}), NNTP, or other servers; those
are functions of modules such as \refmodule{smtplib} and \refmodule{nntplib}.
The \module{email} package attempts to be as RFC-compliant as possible,
supporting in addition to \rfc{2822}, such MIME-related RFCs as
\rfc{2045}, \rfc{2046}, \rfc{2047}, and \rfc{2231}.
The primary distinguishing feature of the \module{email} package is
@ -41,7 +41,7 @@ The following sections describe the functionality of the
should be common in applications: an email message is read as flat
text from a file or other source, the text is parsed to produce the
object structure of the email message, this structure is manipulated,
and finally rendered back into flat text.
and finally, the object tree is rendered back into flat text.
It is perfectly feasible to create the object structure out of whole
cloth --- i.e. completely from scratch. From there, a similar
@ -56,6 +56,7 @@ package, a section on differences and porting is provided.
\begin{seealso}
\seemodule{smtplib}{SMTP protocol client}
\seemodule{nntplib}{NNTP protocol client}
\end{seealso}
\subsection{Representing an email message}
@ -88,22 +89,51 @@ package, a section on differences and porting is provided.
\subsection{Iterators}
\input{emailiter}
\subsection{Package History}
\subsection{Package History\label{email-pkg-history}}
Version 1 of the \module{email} package was bundled with Python
releases up to Python 2.2.1. Version 2 was developed for the Python
2.3 release, and backported to Python 2.2.2. It was also available as
a separate distutils-based package, and is compatible back to Python 2.1.
This table describes the release history of the email package, corresponding
to the version of Python that the package was released with. For purposes of
this document, when you see a note about change or added versions, these refer
to the Python version the change was made it, \emph{not} the email package
version. This table also describes the Python compatibility of each version
of the package.
\module{email} version 3.0 was released with Python 2.4 and as a separate
distutils-based package. It is compatible back to Python 2.3.
\begin{tableiii}{l|l|l}{constant}{email version}{distributed with}{compatible with}
\lineiii{1.x}{Python 2.2.0 to Python 2.2.1}{\emph{no longer supported}}
\lineiii{2.5}{Python 2.2.2+ and Python 2.3}{Python 2.1 to 2.5}
\lineiii{3.0}{Python 2.4}{Python 2.3 to 2.5}
\lineiii{4.0}{Python 2.5}{Python 2.3 to 2.5}
\end{tableiii}
Here are the differences between \module{email} version 3 and version 2:
Here are the major differences between \module{email} verson 4 and version 3:
\begin{itemize}
\item All modules have been renamed according to \pep{8} standards. For
example, the version 3 module \module{email.Message} was renamed to
\module{email.message} in version 4.
\item A new subpackage \module{email.mime} was added and all the version 3
\module{email.MIME*} modules were renamed and situated into the
\module{email.mime} subpackage. For example, the version 3 module
\module{email.MIMEText} was renamed to \module{email.mime.text}.
\emph{Note that the version 3 names will continue to work until Python
2.6}.
\item The \module{email.mime.application} module was added, which contains the
\class{MIMEApplication} class.
\item Methods that were deprecated in version 3 have been removed. These
include \method{Generator.__call__()}, \method{Message.get_type()},
\method{Message.get_main_type()}, \method{Message.get_subtype()}.
\end{itemize}
Here are the major differences between \module{email} version 3 and version 2:
\begin{itemize}
\item The \class{FeedParser} class was introduced, and the \class{Parser}
class was implemented in terms of the \class{FeedParser}. All parsing
there for is non-strict, and parsing will make a best effort never to
therefore is non-strict, and parsing will make a best effort never to
raise an exception. Problems found while parsing messages are stored in
the message's \var{defect} attribute.
@ -117,7 +147,7 @@ Here are the differences between \module{email} version 3 and version 2:
\method{Generator.__call__()}, \method{Message.get_type()},
\method{Message.get_main_type()}, \method{Message.get_subtype()}, and
the \var{strict} argument to the \class{Parser} class. These are
expected to be removed in email 3.1.
expected to be removed in future versions.
\item Support for Pythons earlier than 2.3 has been removed.
\end{itemize}
@ -278,12 +308,12 @@ The \class{Message} class has the following differences:
\item The method \method{getpayloadastext()} was removed. Similar
functionality
is supported by the \class{DecodedGenerator} class in the
\refmodule{email.Generator} module.
\refmodule{email.generator} module.
\item The method \method{getbodyastext()} was removed. You can get
similar functionality by creating an iterator with
\function{typed_subpart_iterator()} in the
\refmodule{email.Iterators} module.
\refmodule{email.iterators} module.
\end{itemize}
The \class{Parser} class has no differences in its public interface.
@ -295,7 +325,7 @@ notification\footnote{Delivery Status Notifications (DSN) are defined
in \rfc{1894}.}.
The \class{Generator} class has no differences in its public
interface. There is a new class in the \refmodule{email.Generator}
interface. There is a new class in the \refmodule{email.generator}
module though, called \class{DecodedGenerator} which provides most of
the functionality previously available in the
\method{Message.getpayloadastext()} method.
@ -329,11 +359,11 @@ The following modules and classes have been changed:
\module{mimelib} provided some utility functions in its
\module{address} and \module{date} modules. All of these functions
have been moved to the \refmodule{email.Utils} module.
have been moved to the \refmodule{email.utils} module.
The \code{MsgReader} class/module has been removed. Its functionality
is most closely supported in the \function{body_line_iterator()}
function in the \refmodule{email.Iterators} module.
function in the \refmodule{email.iterators} module.
\subsection{Examples}

View File

@ -1,4 +1,4 @@
\declaremodule{standard}{email.Charset}
\declaremodule{standard}{email.charset}
\modulesynopsis{Character Sets}
This module provides a class \class{Charset} for representing
@ -7,6 +7,8 @@ well as a character set registry and several convenience methods for
manipulating this registry. Instances of \class{Charset} are used in
several other modules within the \module{email} package.
Import this class from the \module{email.charset} module.
\versionadded{2.2.2}
\begin{classdesc}{Charset}{\optional{input_charset}}
@ -153,7 +155,7 @@ input charset to the output charset automatically. This is not useful
for multibyte character sets, which have line length issues (multibyte
characters must be split on a character, not a byte boundary); use the
higher-level \class{Header} class to deal with these issues (see
\refmodule{email.Header}). \var{convert} defaults to \code{False}.
\refmodule{email.header}). \var{convert} defaults to \code{False}.
The type of encoding (base64 or quoted-printable) will be based on
the \var{header_encoding} attribute.
@ -188,7 +190,7 @@ This method allows you to compare two \class{Charset} instances for equality.
This method allows you to compare two \class{Charset} instances for inequality.
\end{methoddesc}
The \module{email.Charset} module also provides the following
The \module{email.charset} module also provides the following
functions for adding new entries to the global character set, alias,
and codec registries:

View File

@ -1,4 +1,4 @@
\declaremodule{standard}{email.Encoders}
\declaremodule{standard}{email.encoders}
\modulesynopsis{Encoders for email message payloads.}
When creating \class{Message} objects from scratch, you often need to
@ -7,7 +7,7 @@ This is especially true for \mimetype{image/*} and \mimetype{text/*}
type messages containing binary data.
The \module{email} package provides some convenient encodings in its
\module{Encoders} module. These encoders are actually used by the
\module{encoders} module. These encoders are actually used by the
\class{MIMEAudio} and \class{MIMEImage} class constructors to provide default
encodings. All encoder functions take exactly one argument, the message
object to encode. They usually extract the payload, encode it, and reset the

View File

@ -1,8 +1,8 @@
\declaremodule{standard}{email.Errors}
\declaremodule{standard}{email.errors}
\modulesynopsis{The exception classes used by the email package.}
The following exception classes are defined in the
\module{email.Errors} module:
\module{email.errors} module:
\begin{excclassdesc}{MessageError}{}
This is the base class for all exceptions that the \module{email}
@ -59,7 +59,7 @@ problem was found, so for example, if a message nested inside a
\mimetype{multipart/alternative} had a malformed header, that nested message
object would have a defect, but the containing messages would not.
All defect classes are subclassed from \class{email.Errors.MessageDefect}, but
All defect classes are subclassed from \class{email.errors.MessageDefect}, but
this class is \emph{not} an exception!
\versionadded[All the defect classes were added]{2.4}

View File

@ -1,4 +1,4 @@
\declaremodule{standard}{email.Generator}
\declaremodule{standard}{email.generator}
\modulesynopsis{Generate flat text email messages from a message structure.}
One of the most common tasks is to generate the flat text of the email
@ -8,7 +8,7 @@ module or the \refmodule{nntplib} module, or print the message on the
console. Taking a message object structure and producing a flat text
document is the job of the \class{Generator} class.
Again, as with the \refmodule{email.Parser} module, you aren't limited
Again, as with the \refmodule{email.parser} module, you aren't limited
to the functionality of the bundled generator; you could write one
from scratch yourself. However the bundled generator knows how to
generate most email in a standards-compliant way, should handle MIME
@ -17,7 +17,8 @@ transformation from flat text, to a message structure via the
\class{Parser} class, and back to flat text, is idempotent (the input
is identical to the output).
Here are the public methods of the \class{Generator} class:
Here are the public methods of the \class{Generator} class, imported from the
\module{email.generator} module:
\begin{classdesc}{Generator}{outfp\optional{, mangle_from_\optional{,
maxheaderlen}}}
@ -40,7 +41,7 @@ mailbox format files.
Optional \var{maxheaderlen} specifies the longest length for a
non-continued header. When a header line is longer than
\var{maxheaderlen} (in characters, with tabs expanded to 8 spaces),
the header will be split as defined in the \module{email.Header}
the header will be split as defined in the \module{email.header.Header}
class. Set to zero to disable header wrapping. The default is 78, as
recommended (but not required) by \rfc{2822}.
\end{classdesc}
@ -81,9 +82,9 @@ be used in extended print statements.
As a convenience, see the methods \method{Message.as_string()} and
\code{str(aMessage)}, a.k.a. \method{Message.__str__()}, which
simplify the generation of a formatted string representation of a
message object. For more detail, see \refmodule{email.Message}.
message object. For more detail, see \refmodule{email.message}.
The \module{email.Generator} module also provides a derived class,
The \module{email.generator} module also provides a derived class,
called \class{DecodedGenerator} which is like the \class{Generator}
base class, except that non-\mimetype{text} parts are substituted with
a format string representing the part.
@ -128,13 +129,5 @@ The default value for \var{fmt} is \code{None}, meaning
\versionadded{2.2.2}
\end{classdesc}
\subsubsection{Deprecated methods}
The following methods are deprecated in \module{email} version 2.
They are documented here for completeness.
\begin{methoddesc}[Generator]{__call__}{msg\optional{, unixfrom}}
This method is identical to the \method{flatten()} method.
\deprecated{2.2.2}{Use the \method{flatten()} method instead.}
\end{methoddesc}
\versionchanged[The previously deprecated method \method{__call__()} was
removed]{2.5}

View File

@ -1,4 +1,4 @@
\declaremodule{standard}{email.Header}
\declaremodule{standard}{email.header}
\modulesynopsis{Representing non-ASCII headers}
\rfc{2822} is the base standard that describes the format of email
@ -15,17 +15,18 @@ slew of RFCs have been written describing how to encode email
containing non-\ASCII{} characters into \rfc{2822}-compliant format.
These RFCs include \rfc{2045}, \rfc{2046}, \rfc{2047}, and \rfc{2231}.
The \module{email} package supports these standards in its
\module{email.Header} and \module{email.Charset} modules.
\module{email.header} and \module{email.charset} modules.
If you want to include non-\ASCII{} characters in your email headers,
say in the \mailheader{Subject} or \mailheader{To} fields, you should
use the \class{Header} class and assign the field in the
\class{Message} object to an instance of \class{Header} instead of
using a string for the header value. For example:
using a string for the header value. Import the \class{Header} class from the
\module{email.header} module. For example:
\begin{verbatim}
>>> from email.Message import Message
>>> from email.Header import Header
>>> from email.message import Message
>>> from email.header import Header
>>> msg = Message()
>>> h = Header('p\xf6stal', 'iso-8859-1')
>>> msg['Subject'] = h
@ -87,7 +88,7 @@ Optional \var{errors} is passed straight through to the
Append the string \var{s} to the MIME header.
Optional \var{charset}, if given, should be a \class{Charset} instance
(see \refmodule{email.Charset}) or the name of a character set, which
(see \refmodule{email.charset}) or the name of a character set, which
will be converted to a \class{Charset} instance. A value of
\code{None} (the default) means that the \var{charset} given in the
constructor is used.
@ -139,7 +140,7 @@ This method allows you to compare two \class{Header} instances for equality.
This method allows you to compare two \class{Header} instances for inequality.
\end{methoddesc}
The \module{email.Header} module also provides the following
The \module{email.header} module also provides the following
convenient functions.
\begin{funcdesc}{decode_header}{header}
@ -155,7 +156,7 @@ encoded string.
Here's an example:
\begin{verbatim}
>>> from email.Header import decode_header
>>> from email.header import decode_header
>>> decode_header('=?iso-8859-1?q?p=F6stal?=')
[('p\xf6stal', 'iso-8859-1')]
\end{verbatim}

View File

@ -1,8 +1,8 @@
\declaremodule{standard}{email.Iterators}
\declaremodule{standard}{email.iterators}
\modulesynopsis{Iterate over a message object tree.}
Iterating over a message object tree is fairly easy with the
\method{Message.walk()} method. The \module{email.Iterators} module
\method{Message.walk()} method. The \module{email.iterators} module
provides some useful higher level iterations over message object
trees.

View File

@ -1,10 +1,11 @@
\declaremodule{standard}{email.Message}
\declaremodule{standard}{email.message}
\modulesynopsis{The base class representing email messages.}
The central class in the \module{email} package is the
\class{Message} class; it is the base class for the \module{email}
object model. \class{Message} provides the core functionality for
setting and querying header fields, and for accessing message bodies.
\class{Message} class, imported from the \module{email.message} module. It is
the base class for the \module{email} object model. \class{Message} provides
the core functionality for setting and querying header fields, and for
accessing message bodies.
Conceptually, a \class{Message} object consists of \emph{headers} and
\emph{payloads}. Headers are \rfc{2822} style field names and
@ -45,7 +46,7 @@ begin with \code{From }. For more flexibility, instantiate a
\begin{verbatim}
from cStringIO import StringIO
from email.Generator import Generator
from email.generator import Generator
fp = StringIO()
g = Generator(fp, mangle_from_=False, maxheaderlen=60)
g.flatten(msg)
@ -119,7 +120,7 @@ client's responsibility to ensure the payload invariants. Optional
\begin{methoddesc}[Message]{set_charset}{charset}
Set the character set of the payload to \var{charset}, which can
either be a \class{Charset} instance (see \refmodule{email.Charset}), a
either be a \class{Charset} instance (see \refmodule{email.charset}), a
string naming a character set,
or \code{None}. If it is a string, it will be converted to a
\class{Charset} instance. If \var{charset} is \code{None}, the
@ -128,8 +129,8 @@ or \code{None}. If it is a string, it will be converted to a
\exception{TypeError}.
The message will be assumed to be of type \mimetype{text/*} encoded with
\code{charset.input_charset}. It will be converted to
\code{charset.output_charset}
\var{charset.input_charset}. It will be converted to
\var{charset.output_charset}
and encoded properly, if needed, when generating the plain text
representation of the message. MIME headers
(\mailheader{MIME-Version}, \mailheader{Content-Type},
@ -513,6 +514,9 @@ message/rfc822
\end{verbatim}
\end{methoddesc}
\versionchanged[The previously deprecated methods \method{get_type()},
\method{get_main_type()}, and \method{get_subtype()} were removed]{2.5}
\class{Message} objects can also optionally contain two instance
attributes, which can be used when generating the plain text of a MIME
message.
@ -532,7 +536,7 @@ to the message's \var{preamble} attribute. When the \class{Generator}
is writing out the plain text representation of a MIME message, and it
finds the message has a \var{preamble} attribute, it will write this
text in the area between the headers and the first boundary. See
\refmodule{email.Parser} and \refmodule{email.Generator} for details.
\refmodule{email.parser} and \refmodule{email.generator} for details.
Note that if the message object has no preamble, the
\var{preamble} attribute will be \code{None}.
@ -543,58 +547,15 @@ The \var{epilogue} attribute acts the same way as the \var{preamble}
attribute, except that it contains text that appears between the last
boundary and the end of the message.
One note: when generating the flat text for a \mimetype{multipart}
message that has no \var{epilogue} (using the standard
\class{Generator} class), no newline is added after the closing
boundary line. If the message object has an \var{epilogue} and its
value does not start with a newline, a newline is printed after the
closing boundary. This seems a little clumsy, but it makes the most
practical sense. The upshot is that if you want to ensure that a
newline get printed after your closing \mimetype{multipart} boundary,
set the \var{epilogue} to the empty string.
\versionchanged[You do not need to set the epilogue to the empty string in
order for the \class{Generator} to print a newline at the end of the
file]{2.5}
\end{datadesc}
\begin{datadesc}{defects}
The \var{defects} attribute contains a list of all the problems found when
parsing this message. See \refmodule{email.Errors} for a detailed description
parsing this message. See \refmodule{email.errors} for a detailed description
of the possible parsing defects.
\versionadded{2.4}
\end{datadesc}
\subsubsection{Deprecated methods}
\versionchanged[The \method{add_payload()} method was removed; use the
\method{attach()} method instead]{2.4}
The following methods are deprecated. They are documented here for
completeness.
\begin{methoddesc}[Message]{get_type}{\optional{failobj}}
Return the message's content type, as a string of the form
\mimetype{maintype/subtype} as taken from the
\mailheader{Content-Type} header.
The returned string is coerced to lowercase.
If there is no \mailheader{Content-Type} header in the message,
\var{failobj} is returned (defaults to \code{None}).
\deprecated{2.2.2}{Use the \method{get_content_type()} method instead.}
\end{methoddesc}
\begin{methoddesc}[Message]{get_main_type}{\optional{failobj}}
Return the message's \emph{main} content type. This essentially returns the
\var{maintype} part of the string returned by \method{get_type()}, with the
same semantics for \var{failobj}.
\deprecated{2.2.2}{Use the \method{get_content_maintype()} method instead.}
\end{methoddesc}
\begin{methoddesc}[Message]{get_subtype}{\optional{failobj}}
Return the message's sub-content type. This essentially returns the
\var{subtype} part of the string returned by \method{get_type()}, with the
same semantics for \var{failobj}.
\deprecated{2.2.2}{Use the \method{get_content_subtype()} method instead.}
\end{methoddesc}

View File

@ -1,3 +1,11 @@
\declaremodule{standard}{email.mime}
\declaremodule{standard}{email.mime.base}
\declaremodule{standard}{email.mime.nonmultipart}
\declaremodule{standard}{email.mime.multipart}
\declaremodule{standard}{email.mime.audio}
\declaremodule{standard}{email.mime.image}
\declaremodule{standard}{email.mime.message}
\declaremodule{standard}{email.mime.text}
Ordinarily, you get a message object structure by passing a file or
some text to a parser, which parses the text and returns the root
message object. However you can also build a complete message
@ -6,26 +14,16 @@ hand. In fact, you can also take an existing structure and add new
\class{Message} objects, move them around, etc. This makes a very
convenient interface for slicing-and-dicing MIME messages.
You can create a new object structure by creating \class{Message}
instances, adding attachments and all the appropriate headers manually.
For MIME messages though, the \module{email} package provides some
convenient subclasses to make things easier. Each of these classes
should be imported from a module with the same name as the class, from
within the \module{email} package. E.g.:
\begin{verbatim}
import email.MIMEImage.MIMEImage
\end{verbatim}
or
\begin{verbatim}
from email.MIMEText import MIMEText
\end{verbatim}
You can create a new object structure by creating \class{Message} instances,
adding attachments and all the appropriate headers manually. For MIME
messages though, the \module{email} package provides some convenient
subclasses to make things easier.
Here are the classes:
\begin{classdesc}{MIMEBase}{_maintype, _subtype, **_params}
Module: \module{email.mime.base}
This is the base class for all the MIME-specific subclasses of
\class{Message}. Ordinarily you won't create instances specifically
of \class{MIMEBase}, although you could. \class{MIMEBase} is provided
@ -45,6 +43,8 @@ The \class{MIMEBase} class always adds a \mailheader{Content-Type} header
\end{classdesc}
\begin{classdesc}{MIMENonMultipart}{}
Module: \module{email.mime.nonmultipart}
A subclass of \class{MIMEBase}, this is an intermediate base class for
MIME messages that are not \mimetype{multipart}. The primary purpose
of this class is to prevent the use of the \method{attach()} method,
@ -57,6 +57,7 @@ exception is raised.
\begin{classdesc}{MIMEMultipart}{\optional{subtype\optional{,
boundary\optional{, _subparts\optional{, _params}}}}}
Module: \module{email.mime.multipart}
A subclass of \class{MIMEBase}, this is an intermediate base class for
MIME messages that are \mimetype{multipart}. Optional \var{_subtype}
@ -80,8 +81,31 @@ argument, which is a keyword dictionary.
\versionadded{2.2.2}
\end{classdesc}
\begin{classdesc}{MIMEApplication}{_data\optional{, _subtype\optional{,
_encoder\optional{, **_params}}}}
Module: \module{email.mime.application}
A subclass of \class{MIMENonMultipart}, the \class{MIMEApplication} class is
used to represent MIME message objects of major type \mimetype{application}.
\var{_data} is a string containing the raw byte data. Optional \var{_subtype}
specifies the MIME subtype and defaults to \mimetype{octet-stream}.
Optional \var{_encoder} is a callable (i.e. function) which will
perform the actual encoding of the data for transport. This
callable takes one argument, which is the \class{MIMEApplication} instance.
It should use \method{get_payload()} and \method{set_payload()} to
change the payload to encoded form. It should also add any
\mailheader{Content-Transfer-Encoding} or other headers to the message
object as necessary. The default encoding is base64. See the
\refmodule{email.encoders} module for a list of the built-in encoders.
\var{_params} are passed straight through to the base class constructor.
\versionadded{2.5}
\end{classdesc}
\begin{classdesc}{MIMEAudio}{_audiodata\optional{, _subtype\optional{,
_encoder\optional{, **_params}}}}
Module: \module{email.mime.audio}
A subclass of \class{MIMENonMultipart}, the \class{MIMEAudio} class
is used to create MIME message objects of major type \mimetype{audio}.
@ -100,13 +124,14 @@ It should use \method{get_payload()} and \method{set_payload()} to
change the payload to encoded form. It should also add any
\mailheader{Content-Transfer-Encoding} or other headers to the message
object as necessary. The default encoding is base64. See the
\refmodule{email.Encoders} module for a list of the built-in encoders.
\refmodule{email.encoders} module for a list of the built-in encoders.
\var{_params} are passed straight through to the base class constructor.
\end{classdesc}
\begin{classdesc}{MIMEImage}{_imagedata\optional{, _subtype\optional{,
_encoder\optional{, **_params}}}}
Module: \module{email.mime.image}
A subclass of \class{MIMENonMultipart}, the \class{MIMEImage} class is
used to create MIME message objects of major type \mimetype{image}.
@ -125,13 +150,15 @@ It should use \method{get_payload()} and \method{set_payload()} to
change the payload to encoded form. It should also add any
\mailheader{Content-Transfer-Encoding} or other headers to the message
object as necessary. The default encoding is base64. See the
\refmodule{email.Encoders} module for a list of the built-in encoders.
\refmodule{email.encoders} module for a list of the built-in encoders.
\var{_params} are passed straight through to the \class{MIMEBase}
constructor.
\end{classdesc}
\begin{classdesc}{MIMEMessage}{_msg\optional{, _subtype}}
Module: \module{email.mime.message}
A subclass of \class{MIMENonMultipart}, the \class{MIMEMessage} class
is used to create MIME objects of main type \mimetype{message}.
\var{_msg} is used as the payload, and must be an instance of class
@ -143,6 +170,8 @@ to \mimetype{rfc822}.
\end{classdesc}
\begin{classdesc}{MIMEText}{_text\optional{, _subtype\optional{, _charset}}}
Module: \module{email.mime.text}
A subclass of \class{MIMENonMultipart}, the \class{MIMEText} class is
used to create MIME objects of major type \mimetype{text}.
\var{_text} is the string for the payload. \var{_subtype} is the

View File

@ -1,4 +1,4 @@
\declaremodule{standard}{email.Parser}
\declaremodule{standard}{email.parser}
\modulesynopsis{Parse flat text email messages to produce a message
object structure.}
@ -41,9 +41,10 @@ message object trees any way it finds necessary.
\versionadded{2.4}
The \class{FeedParser} provides an API that is conducive to incremental
parsing of email messages, such as would be necessary when reading the text of
an email message from a source that can block (e.g. a socket). The
The \class{FeedParser}, imported from the \module{email.feedparser} module,
provides an API that is conducive to incremental parsing of email messages,
such as would be necessary when reading the text of an email message from a
source that can block (e.g. a socket). The
\class{FeedParser} can of course be used to parse an email message fully
contained in a string or a file, but the classic \class{Parser} API may be
more convenient for such use cases. The semantics and results of the two
@ -56,14 +57,14 @@ accurate when parsing standards-compliant messages, and it does a very good
job of parsing non-compliant messages, providing information about how a
message was deemed broken. It will populate a message object's \var{defects}
attribute with a list of any problems it found in a message. See the
\refmodule{email.Errors} module for the list of defects that it can find.
\refmodule{email.errors} module for the list of defects that it can find.
Here is the API for the \class{FeedParser}:
\begin{classdesc}{FeedParser}{\optional{_factory}}
Create a \class{FeedParser} instance. Optional \var{_factory} is a
no-argument callable that will be called whenever a new message object is
needed. It defaults to the \class{email.Message.Message} class.
needed. It defaults to the \class{email.message.Message} class.
\end{classdesc}
\begin{methoddesc}[FeedParser]{feed}{data}
@ -82,21 +83,22 @@ more data to a closed \class{FeedParser}.
\subsubsection{Parser class API}
The \class{Parser} provides an API that can be used to parse a message when
the complete contents of the message are available in a string or file. The
\module{email.Parser} module also provides a second class, called
The \class{Parser} class, imported from the \module{email.parser} module,
provides an API that can be used to parse a message when the complete contents
of the message are available in a string or file. The
\module{email.parser} module also provides a second class, called
\class{HeaderParser} which can be used if you're only interested in
the headers of the message. \class{HeaderParser} can be much faster in
these situations, since it does not attempt to parse the message body,
instead setting the payload to the raw body as a string.
\class{HeaderParser} has the same API as the \class{Parser} class.
\begin{classdesc}{Parser}{\optional{_class\optional{, strict}}}
\begin{classdesc}{Parser}{\optional{_class}}
The constructor for the \class{Parser} class takes an optional
argument \var{_class}. This must be a callable factory (such as a
function or a class), and it is used whenever a sub-message object
needs to be created. It defaults to \class{Message} (see
\refmodule{email.Message}). The factory will be called without
\refmodule{email.message}). The factory will be called without
arguments.
The optional \var{strict} flag is ignored. \deprecated{2.4}{Because the
@ -201,6 +203,6 @@ Here are some notes on the parsing semantics:
\method{is_multipart()} method may return \code{False}. If such
messages were parsed with the \class{FeedParser}, they will have an
instance of the \class{MultipartInvariantViolationDefect} class in their
\var{defects} attribute list. See \refmodule{email.Errors} for
\var{defects} attribute list. See \refmodule{email.errors} for
details.
\end{itemize}

View File

@ -1,7 +1,7 @@
\declaremodule{standard}{email.Utils}
\declaremodule{standard}{email.utils}
\modulesynopsis{Miscellaneous email package utilities.}
There are several useful utilities provided in the \module{email.Utils}
There are several useful utilities provided in the \module{email.utils}
module:
\begin{funcdesc}{quote}{str}
@ -38,7 +38,7 @@ values as might be returned by \method{Message.get_all()}. Here's a
simple example that gets all the recipients of a message:
\begin{verbatim}
from email.Utils import getaddresses
from email.utils import getaddresses
tos = msg.get_all('to', [])
ccs = msg.get_all('cc', [])

View File

@ -12,9 +12,9 @@
\authoraddress{\email{barry@python.org}}
\date{\today}
\release{3.0} % software release, not documentation
\release{4.0} % software release, not documentation
\setreleaseinfo{} % empty for final release
\setshortversion{3.0} % major.minor only for software
\setshortversion{4.0} % major.minor only for software
\begin{document}
@ -38,11 +38,11 @@ The \module{email} package provides classes and utilities to create,
parse, generate, and modify email messages, conforming to all the
relevant email and MIME related RFCs.
This document describes version 3.0 of the \module{email} package, which is
distributed with Python 2.4 and is available as a standalone distutils-based
package for use with Python 2.3. \module{email} 3.0 is not compatible with
Python versions earlier than 2.3. For more information about the
\module{email} package, including download links and mailing lists, see
This document describes version 4.0 of the \module{email} package, which is
distributed with Python 2.5 and is available as a standalone distutils-based
package for use with earlier Python versions. \module{email} 4.0 is not
compatible with Python versions earlier than 2.3. For more information about
the \module{email} package, including download links and mailing lists, see
\ulink{Python's email SIG}{http://www.python.org/sigs/email-sig}.
The documentation that follows was written for the Python project, so
@ -51,7 +51,8 @@ package documentation, there are a few notes to be aware of:
\begin{itemize}
\item Deprecation and ``version added'' notes are relative to the
Python version a feature was added or deprecated.
Python version a feature was added or deprecated. See
the package history in section \ref{email-pkg-history} for details.
\item If you're reading this documentation as part of the
standalone \module{email} package, some of the internal links to

View File

@ -4,9 +4,10 @@
"""A package for parsing, handling, and generating email messages."""
__version__ = '3.0.1'
__version__ = '4.0a2'
__all__ = [
# Old names
'base64MIME',
'Charset',
'Encoders',
@ -27,6 +28,19 @@ __all__ = [
'Utils',
'message_from_string',
'message_from_file',
# new names
'base64mime',
'charset',
'encoders',
'errors',
'generator',
'header',
'iterators',
'message',
'mime',
'parser',
'quoprimime',
'utils',
]
@ -39,7 +53,7 @@ def message_from_string(s, *args, **kws):
Optional _class and strict are passed to the Parser constructor.
"""
from email.Parser import Parser
from email.parser import Parser
return Parser(*args, **kws).parsestr(s)
@ -48,5 +62,62 @@ def message_from_file(fp, *args, **kws):
Optional _class and strict are passed to the Parser constructor.
"""
from email.Parser import Parser
from email.parser import Parser
return Parser(*args, **kws).parse(fp)
# Lazy loading to provide name mapping from new-style names (PEP 8 compatible
# email 4.0 module names), to old-style names (email 3.0 module names).
import sys
class LazyImporter(object):
def __init__(self, module_name):
self.__name__ = 'email.' + module_name
def __getattr__(self, name):
__import__(self.__name__)
mod = sys.modules[self.__name__]
self.__dict__.update(mod.__dict__)
return getattr(mod, name)
_LOWERNAMES = [
# email.<old name> -> email.<new name is lowercased old name>
'Charset',
'Encoders',
'Errors',
'FeedParser',
'Generator',
'Header',
'Iterators',
'Message',
'Parser',
'Utils',
'base64MIME',
'quopriMIME',
]
_MIMENAMES = [
# email.MIME<old name> -> email.mime.<new name is lowercased old name>
'Audio',
'Base',
'Image',
'Message',
'Multipart',
'NonMultipart',
'Text',
]
for _name in _LOWERNAMES:
importer = LazyImporter(_name.lower())
sys.modules['email.' + _name] = importer
setattr(sys.modules['email'], _name, importer)
import email.mime
for _name in _MIMENAMES:
importer = LazyImporter('mime.' + _name.lower())
sys.modules['email.MIME' + _name] = importer
setattr(sys.modules['email'], 'MIME' + _name, importer)
setattr(sys.modules['email.mime'], _name, importer)

View File

@ -6,6 +6,13 @@
Lifted directly from rfc822.py. This should eventually be rewritten.
"""
__all__ = [
'mktime_tz',
'parsedate',
'parsedate_tz',
'quote',
]
import time
SPACE = ' '

View File

@ -1,4 +1,4 @@
# Copyright (C) 2002-2004 Python Software Foundation
# Copyright (C) 2002-2006 Python Software Foundation
# Author: Ben Gertzfield
# Contact: email-sig@python.org
@ -24,9 +24,21 @@ decoding. To deal with the various line wrapping issues, use the email.Header
module.
"""
__all__ = [
'base64_len',
'body_decode',
'body_encode',
'decode',
'decodestring',
'encode',
'encodestring',
'header_encode',
]
import re
from binascii import b2a_base64, a2b_base64
from email.Utils import fix_eols
from email.utils import fix_eols
CRLF = '\r\n'
NL = '\n'

View File

@ -2,9 +2,18 @@
# Author: Ben Gertzfield, Barry Warsaw
# Contact: email-sig@python.org
import email.base64MIME
import email.quopriMIME
from email.Encoders import encode_7or8bit
__all__ = [
'Charset',
'add_alias',
'add_charset',
'add_codec',
]
import email.base64mime
import email.quoprimime
from email import errors
from email.encoders import encode_7or8bit
@ -186,8 +195,17 @@ class Charset:
"""
def __init__(self, input_charset=DEFAULT_CHARSET):
# RFC 2046, $4.1.2 says charsets are not case sensitive. We coerce to
# unicode because its .lower() is locale insensitive.
input_charset = unicode(input_charset, 'ascii').lower()
# unicode because its .lower() is locale insensitive. If the argument
# is already a unicode, we leave it at that, but ensure that the
# charset is ASCII, as the standard (RFC XXX) requires.
try:
if isinstance(input_charset, unicode):
input_charset.encode('ascii')
else:
input_charset = unicode(input_charset, 'ascii')
except UnicodeError:
raise errors.CharsetError(input_charset)
input_charset = input_charset.lower()
# Set the input charset after filtering through the aliases
self.input_charset = ALIASES.get(input_charset, input_charset)
# We can try to guess which encoding and conversion to use by the
@ -307,12 +325,12 @@ class Charset:
cset = self.get_output_charset()
# The len(s) of a 7bit encoding is len(s)
if self.header_encoding == BASE64:
return email.base64MIME.base64_len(s) + len(cset) + MISC_LEN
return email.base64mime.base64_len(s) + len(cset) + MISC_LEN
elif self.header_encoding == QP:
return email.quopriMIME.header_quopri_len(s) + len(cset) + MISC_LEN
return email.quoprimime.header_quopri_len(s) + len(cset) + MISC_LEN
elif self.header_encoding == SHORTEST:
lenb64 = email.base64MIME.base64_len(s)
lenqp = email.quopriMIME.header_quopri_len(s)
lenb64 = email.base64mime.base64_len(s)
lenqp = email.quoprimime.header_quopri_len(s)
return min(lenb64, lenqp) + len(cset) + MISC_LEN
else:
return len(s)
@ -335,16 +353,16 @@ class Charset:
s = self.convert(s)
# 7bit/8bit encodings return the string unchanged (modulo conversions)
if self.header_encoding == BASE64:
return email.base64MIME.header_encode(s, cset)
return email.base64mime.header_encode(s, cset)
elif self.header_encoding == QP:
return email.quopriMIME.header_encode(s, cset, maxlinelen=None)
return email.quoprimime.header_encode(s, cset, maxlinelen=None)
elif self.header_encoding == SHORTEST:
lenb64 = email.base64MIME.base64_len(s)
lenqp = email.quopriMIME.header_quopri_len(s)
lenb64 = email.base64mime.base64_len(s)
lenqp = email.quoprimime.header_quopri_len(s)
if lenb64 < lenqp:
return email.base64MIME.header_encode(s, cset)
return email.base64mime.header_encode(s, cset)
else:
return email.quopriMIME.header_encode(s, cset, maxlinelen=None)
return email.quoprimime.header_encode(s, cset, maxlinelen=None)
else:
return s
@ -363,8 +381,8 @@ class Charset:
s = self.convert(s)
# 7bit/8bit encodings return the string unchanged (module conversions)
if self.body_encoding is BASE64:
return email.base64MIME.body_encode(s)
return email.base64mime.body_encode(s)
elif self.body_encoding is QP:
return email.quopriMIME.body_encode(s)
return email.quoprimime.body_encode(s)
else:
return s

View File

@ -1,12 +1,22 @@
# Copyright (C) 2001-2004 Python Software Foundation
# Copyright (C) 2001-2006 Python Software Foundation
# Author: Barry Warsaw
# Contact: email-sig@python.org
"""Encodings and related functions."""
__all__ = [
'encode_7or8bit',
'encode_base64',
'encode_noop',
'encode_quopri',
]
import base64
from quopri import encodestring as _encodestring
def _qencode(s):
enc = _encodestring(s, quotetabs=True)
# Must encode spaces, which quopri.encodestring() doesn't do

View File

@ -1,4 +1,4 @@
# Copyright (C) 2001-2004 Python Software Foundation
# Copyright (C) 2001-2006 Python Software Foundation
# Author: Barry Warsaw
# Contact: email-sig@python.org
@ -26,6 +26,10 @@ class MultipartConversionError(MessageError, TypeError):
"""Conversion to a multipart is prohibited."""
class CharsetError(MessageError):
"""An illegal charset was given."""
# These are parsing defects which the parser was able to work around.
class MessageDefect:

View File

@ -19,9 +19,12 @@ the current message. Defects are just instances that live on the message
object's .defects attribute.
"""
__all__ = ['FeedParser']
import re
from email import Errors
from email import Message
from email import errors
from email import message
NLCRE = re.compile('\r\n|\r|\n')
NLCRE_bol = re.compile('(\r\n|\r|\n)')
@ -130,7 +133,7 @@ class BufferedSubFile(object):
class FeedParser:
"""A feed-style parser of email."""
def __init__(self, _factory=Message.Message):
def __init__(self, _factory=message.Message):
"""_factory is called with no arguments to create a new message obj"""
self._factory = _factory
self._input = BufferedSubFile()
@ -164,7 +167,7 @@ class FeedParser:
# Look for final set of defects
if root.get_content_maintype() == 'multipart' \
and not root.is_multipart():
root.defects.append(Errors.MultipartInvariantViolationDefect())
root.defects.append(errors.MultipartInvariantViolationDefect())
return root
def _new_message(self):
@ -277,7 +280,7 @@ class FeedParser:
# defined a boundary. That's a problem which we'll handle by
# reading everything until the EOF and marking the message as
# defective.
self._cur.defects.append(Errors.NoBoundaryInMultipartDefect())
self._cur.defects.append(errors.NoBoundaryInMultipartDefect())
lines = []
for line in self._input:
if line is NeedMoreData:
@ -381,7 +384,7 @@ class FeedParser:
# that as a defect and store the captured text as the payload.
# Everything from here to the EOF is epilogue.
if capturing_preamble:
self._cur.defects.append(Errors.StartBoundaryNotFoundDefect())
self._cur.defects.append(errors.StartBoundaryNotFoundDefect())
self._cur.set_payload(EMPTYSTRING.join(preamble))
epilogue = []
for line in self._input:
@ -432,7 +435,7 @@ class FeedParser:
# The first line of the headers was a continuation. This
# is illegal, so let's note the defect, store the illegal
# line, and ignore it for purposes of headers.
defect = Errors.FirstHeaderLineIsContinuationDefect(line)
defect = errors.FirstHeaderLineIsContinuationDefect(line)
self._cur.defects.append(defect)
continue
lastvalue.append(line)
@ -460,13 +463,13 @@ class FeedParser:
else:
# Weirdly placed unix-from line. Note this as a defect
# and ignore it.
defect = Errors.MisplacedEnvelopeHeaderDefect(line)
defect = errors.MisplacedEnvelopeHeaderDefect(line)
self._cur.defects.append(defect)
continue
# Split the line on the colon separating field name from value.
i = line.find(':')
if i < 0:
defect = Errors.MalformedHeaderDefect(line)
defect = errors.MalformedHeaderDefect(line)
self._cur.defects.append(defect)
continue
lastheader = line[:i]

View File

@ -4,14 +4,16 @@
"""Classes to generate plain text from a message object tree."""
__all__ = ['Generator', 'DecodedGenerator']
import re
import sys
import time
import random
import warnings
from cStringIO import StringIO
from email.Header import Header
from cStringIO import StringIO
from email.header import Header
UNDERSCORE = '_'
NL = '\n'
@ -81,12 +83,6 @@ class Generator:
print >> self._fp, ufrom
self._write(msg)
# For backwards compatibility, but this is slower
def __call__(self, msg, unixfrom=False):
warnings.warn('__call__() deprecated; use flatten()',
DeprecationWarning, 2)
self.flatten(msg, unixfrom)
def clone(self, fp):
"""Clone this generator with the exact same options."""
return self.__class__(fp, self._mangle_from_, self._maxheaderlen)

View File

@ -1,16 +1,23 @@
# Copyright (C) 2002-2004 Python Software Foundation
# Copyright (C) 2002-2006 Python Software Foundation
# Author: Ben Gertzfield, Barry Warsaw
# Contact: email-sig@python.org
"""Header encoding and decoding functionality."""
__all__ = [
'Header',
'decode_header',
'make_header',
]
import re
import binascii
import email.quopriMIME
import email.base64MIME
from email.Errors import HeaderParseError
from email.Charset import Charset
import email.quoprimime
import email.base64mime
from email.errors import HeaderParseError
from email.charset import Charset
NL = '\n'
SPACE = ' '
@ -42,7 +49,7 @@ fcre = re.compile(r'[\041-\176]+:$')
# Helpers
_max_append = email.quopriMIME._max_append
_max_append = email.quoprimime._max_append
@ -82,10 +89,10 @@ def decode_header(header):
encoded = parts[2]
dec = None
if encoding == 'q':
dec = email.quopriMIME.header_decode(encoded)
dec = email.quoprimime.header_decode(encoded)
elif encoding == 'b':
try:
dec = email.base64MIME.decode(encoded)
dec = email.base64mime.decode(encoded)
except binascii.Error:
# Turn this into a higher level exception. BAW: Right
# now we throw the lower level exception away but

View File

@ -1,9 +1,16 @@
# Copyright (C) 2001-2004 Python Software Foundation
# Copyright (C) 2001-2006 Python Software Foundation
# Author: Barry Warsaw
# Contact: email-sig@python.org
"""Various types of useful iterators and generators."""
__all__ = [
'body_line_iterator',
'typed_subpart_iterator',
'walk',
# Do not include _structure() since it's part of the debugging API.
]
import sys
from cStringIO import StringIO
@ -25,7 +32,6 @@ def walk(self):
# These two functions are imported into the Iterators.py interface module.
# The Python 2.2 version uses generators for efficiency.
def body_line_iterator(msg, decode=False):
"""Iterate over the parts, returning string payloads line-by-line.

View File

@ -4,6 +4,8 @@
"""Basic message object for the email package object model."""
__all__ = ['Message']
import re
import uu
import binascii
@ -11,9 +13,9 @@ import warnings
from cStringIO import StringIO
# Intrapackage imports
from email import Utils
from email import Errors
from email import Charset
import email.charset
from email import utils
from email import errors
SEMISPACE = '; '
@ -41,11 +43,11 @@ def _formatparam(param, value=None, quote=True):
if isinstance(value, tuple):
# Encode as per RFC 2231
param += '*'
value = Utils.encode_rfc2231(value[2], value[0], value[1])
value = utils.encode_rfc2231(value[2], value[0], value[1])
# BAW: Please check this. I think that if quote is set it should
# force quoting even if not necessary.
if quote or tspecials.search(value):
return '%s="%s"' % (param, Utils.quote(value))
return '%s="%s"' % (param, utils.quote(value))
else:
return '%s=%s' % (param, value)
else:
@ -70,14 +72,14 @@ def _parseparam(s):
def _unquotevalue(value):
# This is different than Utils.collapse_rfc2231_value() because it doesn't
# This is different than utils.collapse_rfc2231_value() because it doesn't
# try to convert the value to a unicode. Message.get_param() and
# Message.get_params() are both currently defined to return the tuple in
# the face of RFC 2231 parameters.
if isinstance(value, tuple):
return value[0], value[1], Utils.unquote(value[2])
return value[0], value[1], utils.unquote(value[2])
else:
return Utils.unquote(value)
return utils.unquote(value)
@ -188,17 +190,17 @@ class Message:
return None
cte = self.get('content-transfer-encoding', '').lower()
if cte == 'quoted-printable':
return Utils._qdecode(payload)
return utils._qdecode(payload)
elif cte == 'base64':
try:
return Utils._bdecode(payload)
return utils._bdecode(payload)
except binascii.Error:
# Incorrect padding
return payload
elif cte in ('x-uuencode', 'uuencode', 'uue', 'x-uue'):
sfp = StringIO()
try:
uu.decode(StringIO(payload+'\n'), sfp)
uu.decode(StringIO(payload+'\n'), sfp, quiet=True)
payload = sfp.getvalue()
except uu.Error:
# Some decoding problem
@ -237,8 +239,8 @@ class Message:
self._charset = None
return
if isinstance(charset, str):
charset = Charset.Charset(charset)
if not isinstance(charset, Charset.Charset):
charset = email.charset.Charset(charset)
if not isinstance(charset, email.charset.Charset):
raise TypeError(charset)
# BAW: should we accept strings that can serve as arguments to the
# Charset constructor?
@ -412,49 +414,6 @@ class Message:
else:
raise KeyError(_name)
#
# Deprecated methods. These will be removed in email 3.1.
#
def get_type(self, failobj=None):
"""Returns the message's content type.
The returned string is coerced to lowercase and returned as a single
string of the form `maintype/subtype'. If there was no Content-Type
header in the message, failobj is returned (defaults to None).
"""
warnings.warn('get_type() deprecated; use get_content_type()',
DeprecationWarning, 2)
missing = object()
value = self.get('content-type', missing)
if value is missing:
return failobj
return paramre.split(value)[0].lower().strip()
def get_main_type(self, failobj=None):
"""Return the message's main content type if present."""
warnings.warn('get_main_type() deprecated; use get_content_maintype()',
DeprecationWarning, 2)
missing = object()
ctype = self.get_type(missing)
if ctype is missing:
return failobj
if ctype.count('/') <> 1:
return failobj
return ctype.split('/')[0]
def get_subtype(self, failobj=None):
"""Return the message's content subtype if present."""
warnings.warn('get_subtype() deprecated; use get_content_subtype()',
DeprecationWarning, 2)
missing = object()
ctype = self.get_type(missing)
if ctype is missing:
return failobj
if ctype.count('/') <> 1:
return failobj
return ctype.split('/')[1]
#
# Use these three methods instead of the three above.
#
@ -537,7 +496,7 @@ class Message:
name = p.strip()
val = ''
params.append((name, val))
params = Utils.decode_params(params)
params = utils.decode_params(params)
return params
def get_params(self, failobj=None, header='content-type', unquote=True):
@ -714,7 +673,7 @@ class Message:
filename = self.get_param('name', missing, 'content-disposition')
if filename is missing:
return failobj
return Utils.collapse_rfc2231_value(filename).strip()
return utils.collapse_rfc2231_value(filename).strip()
def get_boundary(self, failobj=None):
"""Return the boundary associated with the payload if present.
@ -727,7 +686,7 @@ class Message:
if boundary is missing:
return failobj
# RFC 2046 says that boundaries may begin but not end in w/s
return Utils.collapse_rfc2231_value(boundary).rstrip()
return utils.collapse_rfc2231_value(boundary).rstrip()
def set_boundary(self, boundary):
"""Set the boundary parameter in Content-Type to 'boundary'.
@ -744,7 +703,7 @@ class Message:
if params is missing:
# There was no Content-Type header, and we don't know what type
# to set it to, so raise an exception.
raise Errors.HeaderParseError, 'No Content-Type header found'
raise errors.HeaderParseError('No Content-Type header found')
newparams = []
foundp = False
for pk, pv in params:

View File

View File

@ -0,0 +1,36 @@
# Copyright (C) 2001-2006 Python Software Foundation
# Author: Keith Dart
# Contact: email-sig@python.org
"""Class representing application/* type MIME documents."""
__all__ = ["MIMEApplication"]
from email import encoders
from email.mime.nonmultipart import MIMENonMultipart
class MIMEApplication(MIMENonMultipart):
"""Class for generating application/* MIME documents."""
def __init__(self, _data, _subtype='octet-stream',
_encoder=encoders.encode_base64, **_params):
"""Create an application/* type MIME document.
_data is a string containing the raw applicatoin data.
_subtype is the MIME content type subtype, defaulting to
'octet-stream'.
_encoder is a function which will perform the actual encoding for
transport of the application data, defaulting to base64 encoding.
Any additional keyword arguments are passed to the base class
constructor, which turns them into parameters on the Content-Type
header.
"""
if _subtype is None:
raise TypeError('Invalid application MIME subtype')
MIMENonMultipart.__init__(self, 'application', _subtype, **_params)
self.set_payload(_data)
_encoder(self)

View File

@ -1,15 +1,16 @@
# Copyright (C) 2001-2004 Python Software Foundation
# Copyright (C) 2001-2006 Python Software Foundation
# Author: Anthony Baxter
# Contact: email-sig@python.org
"""Class representing audio/* type MIME documents."""
import sndhdr
from cStringIO import StringIO
__all__ = ['MIMEAudio']
from email import Errors
from email import Encoders
from email.MIMENonMultipart import MIMENonMultipart
import sndhdr
from cStringIO import StringIO
from email import encoders
from email.mime.nonmultipart import MIMENonMultipart
@ -42,7 +43,7 @@ class MIMEAudio(MIMENonMultipart):
"""Class for generating audio/* MIME documents."""
def __init__(self, _audiodata, _subtype=None,
_encoder=Encoders.encode_base64, **_params):
_encoder=encoders.encode_base64, **_params):
"""Create an audio/* type MIME document.
_audiodata is a string containing the raw audio data. If this data

View File

@ -1,14 +1,16 @@
# Copyright (C) 2001-2004 Python Software Foundation
# Copyright (C) 2001-2006 Python Software Foundation
# Author: Barry Warsaw
# Contact: email-sig@python.org
"""Base class for MIME specializations."""
from email import Message
__all__ = ['MIMEBase']
from email import message
class MIMEBase(Message.Message):
class MIMEBase(message.Message):
"""Base class for MIME specializations."""
def __init__(self, _maintype, _subtype, **_params):
@ -18,7 +20,7 @@ class MIMEBase(Message.Message):
arguments. Additional parameters for this header are taken from the
keyword arguments.
"""
Message.Message.__init__(self)
message.Message.__init__(self)
ctype = '%s/%s' % (_maintype, _subtype)
self.add_header('Content-Type', ctype, **_params)
self['MIME-Version'] = '1.0'

View File

@ -1,14 +1,15 @@
# Copyright (C) 2001-2004 Python Software Foundation
# Copyright (C) 2001-2006 Python Software Foundation
# Author: Barry Warsaw
# Contact: email-sig@python.org
"""Class representing image/* type MIME documents."""
__all__ = ['MIMEImage']
import imghdr
from email import Errors
from email import Encoders
from email.MIMENonMultipart import MIMENonMultipart
from email import encoders
from email.mime.nonmultipart import MIMENonMultipart
@ -16,7 +17,7 @@ class MIMEImage(MIMENonMultipart):
"""Class for generating image/* type MIME documents."""
def __init__(self, _imagedata, _subtype=None,
_encoder=Encoders.encode_base64, **_params):
_encoder=encoders.encode_base64, **_params):
"""Create an image/* type MIME document.
_imagedata is a string containing the raw image data. If this data

View File

@ -1,11 +1,13 @@
# Copyright (C) 2001-2004 Python Software Foundation
# Copyright (C) 2001-2006 Python Software Foundation
# Author: Barry Warsaw
# Contact: email-sig@python.org
"""Class representing message/* MIME documents."""
from email import Message
from email.MIMENonMultipart import MIMENonMultipart
__all__ = ['MIMEMessage']
from email import message
from email.mime.nonmultipart import MIMENonMultipart
@ -23,10 +25,10 @@ class MIMEMessage(MIMENonMultipart):
the term "rfc822" is technically outdated by RFC 2822).
"""
MIMENonMultipart.__init__(self, 'message', _subtype)
if not isinstance(_msg, Message.Message):
if not isinstance(_msg, message.Message):
raise TypeError('Argument is not an instance of Message')
# It's convenient to use this base class method. We need to do it
# this way or we'll get an exception
Message.Message.attach(self, _msg)
message.Message.attach(self, _msg)
# And be sure our default type is set correctly
self.set_default_type('message/rfc822')

View File

@ -1,14 +1,16 @@
# Copyright (C) 2002-2004 Python Software Foundation
# Copyright (C) 2002-2006 Python Software Foundation
# Author: Barry Warsaw
# Contact: email-sig@python.org
"""Base class for MIME multipart/* type messages."""
from email import MIMEBase
__all__ = ['MIMEMultipart']
from email.mime.base import MIMEBase
class MIMEMultipart(MIMEBase.MIMEBase):
class MIMEMultipart(MIMEBase):
"""Base class for MIME multipart/* type messages."""
def __init__(self, _subtype='mixed', boundary=None, _subparts=None,
@ -31,7 +33,7 @@ class MIMEMultipart(MIMEBase.MIMEBase):
Additional parameters for the Content-Type header are taken from the
keyword arguments (or passed into the _params argument).
"""
MIMEBase.MIMEBase.__init__(self, 'multipart', _subtype, **_params)
MIMEBase.__init__(self, 'multipart', _subtype, **_params)
if _subparts:
for p in _subparts:
self.attach(p)

View File

@ -1,15 +1,17 @@
# Copyright (C) 2002-2004 Python Software Foundation
# Copyright (C) 2002-2006 Python Software Foundation
# Author: Barry Warsaw
# Contact: email-sig@python.org
"""Base class for MIME type messages that are not multipart."""
from email import Errors
from email import MIMEBase
__all__ = ['MIMENonMultipart']
from email import errors
from email.mime.base import MIMEBase
class MIMENonMultipart(MIMEBase.MIMEBase):
class MIMENonMultipart(MIMEBase):
"""Base class for MIME multipart/* type messages."""
__pychecker__ = 'unusednames=payload'
@ -18,7 +20,7 @@ class MIMENonMultipart(MIMEBase.MIMEBase):
# The public API prohibits attaching multiple subparts to MIMEBase
# derived subtypes since none of them are, by definition, of content
# type multipart/*
raise Errors.MultipartConversionError(
raise errors.MultipartConversionError(
'Cannot attach additional subparts to non-multipart/*')
del __pychecker__

View File

@ -1,11 +1,13 @@
# Copyright (C) 2001-2004 Python Software Foundation
# Copyright (C) 2001-2006 Python Software Foundation
# Author: Barry Warsaw
# Contact: email-sig@python.org
"""Class representing text/* type MIME documents."""
from email.MIMENonMultipart import MIMENonMultipart
from email.Encoders import encode_7or8bit
__all__ = ['MIMEText']
from email.encoders import encode_7or8bit
from email.mime.nonmultipart import MIMENonMultipart

View File

@ -1,13 +1,16 @@
# Copyright (C) 2001-2004 Python Software Foundation
# Copyright (C) 2001-2006 Python Software Foundation
# Author: Barry Warsaw, Thomas Wouters, Anthony Baxter
# Contact: email-sig@python.org
"""A parser of RFC 2822 and MIME email messages."""
__all__ = ['Parser', 'HeaderParser']
import warnings
from cStringIO import StringIO
from email.FeedParser import FeedParser
from email.Message import Message
from email.feedparser import FeedParser
from email.message import Message

View File

@ -1,4 +1,4 @@
# Copyright (C) 2001-2004 Python Software Foundation
# Copyright (C) 2001-2006 Python Software Foundation
# Author: Ben Gertzfield
# Contact: email-sig@python.org
@ -26,9 +26,27 @@ does dumb encoding and decoding. To deal with the various line
wrapping issues, use the email.Header module.
"""
__all__ = [
'body_decode',
'body_encode',
'body_quopri_check',
'body_quopri_len',
'decode',
'decodestring',
'encode',
'encodestring',
'header_decode',
'header_encode',
'header_quopri_check',
'header_quopri_len',
'quote',
'unquote',
]
import re
from string import hexdigits
from email.Utils import fix_eols
from email.utils import fix_eols
CRLF = '\r\n'
NL = '\n'

View File

@ -39,9 +39,6 @@ NL = '\n'
EMPTYSTRING = ''
SPACE = ' '
# We don't care about DeprecationWarnings
warnings.filterwarnings('ignore', '', DeprecationWarning, __name__)
def openfile(filename, mode='r'):
@ -87,7 +84,7 @@ class TestMessageAPI(TestEmailBase):
charset = Charset('iso-8859-1')
msg.set_charset(charset)
eq(msg['mime-version'], '1.0')
eq(msg.get_type(), 'text/plain')
eq(msg.get_content_type(), 'text/plain')
eq(msg['content-type'], 'text/plain; charset="iso-8859-1"')
eq(msg.get_param('charset'), 'iso-8859-1')
eq(msg['content-transfer-encoding'], 'quoted-printable')
@ -211,6 +208,19 @@ class TestMessageAPI(TestEmailBase):
msg.set_payload('foo')
eq(msg.get_payload(decode=True), 'foo')
def test_decode_bogus_uu_payload_quietly(self):
msg = Message()
msg.set_payload('begin 664 foo.txt\n%<W1F=0000H \n \nend\n')
msg['Content-Transfer-Encoding'] = 'x-uuencode'
old_stderr = sys.stderr
try:
sys.stderr = sfp = StringIO()
# We don't care about the payload
msg.get_payload(decode=True)
finally:
sys.stderr = old_stderr
self.assertEqual(sfp.getvalue(), '')
def test_decoded_generator(self):
eq = self.assertEqual
msg = self._msgobj('msg_07.txt')
@ -893,7 +903,7 @@ class TestMIMEAudio(unittest.TestCase):
self._au = MIMEAudio(self._audiodata)
def test_guess_minor_type(self):
self.assertEqual(self._au.get_type(), 'audio/basic')
self.assertEqual(self._au.get_content_type(), 'audio/basic')
def test_encoding(self):
payload = self._au.get_payload()
@ -901,7 +911,7 @@ class TestMIMEAudio(unittest.TestCase):
def test_checkSetMinor(self):
au = MIMEAudio(self._audiodata, 'fish')
self.assertEqual(au.get_type(), 'audio/fish')
self.assertEqual(au.get_content_type(), 'audio/fish')
def test_add_header(self):
eq = self.assertEqual
@ -936,7 +946,7 @@ class TestMIMEImage(unittest.TestCase):
self._im = MIMEImage(self._imgdata)
def test_guess_minor_type(self):
self.assertEqual(self._im.get_type(), 'image/gif')
self.assertEqual(self._im.get_content_type(), 'image/gif')
def test_encoding(self):
payload = self._im.get_payload()
@ -944,7 +954,7 @@ class TestMIMEImage(unittest.TestCase):
def test_checkSetMinor(self):
im = MIMEImage(self._imgdata, 'fish')
self.assertEqual(im.get_type(), 'image/fish')
self.assertEqual(im.get_content_type(), 'image/fish')
def test_add_header(self):
eq = self.assertEqual
@ -976,7 +986,7 @@ class TestMIMEText(unittest.TestCase):
def test_types(self):
eq = self.assertEqual
unless = self.failUnless
eq(self._msg.get_type(), 'text/plain')
eq(self._msg.get_content_type(), 'text/plain')
eq(self._msg.get_param('charset'), 'us-ascii')
missing = []
unless(self._msg.get_param('foobar', missing) is missing)
@ -1045,7 +1055,7 @@ This is the dingus fish.
# tests
m = self._msg
unless(m.is_multipart())
eq(m.get_type(), 'multipart/mixed')
eq(m.get_content_type(), 'multipart/mixed')
eq(len(m.get_payload()), 2)
raises(IndexError, m.get_payload, 2)
m0 = m.get_payload(0)
@ -1379,7 +1389,7 @@ class TestNonConformant(TestEmailBase):
def test_parse_missing_minor_type(self):
eq = self.assertEqual
msg = self._msgobj('msg_14.txt')
eq(msg.get_type(), 'text')
eq(msg.get_content_type(), 'text/plain')
eq(msg.get_content_maintype(), 'text')
eq(msg.get_content_subtype(), 'plain')
@ -1531,7 +1541,7 @@ class TestMIMEMessage(TestEmailBase):
m = Message()
m['Subject'] = subject
r = MIMEMessage(m)
eq(r.get_type(), 'message/rfc822')
eq(r.get_content_type(), 'message/rfc822')
payload = r.get_payload()
unless(isinstance(payload, list))
eq(len(payload), 1)
@ -1572,7 +1582,7 @@ Here is the body of the message.
eq = self.assertEqual
unless = self.failUnless
msg = self._msgobj('msg_11.txt')
eq(msg.get_type(), 'message/rfc822')
eq(msg.get_content_type(), 'message/rfc822')
payload = msg.get_payload()
unless(isinstance(payload, list))
eq(len(payload), 1)
@ -1586,12 +1596,12 @@ Here is the body of the message.
unless = self.failUnless
# msg 16 is a Delivery Status Notification, see RFC 1894
msg = self._msgobj('msg_16.txt')
eq(msg.get_type(), 'multipart/report')
eq(msg.get_content_type(), 'multipart/report')
unless(msg.is_multipart())
eq(len(msg.get_payload()), 3)
# Subpart 1 is a text/plain, human readable section
subpart = msg.get_payload(0)
eq(subpart.get_type(), 'text/plain')
eq(subpart.get_content_type(), 'text/plain')
eq(subpart.get_payload(), """\
This report relates to a message you sent with the following header fields:
@ -1611,7 +1621,7 @@ Your message cannot be delivered to the following recipients:
# consists of two blocks of headers, represented by two nested Message
# objects.
subpart = msg.get_payload(1)
eq(subpart.get_type(), 'message/delivery-status')
eq(subpart.get_content_type(), 'message/delivery-status')
eq(len(subpart.get_payload()), 2)
# message/delivery-status should treat each block as a bunch of
# headers, i.e. a bunch of Message objects.
@ -1629,13 +1639,13 @@ Your message cannot be delivered to the following recipients:
eq(dsn2.get_param('rfc822', header='final-recipient'), '')
# Subpart 3 is the original message
subpart = msg.get_payload(2)
eq(subpart.get_type(), 'message/rfc822')
eq(subpart.get_content_type(), 'message/rfc822')
payload = subpart.get_payload()
unless(isinstance(payload, list))
eq(len(payload), 1)
subsubpart = payload[0]
unless(isinstance(subsubpart, Message))
eq(subsubpart.get_type(), 'text/plain')
eq(subsubpart.get_content_type(), 'text/plain')
eq(subsubpart['message-id'],
'<002001c144a6$8752e060$56104586@oxy.edu>')
@ -1706,16 +1716,16 @@ Two
fp.close()
container1 = msg.get_payload(0)
eq(container1.get_default_type(), 'message/rfc822')
eq(container1.get_type(), None)
eq(container1.get_content_type(), 'message/rfc822')
container2 = msg.get_payload(1)
eq(container2.get_default_type(), 'message/rfc822')
eq(container2.get_type(), None)
eq(container2.get_content_type(), 'message/rfc822')
container1a = container1.get_payload(0)
eq(container1a.get_default_type(), 'text/plain')
eq(container1a.get_type(), 'text/plain')
eq(container1a.get_content_type(), 'text/plain')
container2a = container2.get_payload(0)
eq(container2a.get_default_type(), 'text/plain')
eq(container2a.get_type(), 'text/plain')
eq(container2a.get_content_type(), 'text/plain')
def test_default_type_with_explicit_container_type(self):
eq = self.assertEqual
@ -1726,16 +1736,16 @@ Two
fp.close()
container1 = msg.get_payload(0)
eq(container1.get_default_type(), 'message/rfc822')
eq(container1.get_type(), 'message/rfc822')
eq(container1.get_content_type(), 'message/rfc822')
container2 = msg.get_payload(1)
eq(container2.get_default_type(), 'message/rfc822')
eq(container2.get_type(), 'message/rfc822')
eq(container2.get_content_type(), 'message/rfc822')
container1a = container1.get_payload(0)
eq(container1a.get_default_type(), 'text/plain')
eq(container1a.get_type(), 'text/plain')
eq(container1a.get_content_type(), 'text/plain')
container2a = container2.get_payload(0)
eq(container2a.get_default_type(), 'text/plain')
eq(container2a.get_type(), 'text/plain')
eq(container2a.get_content_type(), 'text/plain')
def test_default_type_non_parsed(self):
eq = self.assertEqual
@ -1750,9 +1760,9 @@ Two
subpart2 = MIMEMessage(subpart2a)
container.attach(subpart1)
container.attach(subpart2)
eq(subpart1.get_type(), 'message/rfc822')
eq(subpart1.get_content_type(), 'message/rfc822')
eq(subpart1.get_default_type(), 'message/rfc822')
eq(subpart2.get_type(), 'message/rfc822')
eq(subpart2.get_content_type(), 'message/rfc822')
eq(subpart2.get_default_type(), 'message/rfc822')
neq(container.as_string(0), '''\
Content-Type: multipart/digest; boundary="BOUNDARY"
@ -1784,9 +1794,9 @@ message 2
del subpart1['mime-version']
del subpart2['content-type']
del subpart2['mime-version']
eq(subpart1.get_type(), None)
eq(subpart1.get_content_type(), 'message/rfc822')
eq(subpart1.get_default_type(), 'message/rfc822')
eq(subpart2.get_type(), None)
eq(subpart2.get_content_type(), 'message/rfc822')
eq(subpart2.get_default_type(), 'message/rfc822')
neq(container.as_string(0), '''\
Content-Type: multipart/digest; boundary="BOUNDARY"
@ -1847,7 +1857,7 @@ class TestIdempotent(TestEmailBase):
def test_parse_text_message(self):
eq = self.assertEquals
msg, text = self._msgobj('msg_01.txt')
eq(msg.get_type(), 'text/plain')
eq(msg.get_content_type(), 'text/plain')
eq(msg.get_content_maintype(), 'text')
eq(msg.get_content_subtype(), 'plain')
eq(msg.get_params()[1], ('charset', 'us-ascii'))
@ -1859,7 +1869,7 @@ class TestIdempotent(TestEmailBase):
def test_parse_untyped_message(self):
eq = self.assertEquals
msg, text = self._msgobj('msg_03.txt')
eq(msg.get_type(), None)
eq(msg.get_content_type(), 'text/plain')
eq(msg.get_params(), None)
eq(msg.get_param('charset'), None)
self._idempotent(msg, text)
@ -1933,7 +1943,7 @@ class TestIdempotent(TestEmailBase):
unless = self.failUnless
# Get a message object and reset the seek pointer for other tests
msg, text = self._msgobj('msg_05.txt')
eq(msg.get_type(), 'multipart/report')
eq(msg.get_content_type(), 'multipart/report')
# Test the Content-Type: parameters
params = {}
for pk, pv in msg.get_params():
@ -1945,13 +1955,13 @@ class TestIdempotent(TestEmailBase):
eq(len(msg.get_payload()), 3)
# Make sure the subparts are what we expect
msg1 = msg.get_payload(0)
eq(msg1.get_type(), 'text/plain')
eq(msg1.get_content_type(), 'text/plain')
eq(msg1.get_payload(), 'Yadda yadda yadda\n')
msg2 = msg.get_payload(1)
eq(msg2.get_type(), None)
eq(msg2.get_content_type(), 'text/plain')
eq(msg2.get_payload(), 'Yadda yadda yadda\n')
msg3 = msg.get_payload(2)
eq(msg3.get_type(), 'message/rfc822')
eq(msg3.get_content_type(), 'message/rfc822')
self.failUnless(isinstance(msg3, Message))
payload = msg3.get_payload()
unless(isinstance(payload, list))
@ -1965,7 +1975,7 @@ class TestIdempotent(TestEmailBase):
unless = self.failUnless
msg, text = self._msgobj('msg_06.txt')
# Check some of the outer headers
eq(msg.get_type(), 'message/rfc822')
eq(msg.get_content_type(), 'message/rfc822')
# Make sure the payload is a list of exactly one sub-Message, and that
# that submessage has a type of text/plain
payload = msg.get_payload()
@ -1973,7 +1983,7 @@ class TestIdempotent(TestEmailBase):
eq(len(payload), 1)
msg1 = payload[0]
self.failUnless(isinstance(msg1, Message))
eq(msg1.get_type(), 'text/plain')
eq(msg1.get_content_type(), 'text/plain')
self.failUnless(isinstance(msg1.get_payload(), str))
eq(msg1.get_payload(), '\n')
@ -2058,13 +2068,19 @@ class TestMiscellaneous(TestEmailBase):
module = __import__('email')
all = module.__all__
all.sort()
self.assertEqual(all, ['Charset', 'Encoders', 'Errors', 'Generator',
self.assertEqual(all, [
# Old names
'Charset', 'Encoders', 'Errors', 'Generator',
'Header', 'Iterators', 'MIMEAudio', 'MIMEBase',
'MIMEImage', 'MIMEMessage', 'MIMEMultipart',
'MIMENonMultipart', 'MIMEText', 'Message',
'Parser', 'Utils', 'base64MIME',
'message_from_file', 'message_from_string',
'quopriMIME'])
# new names
'base64mime', 'charset', 'encoders', 'errors', 'generator',
'header', 'iterators', 'message', 'message_from_file',
'message_from_string', 'mime', 'parser',
'quopriMIME', 'quoprimime', 'utils',
])
def test_formatdate(self):
now = time.time()
@ -2356,7 +2372,7 @@ class TestParsers(TestEmailBase):
fp.close()
eq(msg['from'], 'ppp-request@zzz.org')
eq(msg['to'], 'ppp@zzz.org')
eq(msg.get_type(), 'multipart/mixed')
eq(msg.get_content_type(), 'multipart/mixed')
self.failIf(msg.is_multipart())
self.failUnless(isinstance(msg.get_payload(), str))
@ -2405,10 +2421,10 @@ Here's the message body
fp.close()
eq(len(msg.get_payload()), 2)
part1 = msg.get_payload(0)
eq(part1.get_type(), 'text/plain')
eq(part1.get_content_type(), 'text/plain')
eq(part1.get_payload(), 'Simple email with attachment.\r\n\r\n')
part2 = msg.get_payload(1)
eq(part2.get_type(), 'application/riscos')
eq(part2.get_content_type(), 'application/riscos')
def test_multipart_digest_with_extra_mime_headers(self):
eq = self.assertEqual
@ -2427,21 +2443,21 @@ Here's the message body
eq(msg.is_multipart(), 1)
eq(len(msg.get_payload()), 2)
part1 = msg.get_payload(0)
eq(part1.get_type(), 'message/rfc822')
eq(part1.get_content_type(), 'message/rfc822')
eq(part1.is_multipart(), 1)
eq(len(part1.get_payload()), 1)
part1a = part1.get_payload(0)
eq(part1a.is_multipart(), 0)
eq(part1a.get_type(), 'text/plain')
eq(part1a.get_content_type(), 'text/plain')
neq(part1a.get_payload(), 'message 1\n')
# next message/rfc822
part2 = msg.get_payload(1)
eq(part2.get_type(), 'message/rfc822')
eq(part2.get_content_type(), 'message/rfc822')
eq(part2.is_multipart(), 1)
eq(len(part2.get_payload()), 1)
part2a = part2.get_payload(0)
eq(part2a.is_multipart(), 0)
eq(part2a.get_type(), 'text/plain')
eq(part2a.get_content_type(), 'text/plain')
neq(part2a.get_payload(), 'message 2\n')
def test_three_lines(self):
@ -2723,6 +2739,11 @@ class TestCharset(unittest.TestCase):
c = Charset('fake')
eq('hello w\xf6rld', c.body_encode('hello w\xf6rld'))
def test_unicode_charset_name(self):
charset = Charset(u'us-ascii')
self.assertEqual(str(charset), 'us-ascii')
self.assertRaises(Errors.CharsetError, Charset, 'asc\xffii')
# Test multilingual MIME headers.

View File

@ -10,6 +10,13 @@ from email.Charset import Charset
from email.Header import Header, decode_header
from email.Message import Message
# We're compatible with Python 2.3, but it doesn't have the built-in Asian
# codecs, so we have to skip all these tests.
try:
unicode('foo', 'euc-jp')
except LookupError:
raise TestSkipped
class TestEmailAsianCodecs(TestEmailBase):

View File

@ -0,0 +1,77 @@
# Copyright (C) 2002-2006 Python Software Foundation
# Contact: email-sig@python.org
# email package unit tests for (optional) Asian codecs
import unittest
from test.test_support import TestSkipped, run_unittest
from email.test.test_email import TestEmailBase
from email.charset import Charset
from email.header import Header, decode_header
from email.message import Message
# We're compatible with Python 2.3, but it doesn't have the built-in Asian
# codecs, so we have to skip all these tests.
try:
unicode('foo', 'euc-jp')
except LookupError:
raise TestSkipped
class TestEmailAsianCodecs(TestEmailBase):
def test_japanese_codecs(self):
eq = self.ndiffAssertEqual
j = Charset("euc-jp")
g = Charset("iso-8859-1")
h = Header("Hello World!")
jhello = '\xa5\xcf\xa5\xed\xa1\xbc\xa5\xef\xa1\xbc\xa5\xeb\xa5\xc9\xa1\xaa'
ghello = 'Gr\xfc\xdf Gott!'
h.append(jhello, j)
h.append(ghello, g)
# BAW: This used to -- and maybe should -- fold the two iso-8859-1
# chunks into a single encoded word. However it doesn't violate the
# standard to have them as two encoded chunks and maybe it's
# reasonable <wink> for each .append() call to result in a separate
# encoded word.
eq(h.encode(), """\
Hello World! =?iso-2022-jp?b?GyRCJU8lbSE8JW8hPCVrJUkhKhsoQg==?=
=?iso-8859-1?q?Gr=FC=DF?= =?iso-8859-1?q?_Gott!?=""")
eq(decode_header(h.encode()),
[('Hello World!', None),
('\x1b$B%O%m!<%o!<%k%I!*\x1b(B', 'iso-2022-jp'),
('Gr\xfc\xdf Gott!', 'iso-8859-1')])
long = 'test-ja \xa4\xd8\xc5\xea\xb9\xc6\xa4\xb5\xa4\xec\xa4\xbf\xa5\xe1\xa1\xbc\xa5\xeb\xa4\xcf\xbb\xca\xb2\xf1\xbc\xd4\xa4\xce\xbe\xb5\xc7\xa7\xa4\xf2\xc2\xd4\xa4\xc3\xa4\xc6\xa4\xa4\xa4\xde\xa4\xb9'
h = Header(long, j, header_name="Subject")
# test a very long header
enc = h.encode()
# TK: splitting point may differ by codec design and/or Header encoding
eq(enc , """\
=?iso-2022-jp?b?dGVzdC1qYSAbJEIkWEVqOUYkNSRsJD8lYSE8JWskTztKGyhC?=
=?iso-2022-jp?b?GyRCMnE8VCROPjVHJyRyQlQkQyRGJCQkXiQ5GyhC?=""")
# TK: full decode comparison
eq(h.__unicode__().encode('euc-jp'), long)
def test_payload_encoding(self):
jhello = '\xa5\xcf\xa5\xed\xa1\xbc\xa5\xef\xa1\xbc\xa5\xeb\xa5\xc9\xa1\xaa'
jcode = 'euc-jp'
msg = Message()
msg.set_payload(jhello, jcode)
ustr = unicode(msg.get_payload(), msg.get_content_charset())
self.assertEqual(jhello, ustr.encode(jcode))
def suite():
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(TestEmailAsianCodecs))
return suite
def test_main():
run_unittest(TestEmailAsianCodecs)
if __name__ == '__main__':
unittest.main(defaultTest='suite')

File diff suppressed because it is too large Load Diff

View File

@ -1,9 +1,24 @@
# Copyright (C) 2001-2004 Python Software Foundation
# Copyright (C) 2001-2006 Python Software Foundation
# Author: Barry Warsaw
# Contact: email-sig@python.org
"""Miscellaneous utilities."""
__all__ = [
'collapse_rfc2231_value',
'decode_params',
'decode_rfc2231',
'encode_rfc2231',
'formataddr',
'formatdate',
'getaddresses',
'make_msgid',
'parseaddr',
'parsedate',
'parsedate_tz',
'unquote',
]
import os
import re
import time
@ -24,7 +39,7 @@ from email._parseaddr import parsedate_tz as _parsedate_tz
from quopri import decodestring as _qdecode
# Intrapackage imports
from email.Encoders import _bencode, _qencode
from email.encoders import _bencode, _qencode
COMMASPACE = ', '
EMPTYSTRING = ''

View File

@ -170,7 +170,7 @@ class PyclbrTest(TestCase):
cm('pydoc')
# Tests for modules inside packages
cm('email.Parser')
cm('email.parser')
cm('test.test_pyclbr')