Commit 40ef0067 authored by Barry Warsaw's avatar Barry Warsaw

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

and NEWS updates.
parent 9ae019bf
#!/usr/bin/env python #!/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 os
import getopt import sys
import smtplib import smtplib
# For guessing MIME type based on file name extension # For guessing MIME type based on file name extension
import mimetypes import mimetypes
from email import Encoders from optparse import OptionParser
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
COMMASPACE = ', '
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
def usage(code, msg=''): COMMASPACE = ', '
print >> sys.stderr, __doc__
if msg:
print >> sys.stderr, msg
sys.exit(code)
def main(): def main():
try: parser = OptionParser(usage="""\
opts, args = getopt.getopt(sys.argv[1:], 'hd:', ['help', 'directory=']) Send the contents of a directory as a MIME message.
except getopt.error, msg:
usage(1, msg) Usage: %prog [options]
dir = os.curdir Unless the -o option is given, the email is sent by forwarding to your local
for opt, arg in opts: SMTP server, which then does the normal delivery process. Your local machine
if opt in ('-h', '--help'): must be running an SMTP server.
usage(0) """)
elif opt in ('-d', '--directory'): parser.add_option('-d', '--directory',
dir = arg type='string', action='store',
help="""Mail the contents of the specified directory,
if len(args) < 2: otherwise use the current directory. Only the regular
usage(1) files in the directory are sent, and we don't recurse to
subdirectories.""")
sender = args[0] parser.add_option('-o', '--output',
recips = args[1:] 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 # Create the enclosing (outer) message
outer = MIMEMultipart() outer = MIMEMultipart()
outer['Subject'] = 'Contents of directory %s' % os.path.abspath(dir) outer['Subject'] = 'Contents of directory %s' % os.path.abspath(directory)
outer['To'] = COMMASPACE.join(recips) outer['To'] = COMMASPACE.join(opts.recipients)
outer['From'] = sender outer['From'] = opts.sender
outer.preamble = 'You will not see this in a MIME-aware mail reader.\n' 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): for filename in os.listdir(directory):
path = os.path.join(dir, filename) path = os.path.join(directory, filename)
if not os.path.isfile(path): if not os.path.isfile(path):
continue continue
# Guess the content type based on the file's extension. Encoding # Guess the content type based on the file's extension. Encoding
...@@ -108,16 +94,21 @@ def main(): ...@@ -108,16 +94,21 @@ def main():
msg.set_payload(fp.read()) msg.set_payload(fp.read())
fp.close() fp.close()
# Encode the payload using Base64 # Encode the payload using Base64
Encoders.encode_base64(msg) encoders.encode_base64(msg)
# Set the filename parameter # Set the filename parameter
msg.add_header('Content-Disposition', 'attachment', filename=filename) msg.add_header('Content-Disposition', 'attachment', filename=filename)
outer.attach(msg) outer.attach(msg)
# Now send or store the message
# Now send the message composed = outer.as_string()
s = smtplib.SMTP() if opts.output:
s.connect() fp = open(opts.output, 'w')
s.sendmail(sender, recips, outer.as_string()) fp.write(composed)
s.close() fp.close()
else:
s = smtplib.SMTP()
s.connect()
s.sendmail(opts.sender, opts.recipients, composed)
s.close()
if __name__ == '__main__': if __name__ == '__main__':
......
...@@ -2,8 +2,8 @@ ...@@ -2,8 +2,8 @@
import smtplib import smtplib
# Here are the email package modules we'll need # Here are the email package modules we'll need
from email.MIMEImage import MIMEImage from email.mime.image import MIMEImage
from email.MIMEMultipart import MIMEMultipart from email.mime.multipart import MIMEMultipart
COMMASPACE = ', ' COMMASPACE = ', '
...@@ -15,8 +15,6 @@ msg['Subject'] = 'Our family reunion' ...@@ -15,8 +15,6 @@ msg['Subject'] = 'Our family reunion'
msg['From'] = me msg['From'] = me
msg['To'] = COMMASPACE.join(family) msg['To'] = COMMASPACE.join(family)
msg.preamble = 'Our family reunion' 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 # Assume we know that the image files are all in PNG format
for file in pngfiles: for file in pngfiles:
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
import smtplib import smtplib
# Import the email modules we'll need # 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 # Open a plain text file for reading. For this example, assume that
# the text file contains only ASCII characters. # the text file contains only ASCII characters.
......
#!/usr/bin/env python #!/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 os
import getopt import sys
import email
import errno import errno
import mimetypes import mimetypes
import email
def usage(code, msg=''): from optparse import OptionParser
print >> sys.stderr, __doc__
if msg:
print >> sys.stderr, msg
sys.exit(code)
def main(): def main():
try: parser = OptionParser(usage="""\
opts, args = getopt.getopt(sys.argv[1:], 'hd:', ['help', 'directory=']) Unpack a MIME message into a directory of files.
except getopt.error, msg:
usage(1, msg) Usage: %prog [options] msgfile
""")
dir = os.curdir parser.add_option('-d', '--directory',
for opt, arg in opts: type='string', action='store',
if opt in ('-h', '--help'): help="""Unpack the MIME message into the named
usage(0) directory, which will be created if it doesn't already
elif opt in ('-d', '--directory'): exist.""")
dir = arg opts, args = parser.parse_args()
if not opts.directory:
parser.print_help()
sys.exit(1)
try: try:
msgfile = args[0] msgfile = args[0]
except IndexError: except IndexError:
usage(1) parser.print_help()
sys.exit(1)
try: try:
os.mkdir(dir) os.mkdir(opts.directory)
except OSError, e: except OSError, e:
# Ignore directory exists error # Ignore directory exists error
if e.errno <> errno.EEXIST: raise if e.errno <> errno.EEXIST:
raise
fp = open(msgfile) fp = open(msgfile)
msg = email.message_from_file(fp) msg = email.message_from_file(fp)
...@@ -74,8 +59,8 @@ def main(): ...@@ -74,8 +59,8 @@ def main():
ext = '.bin' ext = '.bin'
filename = 'part-%03d%s' % (counter, ext) filename = 'part-%03d%s' % (counter, ext)
counter += 1 counter += 1
fp = open(os.path.join(dir, filename), 'wb') fp = open(os.path.join(opts.directory, filename), 'wb')
fp.write(part.get_payload(decode=1)) fp.write(part.get_payload(decode=True))
fp.close() fp.close()
......
% Copyright (C) 2001-2004 Python Software Foundation % Copyright (C) 2001-2006 Python Software Foundation
% Author: barry@python.org (Barry Warsaw) % Author: barry@python.org (Barry Warsaw)
\section{\module{email} --- \section{\module{email} ---
...@@ -18,10 +18,10 @@ subsumes most of the functionality in several older standard modules ...@@ -18,10 +18,10 @@ subsumes most of the functionality in several older standard modules
such as \refmodule{rfc822}, \refmodule{mimetools}, such as \refmodule{rfc822}, \refmodule{mimetools},
\refmodule{multifile}, and other non-standard packages such as \refmodule{multifile}, and other non-standard packages such as
\module{mimecntl}. It is specifically \emph{not} designed to do any \module{mimecntl}. It is specifically \emph{not} designed to do any
sending of email messages to SMTP (\rfc{2821}) servers; that is the sending of email messages to SMTP (\rfc{2821}), NNTP, or other servers; those
function of the \refmodule{smtplib} module. The \module{email} are functions of modules such as \refmodule{smtplib} and \refmodule{nntplib}.
package attempts to be as RFC-compliant as possible, supporting in The \module{email} package attempts to be as RFC-compliant as possible,
addition to \rfc{2822}, such MIME-related RFCs as supporting in addition to \rfc{2822}, such MIME-related RFCs as
\rfc{2045}, \rfc{2046}, \rfc{2047}, and \rfc{2231}. \rfc{2045}, \rfc{2046}, \rfc{2047}, and \rfc{2231}.
The primary distinguishing feature of the \module{email} package is The primary distinguishing feature of the \module{email} package is
...@@ -41,7 +41,7 @@ The following sections describe the functionality of the ...@@ -41,7 +41,7 @@ The following sections describe the functionality of the
should be common in applications: an email message is read as flat 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 text from a file or other source, the text is parsed to produce the
object structure of the email message, this structure is manipulated, 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 It is perfectly feasible to create the object structure out of whole
cloth --- i.e. completely from scratch. From there, a similar cloth --- i.e. completely from scratch. From there, a similar
...@@ -56,6 +56,7 @@ package, a section on differences and porting is provided. ...@@ -56,6 +56,7 @@ package, a section on differences and porting is provided.
\begin{seealso} \begin{seealso}
\seemodule{smtplib}{SMTP protocol client} \seemodule{smtplib}{SMTP protocol client}
\seemodule{nntplib}{NNTP protocol client}
\end{seealso} \end{seealso}
\subsection{Representing an email message} \subsection{Representing an email message}
...@@ -88,22 +89,51 @@ package, a section on differences and porting is provided. ...@@ -88,22 +89,51 @@ package, a section on differences and porting is provided.
\subsection{Iterators} \subsection{Iterators}
\input{emailiter} \input{emailiter}
\subsection{Package History} \subsection{Package History\label{email-pkg-history}}
Version 1 of the \module{email} package was bundled with Python This table describes the release history of the email package, corresponding
releases up to Python 2.2.1. Version 2 was developed for the Python to the version of Python that the package was released with. For purposes of
2.3 release, and backported to Python 2.2.2. It was also available as this document, when you see a note about change or added versions, these refer
a separate distutils-based package, and is compatible back to Python 2.1. 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 \begin{tableiii}{l|l|l}{constant}{email version}{distributed with}{compatible with}
distutils-based package. It is compatible back to Python 2.3. \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} \begin{itemize}
\item The \class{FeedParser} class was introduced, and the \class{Parser} \item The \class{FeedParser} class was introduced, and the \class{Parser}
class was implemented in terms of the \class{FeedParser}. All parsing 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 raise an exception. Problems found while parsing messages are stored in
the message's \var{defect} attribute. the message's \var{defect} attribute.
...@@ -117,7 +147,7 @@ Here are the differences between \module{email} version 3 and version 2: ...@@ -117,7 +147,7 @@ Here are the differences between \module{email} version 3 and version 2:
\method{Generator.__call__()}, \method{Message.get_type()}, \method{Generator.__call__()}, \method{Message.get_type()},
\method{Message.get_main_type()}, \method{Message.get_subtype()}, and \method{Message.get_main_type()}, \method{Message.get_subtype()}, and
the \var{strict} argument to the \class{Parser} class. These are 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. \item Support for Pythons earlier than 2.3 has been removed.
\end{itemize} \end{itemize}
...@@ -278,12 +308,12 @@ The \class{Message} class has the following differences: ...@@ -278,12 +308,12 @@ The \class{Message} class has the following differences:
\item The method \method{getpayloadastext()} was removed. Similar \item The method \method{getpayloadastext()} was removed. Similar
functionality functionality
is supported by the \class{DecodedGenerator} class in the 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 \item The method \method{getbodyastext()} was removed. You can get
similar functionality by creating an iterator with similar functionality by creating an iterator with
\function{typed_subpart_iterator()} in the \function{typed_subpart_iterator()} in the
\refmodule{email.Iterators} module. \refmodule{email.iterators} module.
\end{itemize} \end{itemize}
The \class{Parser} class has no differences in its public interface. The \class{Parser} class has no differences in its public interface.
...@@ -295,7 +325,7 @@ notification\footnote{Delivery Status Notifications (DSN) are defined ...@@ -295,7 +325,7 @@ notification\footnote{Delivery Status Notifications (DSN) are defined
in \rfc{1894}.}. in \rfc{1894}.}.
The \class{Generator} class has no differences in its public 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 module though, called \class{DecodedGenerator} which provides most of
the functionality previously available in the the functionality previously available in the
\method{Message.getpayloadastext()} method. \method{Message.getpayloadastext()} method.
...@@ -329,11 +359,11 @@ The following modules and classes have been changed: ...@@ -329,11 +359,11 @@ The following modules and classes have been changed:
\module{mimelib} provided some utility functions in its \module{mimelib} provided some utility functions in its
\module{address} and \module{date} modules. All of these functions \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 The \code{MsgReader} class/module has been removed. Its functionality
is most closely supported in the \function{body_line_iterator()} 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} \subsection{Examples}
......
\declaremodule{standard}{email.Charset} \declaremodule{standard}{email.charset}
\modulesynopsis{Character Sets} \modulesynopsis{Character Sets}
This module provides a class \class{Charset} for representing This module provides a class \class{Charset} for representing
...@@ -7,6 +7,8 @@ well as a character set registry and several convenience methods for ...@@ -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 manipulating this registry. Instances of \class{Charset} are used in
several other modules within the \module{email} package. several other modules within the \module{email} package.
Import this class from the \module{email.charset} module.
\versionadded{2.2.2} \versionadded{2.2.2}
\begin{classdesc}{Charset}{\optional{input_charset}} \begin{classdesc}{Charset}{\optional{input_charset}}
...@@ -153,7 +155,7 @@ input charset to the output charset automatically. This is not useful ...@@ -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 for multibyte character sets, which have line length issues (multibyte
characters must be split on a character, not a byte boundary); use the characters must be split on a character, not a byte boundary); use the
higher-level \class{Header} class to deal with these issues (see 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 type of encoding (base64 or quoted-printable) will be based on
the \var{header_encoding} attribute. the \var{header_encoding} attribute.
...@@ -188,7 +190,7 @@ This method allows you to compare two \class{Charset} instances for equality. ...@@ -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. This method allows you to compare two \class{Charset} instances for inequality.
\end{methoddesc} \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, functions for adding new entries to the global character set, alias,
and codec registries: and codec registries:
......
\declaremodule{standard}{email.Encoders} \declaremodule{standard}{email.encoders}
\modulesynopsis{Encoders for email message payloads.} \modulesynopsis{Encoders for email message payloads.}
When creating \class{Message} objects from scratch, you often need to 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/*} ...@@ -7,7 +7,7 @@ This is especially true for \mimetype{image/*} and \mimetype{text/*}
type messages containing binary data. type messages containing binary data.
The \module{email} package provides some convenient encodings in its 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 \class{MIMEAudio} and \class{MIMEImage} class constructors to provide default
encodings. All encoder functions take exactly one argument, the message encodings. All encoder functions take exactly one argument, the message
object to encode. They usually extract the payload, encode it, and reset the object to encode. They usually extract the payload, encode it, and reset the
......
\declaremodule{standard}{email.Errors} \declaremodule{standard}{email.errors}
\modulesynopsis{The exception classes used by the email package.} \modulesynopsis{The exception classes used by the email package.}
The following exception classes are defined in the The following exception classes are defined in the
\module{email.Errors} module: \module{email.errors} module:
\begin{excclassdesc}{MessageError}{} \begin{excclassdesc}{MessageError}{}
This is the base class for all exceptions that the \module{email} 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 ...@@ -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 \mimetype{multipart/alternative} had a malformed header, that nested message
object would have a defect, but the containing messages would not. 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! this class is \emph{not} an exception!
\versionadded[All the defect classes were added]{2.4} \versionadded[All the defect classes were added]{2.4}
......
\declaremodule{standard}{email.Generator} \declaremodule{standard}{email.generator}
\modulesynopsis{Generate flat text email messages from a message structure.} \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 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 ...@@ -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 console. Taking a message object structure and producing a flat text
document is the job of the \class{Generator} class. 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 to the functionality of the bundled generator; you could write one
from scratch yourself. However the bundled generator knows how to from scratch yourself. However the bundled generator knows how to
generate most email in a standards-compliant way, should handle MIME 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 ...@@ -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 \class{Parser} class, and back to flat text, is idempotent (the input
is identical to the output). 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{, \begin{classdesc}{Generator}{outfp\optional{, mangle_from_\optional{,
maxheaderlen}}} maxheaderlen}}}
...@@ -40,7 +41,7 @@ mailbox format files. ...@@ -40,7 +41,7 @@ mailbox format files.
Optional \var{maxheaderlen} specifies the longest length for a Optional \var{maxheaderlen} specifies the longest length for a
non-continued header. When a header line is longer than non-continued header. When a header line is longer than
\var{maxheaderlen} (in characters, with tabs expanded to 8 spaces), \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 class. Set to zero to disable header wrapping. The default is 78, as
recommended (but not required) by \rfc{2822}. recommended (but not required) by \rfc{2822}.
\end{classdesc} \end{classdesc}
...@@ -81,9 +82,9 @@ be used in extended print statements. ...@@ -81,9 +82,9 @@ be used in extended print statements.
As a convenience, see the methods \method{Message.as_string()} and As a convenience, see the methods \method{Message.as_string()} and
\code{str(aMessage)}, a.k.a. \method{Message.__str__()}, which \code{str(aMessage)}, a.k.a. \method{Message.__str__()}, which
simplify the generation of a formatted string representation of a 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} called \class{DecodedGenerator} which is like the \class{Generator}
base class, except that non-\mimetype{text} parts are substituted with base class, except that non-\mimetype{text} parts are substituted with
a format string representing the part. a format string representing the part.
...@@ -128,13 +129,5 @@ The default value for \var{fmt} is \code{None}, meaning ...@@ -128,13 +129,5 @@ The default value for \var{fmt} is \code{None}, meaning
\versionadded{2.2.2} \versionadded{2.2.2}
\end{classdesc} \end{classdesc}
\subsubsection{Deprecated methods} \versionchanged[The previously deprecated method \method{__call__()} was
removed]{2.5}
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}
\declaremodule{standard}{email.Header} \declaremodule{standard}{email.header}
\modulesynopsis{Representing non-ASCII headers} \modulesynopsis{Representing non-ASCII headers}
\rfc{2822} is the base standard that describes the format of email \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 ...@@ -15,17 +15,18 @@ slew of RFCs have been written describing how to encode email
containing non-\ASCII{} characters into \rfc{2822}-compliant format. containing non-\ASCII{} characters into \rfc{2822}-compliant format.
These RFCs include \rfc{2045}, \rfc{2046}, \rfc{2047}, and \rfc{2231}. These RFCs include \rfc{2045}, \rfc{2046}, \rfc{2047}, and \rfc{2231}.
The \module{email} package supports these standards in its 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, If you want to include non-\ASCII{} characters in your email headers,
say in the \mailheader{Subject} or \mailheader{To} fields, you should say in the \mailheader{Subject} or \mailheader{To} fields, you should
use the \class{Header} class and assign the field in the use the \class{Header} class and assign the field in the
\class{Message} object to an instance of \class{Header} instead of \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} \begin{verbatim}
>>> from email.Message import Message >>> from email.message import Message
>>> from email.Header import Header >>> from email.header import Header
>>> msg = Message() >>> msg = Message()
>>> h = Header('p\xf6stal', 'iso-8859-1') >>> h = Header('p\xf6stal', 'iso-8859-1')
>>> msg['Subject'] = h >>> msg['Subject'] = h
...@@ -87,7 +88,7 @@ Optional \var{errors} is passed straight through to the ...@@ -87,7 +88,7 @@ Optional \var{errors} is passed straight through to the
Append the string \var{s} to the MIME header. Append the string \var{s} to the MIME header.
Optional \var{charset}, if given, should be a \class{Charset} instance 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 will be converted to a \class{Charset} instance. A value of
\code{None} (the default) means that the \var{charset} given in the \code{None} (the default) means that the \var{charset} given in the
constructor is used. constructor is used.
...@@ -139,7 +140,7 @@ This method allows you to compare two \class{Header} instances for equality. ...@@ -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. This method allows you to compare two \class{Header} instances for inequality.
\end{methoddesc} \end{methoddesc}
The \module{email.Header} module also provides the following The \module{email.header} module also provides the following
convenient functions. convenient functions.
\begin{funcdesc}{decode_header}{header} \begin{funcdesc}{decode_header}{header}
...@@ -155,7 +156,7 @@ encoded string. ...@@ -155,7 +156,7 @@ encoded string.
Here's an example: Here's an example:
\begin{verbatim} \begin{verbatim}
>>> from email.Header import decode_header >>> from email.header import decode_header
>>> decode_header('=?iso-8859-1?q?p=F6stal?=') >>> decode_header('=?iso-8859-1?q?p=F6stal?=')
[('p\xf6stal', 'iso-8859-1')] [('p\xf6stal', 'iso-8859-1')]
\end{verbatim} \end{verbatim}
......
\declaremodule{standard}{email.Iterators} \declaremodule{standard}{email.iterators}
\modulesynopsis{Iterate over a message object tree.} \modulesynopsis{Iterate over a message object tree.}
Iterating over a message object tree is fairly easy with the 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 provides some useful higher level iterations over message object
trees. trees.
......
\declaremodule{standard}{email.Message} \declaremodule{standard}{email.message}
\modulesynopsis{The base class representing email messages.} \modulesynopsis{The base class representing email messages.}
The central class in the \module{email} package is the The central class in the \module{email} package is the
\class{Message} class; it is the base class for the \module{email} \class{Message} class, imported from the \module{email.message} module. It is
object model. \class{Message} provides the core functionality for the base class for the \module{email} object model. \class{Message} provides
setting and querying header fields, and for accessing message bodies. the core functionality for setting and querying header fields, and for
accessing message bodies.
Conceptually, a \class{Message} object consists of \emph{headers} and Conceptually, a \class{Message} object consists of \emph{headers} and
\emph{payloads}. Headers are \rfc{2822} style field names and \emph{payloads}. Headers are \rfc{2822} style field names and
...@@ -45,7 +46,7 @@ begin with \code{From }. For more flexibility, instantiate a ...@@ -45,7 +46,7 @@ begin with \code{From }. For more flexibility, instantiate a
\begin{verbatim} \begin{verbatim}
from cStringIO import StringIO from cStringIO import StringIO
from email.Generator import Generator from email.generator import Generator
fp = StringIO() fp = StringIO()
g = Generator(fp, mangle_from_=False, maxheaderlen=60) g = Generator(fp, mangle_from_=False, maxheaderlen=60)
g.flatten(msg) g.flatten(msg)
...@@ -119,7 +120,7 @@ client's responsibility to ensure the payload invariants. Optional ...@@ -119,7 +120,7 @@ client's responsibility to ensure the payload invariants. Optional
\begin{methoddesc}[Message]{set_charset}{charset} \begin{methoddesc}[Message]{set_charset}{charset}
Set the character set of the payload to \var{charset}, which can 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, string naming a character set,
or \code{None}. If it is a string, it will be converted to a or \code{None}. If it is a string, it will be converted to a
\class{Charset} instance. If \var{charset} is \code{None}, the \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 ...@@ -128,8 +129,8 @@ or \code{None}. If it is a string, it will be converted to a
\exception{TypeError}. \exception{TypeError}.
The message will be assumed to be of type \mimetype{text/*} encoded with The message will be assumed to be of type \mimetype{text/*} encoded with
\code{charset.input_charset}. It will be converted to \var{charset.input_charset}. It will be converted to
\code{charset.output_charset} \var{charset.output_charset}
and encoded properly, if needed, when generating the plain text and encoded properly, if needed, when generating the plain text
representation of the message. MIME headers representation of the message. MIME headers
(\mailheader{MIME-Version}, \mailheader{Content-Type}, (\mailheader{MIME-Version}, \mailheader{Content-Type},
...@@ -513,6 +514,9 @@ message/rfc822 ...@@ -513,6 +514,9 @@ message/rfc822
\end{verbatim} \end{verbatim}
\end{methoddesc} \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 \class{Message} objects can also optionally contain two instance
attributes, which can be used when generating the plain text of a MIME attributes, which can be used when generating the plain text of a MIME
message. message.
...@@ -532,7 +536,7 @@ to the message's \var{preamble} attribute. When the \class{Generator} ...@@ -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 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 finds the message has a \var{preamble} attribute, it will write this
text in the area between the headers and the first boundary. See 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 Note that if the message object has no preamble, the
\var{preamble} attribute will be \code{None}. \var{preamble} attribute will be \code{None}.
...@@ -543,58 +547,15 @@ The \var{epilogue} attribute acts the same way as the \var{preamble} ...@@ -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 attribute, except that it contains text that appears between the last
boundary and the end of the message. boundary and the end of the message.
One note: when generating the flat text for a \mimetype{multipart} \versionchanged[You do not need to set the epilogue to the empty string in
message that has no \var{epilogue} (using the standard order for the \class{Generator} to print a newline at the end of the
\class{Generator} class), no newline is added after the closing file]{2.5}
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.
\end{datadesc} \end{datadesc}
\begin{datadesc}{defects} \begin{datadesc}{defects}
The \var{defects} attribute contains a list of all the problems found when 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. of the possible parsing defects.
\versionadded{2.4} \versionadded{2.4}
\end{datadesc} \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}
\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 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 some text to a parser, which parses the text and returns the root
message object. However you can also build a complete message 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 ...@@ -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 \class{Message} objects, move them around, etc. This makes a very
convenient interface for slicing-and-dicing MIME messages. convenient interface for slicing-and-dicing MIME messages.
You can create a new object structure by creating \class{Message} You can create a new object structure by creating \class{Message} instances,
instances, adding attachments and all the appropriate headers manually. adding attachments and all the appropriate headers manually. For MIME
For MIME messages though, the \module{email} package provides some messages though, the \module{email} package provides some convenient
convenient subclasses to make things easier. Each of these classes subclasses to make things easier.
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}
Here are the classes: Here are the classes:
\begin{classdesc}{MIMEBase}{_maintype, _subtype, **_params} \begin{classdesc}{MIMEBase}{_maintype, _subtype, **_params}
Module: \module{email.mime.base}
This is the base class for all the MIME-specific subclasses of This is the base class for all the MIME-specific subclasses of
\class{Message}. Ordinarily you won't create instances specifically \class{Message}. Ordinarily you won't create instances specifically
of \class{MIMEBase}, although you could. \class{MIMEBase} is provided 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 ...@@ -45,6 +43,8 @@ The \class{MIMEBase} class always adds a \mailheader{Content-Type} header
\end{classdesc} \end{classdesc}
\begin{classdesc}{MIMENonMultipart}{} \begin{classdesc}{MIMENonMultipart}{}
Module: \module{email.mime.nonmultipart}
A subclass of \class{MIMEBase}, this is an intermediate base class for A subclass of \class{MIMEBase}, this is an intermediate base class for
MIME messages that are not \mimetype{multipart}. The primary purpose MIME messages that are not \mimetype{multipart}. The primary purpose
of this class is to prevent the use of the \method{attach()} method, of this class is to prevent the use of the \method{attach()} method,
...@@ -57,6 +57,7 @@ exception is raised. ...@@ -57,6 +57,7 @@ exception is raised.
\begin{classdesc}{MIMEMultipart}{\optional{subtype\optional{, \begin{classdesc}{MIMEMultipart}{\optional{subtype\optional{,
boundary\optional{, _subparts\optional{, _params}}}}} boundary\optional{, _subparts\optional{, _params}}}}}
Module: \module{email.mime.multipart}
A subclass of \class{MIMEBase}, this is an intermediate base class for A subclass of \class{MIMEBase}, this is an intermediate base class for
MIME messages that are \mimetype{multipart}. Optional \var{_subtype} MIME messages that are \mimetype{multipart}. Optional \var{_subtype}
...@@ -80,8 +81,31 @@ argument, which is a keyword dictionary. ...@@ -80,8 +81,31 @@ argument, which is a keyword dictionary.
\versionadded{2.2.2} \versionadded{2.2.2}
\end{classdesc} \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{, \begin{classdesc}{MIMEAudio}{_audiodata\optional{, _subtype\optional{,
_encoder\optional{, **_params}}}} _encoder\optional{, **_params}}}}
Module: \module{email.mime.audio}
A subclass of \class{MIMENonMultipart}, the \class{MIMEAudio} class A subclass of \class{MIMENonMultipart}, the \class{MIMEAudio} class
is used to create MIME message objects of major type \mimetype{audio}. 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 ...@@ -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 change the payload to encoded form. It should also add any
\mailheader{Content-Transfer-Encoding} or other headers to the message \mailheader{Content-Transfer-Encoding} or other headers to the message
object as necessary. The default encoding is base64. See the 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. \var{_params} are passed straight through to the base class constructor.
\end{classdesc} \end{classdesc}
\begin{classdesc}{MIMEImage}{_imagedata\optional{, _subtype\optional{, \begin{classdesc}{MIMEImage}{_imagedata\optional{, _subtype\optional{,
_encoder\optional{, **_params}}}} _encoder\optional{, **_params}}}}
Module: \module{email.mime.image}
A subclass of \class{MIMENonMultipart}, the \class{MIMEImage} class is A subclass of \class{MIMENonMultipart}, the \class{MIMEImage} class is
used to create MIME message objects of major type \mimetype{image}. 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 ...@@ -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 change the payload to encoded form. It should also add any
\mailheader{Content-Transfer-Encoding} or other headers to the message \mailheader{Content-Transfer-Encoding} or other headers to the message
object as necessary. The default encoding is base64. See the 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} \var{_params} are passed straight through to the \class{MIMEBase}
constructor. constructor.
\end{classdesc} \end{classdesc}
\begin{classdesc}{MIMEMessage}{_msg\optional{, _subtype}} \begin{classdesc}{MIMEMessage}{_msg\optional{, _subtype}}
Module: \module{email.mime.message}
A subclass of \class{MIMENonMultipart}, the \class{MIMEMessage} class A subclass of \class{MIMENonMultipart}, the \class{MIMEMessage} class
is used to create MIME objects of main type \mimetype{message}. 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 \var{_msg} is used as the payload, and must be an instance of class
...@@ -143,6 +170,8 @@ to \mimetype{rfc822}. ...@@ -143,6 +170,8 @@ to \mimetype{rfc822}.
\end{classdesc} \end{classdesc}
\begin{classdesc}{MIMEText}{_text\optional{, _subtype\optional{, _charset}}} \begin{classdesc}{MIMEText}{_text\optional{, _subtype\optional{, _charset}}}
Module: \module{email.mime.text}
A subclass of \class{MIMENonMultipart}, the \class{MIMEText} class is A subclass of \class{MIMENonMultipart}, the \class{MIMEText} class is
used to create MIME objects of major type \mimetype{text}. used to create MIME objects of major type \mimetype{text}.
\var{_text} is the string for the payload. \var{_subtype} is the \var{_text} is the string for the payload. \var{_subtype} is the
......
\declaremodule{standard}{email.Parser} \declaremodule{standard}{email.parser}
\modulesynopsis{Parse flat text email messages to produce a message \modulesynopsis{Parse flat text email messages to produce a message
object structure.} object structure.}
...@@ -41,9 +41,10 @@ message object trees any way it finds necessary. ...@@ -41,9 +41,10 @@ message object trees any way it finds necessary.
\versionadded{2.4} \versionadded{2.4}
The \class{FeedParser} provides an API that is conducive to incremental The \class{FeedParser}, imported from the \module{email.feedparser} module,
parsing of email messages, such as would be necessary when reading the text of provides an API that is conducive to incremental parsing of email messages,
an email message from a source that can block (e.g. a socket). The 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 \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 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 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 ...@@ -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 job of parsing non-compliant messages, providing information about how a
message was deemed broken. It will populate a message object's \var{defects} 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 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}: Here is the API for the \class{FeedParser}:
\begin{classdesc}{FeedParser}{\optional{_factory}} \begin{classdesc}{FeedParser}{\optional{_factory}}
Create a \class{FeedParser} instance. Optional \var{_factory} is a Create a \class{FeedParser} instance. Optional \var{_factory} is a
no-argument callable that will be called whenever a new message object is 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} \end{classdesc}
\begin{methoddesc}[FeedParser]{feed}{data} \begin{methoddesc}[FeedParser]{feed}{data}
...@@ -82,21 +83,22 @@ more data to a closed \class{FeedParser}. ...@@ -82,21 +83,22 @@ more data to a closed \class{FeedParser}.
\subsubsection{Parser class API} \subsubsection{Parser class API}
The \class{Parser} provides an API that can be used to parse a message when The \class{Parser} class, imported from the \module{email.parser} module,
the complete contents of the message are available in a string or file. The provides an API that can be used to parse a message when the complete contents
\module{email.Parser} module also provides a second class, called 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 \class{HeaderParser} which can be used if you're only interested in
the headers of the message. \class{HeaderParser} can be much faster 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, these situations, since it does not attempt to parse the message body,
instead setting the payload to the raw body as a string. instead setting the payload to the raw body as a string.
\class{HeaderParser} has the same API as the \class{Parser} class. \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 The constructor for the \class{Parser} class takes an optional
argument \var{_class}. This must be a callable factory (such as a 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 function or a class), and it is used whenever a sub-message object
needs to be created. It defaults to \class{Message} (see 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. arguments.
The optional \var{strict} flag is ignored. \deprecated{2.4}{Because the The optional \var{strict} flag is ignored. \deprecated{2.4}{Because the
...@@ -201,6 +203,6 @@ Here are some notes on the parsing semantics: ...@@ -201,6 +203,6 @@ Here are some notes on the parsing semantics:
\method{is_multipart()} method may return \code{False}. If such \method{is_multipart()} method may return \code{False}. If such
messages were parsed with the \class{FeedParser}, they will have an messages were parsed with the \class{FeedParser}, they will have an
instance of the \class{MultipartInvariantViolationDefect} class in their 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. details.
\end{itemize} \end{itemize}
\declaremodule{standard}{email.Utils} \declaremodule{standard}{email.utils}
\modulesynopsis{Miscellaneous email package utilities.} \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: module:
\begin{funcdesc}{quote}{str} \begin{funcdesc}{quote}{str}
...@@ -38,7 +38,7 @@ values as might be returned by \method{Message.get_all()}. Here's a ...@@ -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: simple example that gets all the recipients of a message:
\begin{verbatim} \begin{verbatim}
from email.Utils import getaddresses from email.utils import getaddresses
tos = msg.get_all('to', []) tos = msg.get_all('to', [])
ccs = msg.get_all('cc', []) ccs = msg.get_all('cc', [])
......
...@@ -12,9 +12,9 @@ ...@@ -12,9 +12,9 @@
\authoraddress{\email{barry@python.org}} \authoraddress{\email{barry@python.org}}
\date{\today} \date{\today}
\release{3.0} % software release, not documentation \release{4.0} % software release, not documentation
\setreleaseinfo{} % empty for final release \setreleaseinfo{} % empty for final release
\setshortversion{3.0} % major.minor only for software \setshortversion{4.0} % major.minor only for software
\begin{document} \begin{document}
...@@ -38,11 +38,11 @@ The \module{email} package provides classes and utilities to create, ...@@ -38,11 +38,11 @@ The \module{email} package provides classes and utilities to create,
parse, generate, and modify email messages, conforming to all the parse, generate, and modify email messages, conforming to all the
relevant email and MIME related RFCs. relevant email and MIME related RFCs.
This document describes version 3.0 of the \module{email} package, which is This document describes version 4.0 of the \module{email} package, which is
distributed with Python 2.4 and is available as a standalone distutils-based distributed with Python 2.5 and is available as a standalone distutils-based
package for use with Python 2.3. \module{email} 3.0 is not compatible with package for use with earlier Python versions. \module{email} 4.0 is not
Python versions earlier than 2.3. For more information about the compatible with Python versions earlier than 2.3. For more information about
\module{email} package, including download links and mailing lists, see the \module{email} package, including download links and mailing lists, see
\ulink{Python's email SIG}{http://www.python.org/sigs/email-sig}. \ulink{Python's email SIG}{http://www.python.org/sigs/email-sig}.
The documentation that follows was written for the Python project, so 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: ...@@ -51,7 +51,8 @@ package documentation, there are a few notes to be aware of:
\begin{itemize} \begin{itemize}
\item Deprecation and ``version added'' notes are relative to the \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 \item If you're reading this documentation as part of the
standalone \module{email} package, some of the internal links to standalone \module{email} package, some of the internal links to
......
...@@ -4,9 +4,10 @@ ...@@ -4,9 +4,10 @@
"""A package for parsing, handling, and generating email messages.""" """A package for parsing, handling, and generating email messages."""
__version__ = '3.0.1' __version__ = '4.0a2'
__all__ = [ __all__ = [
# Old names
'base64MIME', 'base64MIME',
'Charset', 'Charset',
'Encoders', 'Encoders',
...@@ -27,6 +28,19 @@ __all__ = [ ...@@ -27,6 +28,19 @@ __all__ = [
'Utils', 'Utils',
'message_from_string', 'message_from_string',
'message_from_file', '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): ...@@ -39,7 +53,7 @@ def message_from_string(s, *args, **kws):
Optional _class and strict are passed to the Parser constructor. 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) return Parser(*args, **kws).parsestr(s)
...@@ -48,5 +62,62 @@ def message_from_file(fp, *args, **kws): ...@@ -48,5 +62,62 @@ def message_from_file(fp, *args, **kws):
Optional _class and strict are passed to the Parser constructor. 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) 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)
...@@ -6,6 +6,13 @@ ...@@ -6,6 +6,13 @@
Lifted directly from rfc822.py. This should eventually be rewritten. Lifted directly from rfc822.py. This should eventually be rewritten.
""" """
__all__ = [
'mktime_tz',
'parsedate',
'parsedate_tz',
'quote',
]
import time import time
SPACE = ' ' SPACE = ' '
......
# Copyright (C) 2002-2004 Python Software Foundation # Copyright (C) 2002-2006 Python Software Foundation
# Author: Ben Gertzfield # Author: Ben Gertzfield
# Contact: email-sig@python.org # Contact: email-sig@python.org
...@@ -24,9 +24,21 @@ decoding. To deal with the various line wrapping issues, use the email.Header ...@@ -24,9 +24,21 @@ decoding. To deal with the various line wrapping issues, use the email.Header
module. module.
""" """
__all__ = [
'base64_len',
'body_decode',
'body_encode',
'decode',
'decodestring',
'encode',
'encodestring',
'header_encode',
]
import re import re
from binascii import b2a_base64, a2b_base64 from binascii import b2a_base64, a2b_base64
from email.Utils import fix_eols from email.utils import fix_eols
CRLF = '\r\n' CRLF = '\r\n'
NL = '\n' NL = '\n'
......
...@@ -2,9 +2,18 @@ ...@@ -2,9 +2,18 @@
# Author: Ben Gertzfield, Barry Warsaw # Author: Ben Gertzfield, Barry Warsaw
# Contact: email-sig@python.org # Contact: email-sig@python.org
import email.base64MIME __all__ = [
import email.quopriMIME 'Charset',
from email.Encoders import encode_7or8bit '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: ...@@ -186,8 +195,17 @@ class Charset:
""" """
def __init__(self, input_charset=DEFAULT_CHARSET): def __init__(self, input_charset=DEFAULT_CHARSET):
# RFC 2046, $4.1.2 says charsets are not case sensitive. We coerce to # RFC 2046, $4.1.2 says charsets are not case sensitive. We coerce to
# unicode because its .lower() is locale insensitive. # unicode because its .lower() is locale insensitive. If the argument
input_charset = unicode(input_charset, 'ascii').lower() # 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 # Set the input charset after filtering through the aliases
self.input_charset = ALIASES.get(input_charset, input_charset) self.input_charset = ALIASES.get(input_charset, input_charset)
# We can try to guess which encoding and conversion to use by the # We can try to guess which encoding and conversion to use by the
...@@ -307,12 +325,12 @@ class Charset: ...@@ -307,12 +325,12 @@ class Charset:
cset = self.get_output_charset() cset = self.get_output_charset()
# The len(s) of a 7bit encoding is len(s) # The len(s) of a 7bit encoding is len(s)
if self.header_encoding == BASE64: 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: 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: elif self.header_encoding == SHORTEST:
lenb64 = email.base64MIME.base64_len(s) lenb64 = email.base64mime.base64_len(s)
lenqp = email.quopriMIME.header_quopri_len(s) lenqp = email.quoprimime.header_quopri_len(s)
return min(lenb64, lenqp) + len(cset) + MISC_LEN return min(lenb64, lenqp) + len(cset) + MISC_LEN
else: else:
return len(s) return len(s)
...@@ -335,16 +353,16 @@ class Charset: ...@@ -335,16 +353,16 @@ class Charset:
s = self.convert(s) s = self.convert(s)
# 7bit/8bit encodings return the string unchanged (modulo conversions) # 7bit/8bit encodings return the string unchanged (modulo conversions)
if self.header_encoding == BASE64: if self.header_encoding == BASE64:
return email.base64MIME.header_encode(s, cset) return email.base64mime.header_encode(s, cset)
elif self.header_encoding == QP: 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: elif self.header_encoding == SHORTEST:
lenb64 = email.base64MIME.base64_len(s) lenb64 = email.base64mime.base64_len(s)
lenqp = email.quopriMIME.header_quopri_len(s) lenqp = email.quoprimime.header_quopri_len(s)
if lenb64 < lenqp: if lenb64 < lenqp:
return email.base64MIME.header_encode(s, cset) return email.base64mime.header_encode(s, cset)
else: else:
return email.quopriMIME.header_encode(s, cset, maxlinelen=None) return email.quoprimime.header_encode(s, cset, maxlinelen=None)
else: else:
return s return s
...@@ -363,8 +381,8 @@ class Charset: ...@@ -363,8 +381,8 @@ class Charset:
s = self.convert(s) s = self.convert(s)
# 7bit/8bit encodings return the string unchanged (module conversions) # 7bit/8bit encodings return the string unchanged (module conversions)
if self.body_encoding is BASE64: if self.body_encoding is BASE64:
return email.base64MIME.body_encode(s) return email.base64mime.body_encode(s)
elif self.body_encoding is QP: elif self.body_encoding is QP:
return email.quopriMIME.body_encode(s) return email.quoprimime.body_encode(s)
else: else:
return s return s
# Copyright (C) 2001-2004 Python Software Foundation # Copyright (C) 2001-2006 Python Software Foundation
# Author: Barry Warsaw # Author: Barry Warsaw
# Contact: email-sig@python.org # Contact: email-sig@python.org
"""Encodings and related functions.""" """Encodings and related functions."""
__all__ = [
'encode_7or8bit',
'encode_base64',
'encode_noop',
'encode_quopri',
]
import base64 import base64
from quopri import encodestring as _encodestring from quopri import encodestring as _encodestring
def _qencode(s): def _qencode(s):
enc = _encodestring(s, quotetabs=True) enc = _encodestring(s, quotetabs=True)
# Must encode spaces, which quopri.encodestring() doesn't do # Must encode spaces, which quopri.encodestring() doesn't do
......
# Copyright (C) 2001-2004 Python Software Foundation # Copyright (C) 2001-2006 Python Software Foundation
# Author: Barry Warsaw # Author: Barry Warsaw
# Contact: email-sig@python.org # Contact: email-sig@python.org
...@@ -26,6 +26,10 @@ class MultipartConversionError(MessageError, TypeError): ...@@ -26,6 +26,10 @@ class MultipartConversionError(MessageError, TypeError):
"""Conversion to a multipart is prohibited.""" """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. # These are parsing defects which the parser was able to work around.
class MessageDefect: class MessageDefect:
......
...@@ -19,9 +19,12 @@ the current message. Defects are just instances that live on the message ...@@ -19,9 +19,12 @@ the current message. Defects are just instances that live on the message
object's .defects attribute. object's .defects attribute.
""" """
__all__ = ['FeedParser']
import re 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 = re.compile('\r\n|\r|\n')
NLCRE_bol = re.compile('(\r\n|\r|\n)') NLCRE_bol = re.compile('(\r\n|\r|\n)')
...@@ -130,7 +133,7 @@ class BufferedSubFile(object): ...@@ -130,7 +133,7 @@ class BufferedSubFile(object):
class FeedParser: class FeedParser:
"""A feed-style parser of email.""" """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""" """_factory is called with no arguments to create a new message obj"""
self._factory = _factory self._factory = _factory
self._input = BufferedSubFile() self._input = BufferedSubFile()
...@@ -164,7 +167,7 @@ class FeedParser: ...@@ -164,7 +167,7 @@ class FeedParser:
# Look for final set of defects # Look for final set of defects
if root.get_content_maintype() == 'multipart' \ if root.get_content_maintype() == 'multipart' \
and not root.is_multipart(): and not root.is_multipart():
root.defects.append(Errors.MultipartInvariantViolationDefect()) root.defects.append(errors.MultipartInvariantViolationDefect())
return root return root
def _new_message(self): def _new_message(self):
...@@ -277,7 +280,7 @@ class FeedParser: ...@@ -277,7 +280,7 @@ class FeedParser:
# defined a boundary. That's a problem which we'll handle by # defined a boundary. That's a problem which we'll handle by
# reading everything until the EOF and marking the message as # reading everything until the EOF and marking the message as
# defective. # defective.
self._cur.defects.append(Errors.NoBoundaryInMultipartDefect()) self._cur.defects.append(errors.NoBoundaryInMultipartDefect())
lines = [] lines = []
for line in self._input: for line in self._input:
if line is NeedMoreData: if line is NeedMoreData:
...@@ -381,7 +384,7 @@ class FeedParser: ...@@ -381,7 +384,7 @@ class FeedParser:
# that as a defect and store the captured text as the payload. # that as a defect and store the captured text as the payload.
# Everything from here to the EOF is epilogue. # Everything from here to the EOF is epilogue.
if capturing_preamble: if capturing_preamble:
self._cur.defects.append(Errors.StartBoundaryNotFoundDefect()) self._cur.defects.append(errors.StartBoundaryNotFoundDefect())
self._cur.set_payload(EMPTYSTRING.join(preamble)) self._cur.set_payload(EMPTYSTRING.join(preamble))
epilogue = [] epilogue = []
for line in self._input: for line in self._input:
...@@ -432,7 +435,7 @@ class FeedParser: ...@@ -432,7 +435,7 @@ class FeedParser:
# The first line of the headers was a continuation. This # The first line of the headers was a continuation. This
# is illegal, so let's note the defect, store the illegal # is illegal, so let's note the defect, store the illegal
# line, and ignore it for purposes of headers. # line, and ignore it for purposes of headers.
defect = Errors.FirstHeaderLineIsContinuationDefect(line) defect = errors.FirstHeaderLineIsContinuationDefect(line)
self._cur.defects.append(defect) self._cur.defects.append(defect)
continue continue
lastvalue.append(line) lastvalue.append(line)
...@@ -460,13 +463,13 @@ class FeedParser: ...@@ -460,13 +463,13 @@ class FeedParser:
else: else:
# Weirdly placed unix-from line. Note this as a defect # Weirdly placed unix-from line. Note this as a defect
# and ignore it. # and ignore it.
defect = Errors.MisplacedEnvelopeHeaderDefect(line) defect = errors.MisplacedEnvelopeHeaderDefect(line)
self._cur.defects.append(defect) self._cur.defects.append(defect)
continue continue
# Split the line on the colon separating field name from value. # Split the line on the colon separating field name from value.
i = line.find(':') i = line.find(':')
if i < 0: if i < 0:
defect = Errors.MalformedHeaderDefect(line) defect = errors.MalformedHeaderDefect(line)
self._cur.defects.append(defect) self._cur.defects.append(defect)
continue continue
lastheader = line[:i] lastheader = line[:i]
......
...@@ -4,14 +4,16 @@ ...@@ -4,14 +4,16 @@
"""Classes to generate plain text from a message object tree.""" """Classes to generate plain text from a message object tree."""
__all__ = ['Generator', 'DecodedGenerator']
import re import re
import sys import sys
import time import time
import random import random
import warnings import warnings
from cStringIO import StringIO
from email.Header import Header from cStringIO import StringIO
from email.header import Header
UNDERSCORE = '_' UNDERSCORE = '_'
NL = '\n' NL = '\n'
...@@ -81,12 +83,6 @@ class Generator: ...@@ -81,12 +83,6 @@ class Generator:
print >> self._fp, ufrom print >> self._fp, ufrom
self._write(msg) 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): def clone(self, fp):
"""Clone this generator with the exact same options.""" """Clone this generator with the exact same options."""
return self.__class__(fp, self._mangle_from_, self._maxheaderlen) return self.__class__(fp, self._mangle_from_, self._maxheaderlen)
......
# Copyright (C) 2002-2004 Python Software Foundation # Copyright (C) 2002-2006 Python Software Foundation
# Author: Ben Gertzfield, Barry Warsaw # Author: Ben Gertzfield, Barry Warsaw
# Contact: email-sig@python.org # Contact: email-sig@python.org
"""Header encoding and decoding functionality.""" """Header encoding and decoding functionality."""
__all__ = [
'Header',
'decode_header',
'make_header',
]
import re import re
import binascii import binascii
import email.quopriMIME import email.quoprimime
import email.base64MIME import email.base64mime
from email.Errors import HeaderParseError
from email.Charset import Charset from email.errors import HeaderParseError
from email.charset import Charset
NL = '\n' NL = '\n'
SPACE = ' ' SPACE = ' '
...@@ -42,7 +49,7 @@ fcre = re.compile(r'[\041-\176]+:$') ...@@ -42,7 +49,7 @@ fcre = re.compile(r'[\041-\176]+:$')
# Helpers # Helpers
_max_append = email.quopriMIME._max_append _max_append = email.quoprimime._max_append
...@@ -82,10 +89,10 @@ def decode_header(header): ...@@ -82,10 +89,10 @@ def decode_header(header):
encoded = parts[2] encoded = parts[2]
dec = None dec = None
if encoding == 'q': if encoding == 'q':
dec = email.quopriMIME.header_decode(encoded) dec = email.quoprimime.header_decode(encoded)
elif encoding == 'b': elif encoding == 'b':
try: try:
dec = email.base64MIME.decode(encoded) dec = email.base64mime.decode(encoded)
except binascii.Error: except binascii.Error:
# Turn this into a higher level exception. BAW: Right # Turn this into a higher level exception. BAW: Right
# now we throw the lower level exception away but # now we throw the lower level exception away but
......
# Copyright (C) 2001-2004 Python Software Foundation # Copyright (C) 2001-2006 Python Software Foundation
# Author: Barry Warsaw # Author: Barry Warsaw
# Contact: email-sig@python.org # Contact: email-sig@python.org
"""Various types of useful iterators and generators.""" """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 import sys
from cStringIO import StringIO from cStringIO import StringIO
...@@ -25,7 +32,6 @@ def walk(self): ...@@ -25,7 +32,6 @@ def walk(self):
# These two functions are imported into the Iterators.py interface module. # 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): def body_line_iterator(msg, decode=False):
"""Iterate over the parts, returning string payloads line-by-line. """Iterate over the parts, returning string payloads line-by-line.
......
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
"""Basic message object for the email package object model.""" """Basic message object for the email package object model."""
__all__ = ['Message']
import re import re
import uu import uu
import binascii import binascii
...@@ -11,9 +13,9 @@ import warnings ...@@ -11,9 +13,9 @@ import warnings
from cStringIO import StringIO from cStringIO import StringIO
# Intrapackage imports # Intrapackage imports
from email import Utils import email.charset
from email import Errors from email import utils
from email import Charset from email import errors
SEMISPACE = '; ' SEMISPACE = '; '
...@@ -41,11 +43,11 @@ def _formatparam(param, value=None, quote=True): ...@@ -41,11 +43,11 @@ def _formatparam(param, value=None, quote=True):
if isinstance(value, tuple): if isinstance(value, tuple):
# Encode as per RFC 2231 # Encode as per RFC 2231
param += '*' 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 # BAW: Please check this. I think that if quote is set it should
# force quoting even if not necessary. # force quoting even if not necessary.
if quote or tspecials.search(value): if quote or tspecials.search(value):
return '%s="%s"' % (param, Utils.quote(value)) return '%s="%s"' % (param, utils.quote(value))
else: else:
return '%s=%s' % (param, value) return '%s=%s' % (param, value)
else: else:
...@@ -70,14 +72,14 @@ def _parseparam(s): ...@@ -70,14 +72,14 @@ def _parseparam(s):
def _unquotevalue(value): 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 # try to convert the value to a unicode. Message.get_param() and
# Message.get_params() are both currently defined to return the tuple in # Message.get_params() are both currently defined to return the tuple in
# the face of RFC 2231 parameters. # the face of RFC 2231 parameters.
if isinstance(value, tuple): if isinstance(value, tuple):
return value[0], value[1], Utils.unquote(value[2]) return value[0], value[1], utils.unquote(value[2])
else: else:
return Utils.unquote(value) return utils.unquote(value)
...@@ -188,17 +190,17 @@ class Message: ...@@ -188,17 +190,17 @@ class Message:
return None return None
cte = self.get('content-transfer-encoding', '').lower() cte = self.get('content-transfer-encoding', '').lower()
if cte == 'quoted-printable': if cte == 'quoted-printable':
return Utils._qdecode(payload) return utils._qdecode(payload)
elif cte == 'base64': elif cte == 'base64':
try: try:
return Utils._bdecode(payload) return utils._bdecode(payload)
except binascii.Error: except binascii.Error:
# Incorrect padding # Incorrect padding
return payload return payload
elif cte in ('x-uuencode', 'uuencode', 'uue', 'x-uue'): elif cte in ('x-uuencode', 'uuencode', 'uue', 'x-uue'):
sfp = StringIO() sfp = StringIO()
try: try:
uu.decode(StringIO(payload+'\n'), sfp) uu.decode(StringIO(payload+'\n'), sfp, quiet=True)
payload = sfp.getvalue() payload = sfp.getvalue()
except uu.Error: except uu.Error:
# Some decoding problem # Some decoding problem
...@@ -237,8 +239,8 @@ class Message: ...@@ -237,8 +239,8 @@ class Message:
self._charset = None self._charset = None
return return
if isinstance(charset, str): if isinstance(charset, str):
charset = Charset.Charset(charset) charset = email.charset.Charset(charset)
if not isinstance(charset, Charset.Charset): if not isinstance(charset, email.charset.Charset):
raise TypeError(charset) raise TypeError(charset)
# BAW: should we accept strings that can serve as arguments to the # BAW: should we accept strings that can serve as arguments to the
# Charset constructor? # Charset constructor?
...@@ -412,49 +414,6 @@ class Message: ...@@ -412,49 +414,6 @@ class Message:
else: else:
raise KeyError(_name) 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. # Use these three methods instead of the three above.
# #
...@@ -537,7 +496,7 @@ class Message: ...@@ -537,7 +496,7 @@ class Message:
name = p.strip() name = p.strip()
val = '' val = ''
params.append((name, val)) params.append((name, val))
params = Utils.decode_params(params) params = utils.decode_params(params)
return params return params
def get_params(self, failobj=None, header='content-type', unquote=True): def get_params(self, failobj=None, header='content-type', unquote=True):
...@@ -714,7 +673,7 @@ class Message: ...@@ -714,7 +673,7 @@ class Message:
filename = self.get_param('name', missing, 'content-disposition') filename = self.get_param('name', missing, 'content-disposition')
if filename is missing: if filename is missing:
return failobj return failobj
return Utils.collapse_rfc2231_value(filename).strip() return utils.collapse_rfc2231_value(filename).strip()
def get_boundary(self, failobj=None): def get_boundary(self, failobj=None):
"""Return the boundary associated with the payload if present. """Return the boundary associated with the payload if present.
...@@ -727,7 +686,7 @@ class Message: ...@@ -727,7 +686,7 @@ class Message:
if boundary is missing: if boundary is missing:
return failobj return failobj
# RFC 2046 says that boundaries may begin but not end in w/s # 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): def set_boundary(self, boundary):
"""Set the boundary parameter in Content-Type to 'boundary'. """Set the boundary parameter in Content-Type to 'boundary'.
...@@ -744,7 +703,7 @@ class Message: ...@@ -744,7 +703,7 @@ class Message:
if params is missing: if params is missing:
# There was no Content-Type header, and we don't know what type # There was no Content-Type header, and we don't know what type
# to set it to, so raise an exception. # 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 = [] newparams = []
foundp = False foundp = False
for pk, pv in params: for pk, pv in params:
......
# 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)
# Copyright (C) 2001-2004 Python Software Foundation # Copyright (C) 2001-2006 Python Software Foundation
# Author: Anthony Baxter # Author: Anthony Baxter
# Contact: email-sig@python.org # Contact: email-sig@python.org
"""Class representing audio/* type MIME documents.""" """Class representing audio/* type MIME documents."""
__all__ = ['MIMEAudio']
import sndhdr import sndhdr
from cStringIO import StringIO
from email import Errors from cStringIO import StringIO
from email import Encoders from email import encoders
from email.MIMENonMultipart import MIMENonMultipart from email.mime.nonmultipart import MIMENonMultipart
...@@ -42,7 +43,7 @@ class MIMEAudio(MIMENonMultipart): ...@@ -42,7 +43,7 @@ class MIMEAudio(MIMENonMultipart):
"""Class for generating audio/* MIME documents.""" """Class for generating audio/* MIME documents."""
def __init__(self, _audiodata, _subtype=None, def __init__(self, _audiodata, _subtype=None,
_encoder=Encoders.encode_base64, **_params): _encoder=encoders.encode_base64, **_params):
"""Create an audio/* type MIME document. """Create an audio/* type MIME document.
_audiodata is a string containing the raw audio data. If this data _audiodata is a string containing the raw audio data. If this data
......
# Copyright (C) 2001-2004 Python Software Foundation # Copyright (C) 2001-2006 Python Software Foundation
# Author: Barry Warsaw # Author: Barry Warsaw
# Contact: email-sig@python.org # Contact: email-sig@python.org
"""Base class for MIME specializations.""" """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.""" """Base class for MIME specializations."""
def __init__(self, _maintype, _subtype, **_params): def __init__(self, _maintype, _subtype, **_params):
...@@ -18,7 +20,7 @@ class MIMEBase(Message.Message): ...@@ -18,7 +20,7 @@ class MIMEBase(Message.Message):
arguments. Additional parameters for this header are taken from the arguments. Additional parameters for this header are taken from the
keyword arguments. keyword arguments.
""" """
Message.Message.__init__(self) message.Message.__init__(self)
ctype = '%s/%s' % (_maintype, _subtype) ctype = '%s/%s' % (_maintype, _subtype)
self.add_header('Content-Type', ctype, **_params) self.add_header('Content-Type', ctype, **_params)
self['MIME-Version'] = '1.0' self['MIME-Version'] = '1.0'
# Copyright (C) 2001-2004 Python Software Foundation # Copyright (C) 2001-2006 Python Software Foundation
# Author: Barry Warsaw # Author: Barry Warsaw
# Contact: email-sig@python.org # Contact: email-sig@python.org
"""Class representing image/* type MIME documents.""" """Class representing image/* type MIME documents."""
__all__ = ['MIMEImage']
import imghdr import imghdr
from email import Errors from email import encoders
from email import Encoders from email.mime.nonmultipart import MIMENonMultipart
from email.MIMENonMultipart import MIMENonMultipart
...@@ -16,7 +17,7 @@ class MIMEImage(MIMENonMultipart): ...@@ -16,7 +17,7 @@ class MIMEImage(MIMENonMultipart):
"""Class for generating image/* type MIME documents.""" """Class for generating image/* type MIME documents."""
def __init__(self, _imagedata, _subtype=None, def __init__(self, _imagedata, _subtype=None,
_encoder=Encoders.encode_base64, **_params): _encoder=encoders.encode_base64, **_params):
"""Create an image/* type MIME document. """Create an image/* type MIME document.
_imagedata is a string containing the raw image data. If this data _imagedata is a string containing the raw image data. If this data
......
# Copyright (C) 2001-2004 Python Software Foundation # Copyright (C) 2001-2006 Python Software Foundation
# Author: Barry Warsaw # Author: Barry Warsaw
# Contact: email-sig@python.org # Contact: email-sig@python.org
"""Class representing message/* MIME documents.""" """Class representing message/* MIME documents."""
from email import Message __all__ = ['MIMEMessage']
from email.MIMENonMultipart import MIMENonMultipart
from email import message
from email.mime.nonmultipart import MIMENonMultipart
...@@ -23,10 +25,10 @@ class MIMEMessage(MIMENonMultipart): ...@@ -23,10 +25,10 @@ class MIMEMessage(MIMENonMultipart):
the term "rfc822" is technically outdated by RFC 2822). the term "rfc822" is technically outdated by RFC 2822).
""" """
MIMENonMultipart.__init__(self, 'message', _subtype) 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') raise TypeError('Argument is not an instance of Message')
# It's convenient to use this base class method. We need to do it # It's convenient to use this base class method. We need to do it
# this way or we'll get an exception # 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 # And be sure our default type is set correctly
self.set_default_type('message/rfc822') self.set_default_type('message/rfc822')
# Copyright (C) 2002-2004 Python Software Foundation # Copyright (C) 2002-2006 Python Software Foundation
# Author: Barry Warsaw # Author: Barry Warsaw
# Contact: email-sig@python.org # Contact: email-sig@python.org
"""Base class for MIME multipart/* type messages.""" """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.""" """Base class for MIME multipart/* type messages."""
def __init__(self, _subtype='mixed', boundary=None, _subparts=None, def __init__(self, _subtype='mixed', boundary=None, _subparts=None,
...@@ -31,7 +33,7 @@ class MIMEMultipart(MIMEBase.MIMEBase): ...@@ -31,7 +33,7 @@ class MIMEMultipart(MIMEBase.MIMEBase):
Additional parameters for the Content-Type header are taken from the Additional parameters for the Content-Type header are taken from the
keyword arguments (or passed into the _params argument). keyword arguments (or passed into the _params argument).
""" """
MIMEBase.MIMEBase.__init__(self, 'multipart', _subtype, **_params) MIMEBase.__init__(self, 'multipart', _subtype, **_params)
if _subparts: if _subparts:
for p in _subparts: for p in _subparts:
self.attach(p) self.attach(p)
......
# Copyright (C) 2002-2004 Python Software Foundation # Copyright (C) 2002-2006 Python Software Foundation
# Author: Barry Warsaw # Author: Barry Warsaw
# Contact: email-sig@python.org # Contact: email-sig@python.org
"""Base class for MIME type messages that are not multipart.""" """Base class for MIME type messages that are not multipart."""
from email import Errors __all__ = ['MIMENonMultipart']
from email import MIMEBase
from email import errors
from email.mime.base import MIMEBase
class MIMENonMultipart(MIMEBase.MIMEBase): class MIMENonMultipart(MIMEBase):
"""Base class for MIME multipart/* type messages.""" """Base class for MIME multipart/* type messages."""
__pychecker__ = 'unusednames=payload' __pychecker__ = 'unusednames=payload'
...@@ -18,7 +20,7 @@ class MIMENonMultipart(MIMEBase.MIMEBase): ...@@ -18,7 +20,7 @@ class MIMENonMultipart(MIMEBase.MIMEBase):
# The public API prohibits attaching multiple subparts to MIMEBase # The public API prohibits attaching multiple subparts to MIMEBase
# derived subtypes since none of them are, by definition, of content # derived subtypes since none of them are, by definition, of content
# type multipart/* # type multipart/*
raise Errors.MultipartConversionError( raise errors.MultipartConversionError(
'Cannot attach additional subparts to non-multipart/*') 'Cannot attach additional subparts to non-multipart/*')
del __pychecker__ del __pychecker__
# Copyright (C) 2001-2004 Python Software Foundation # Copyright (C) 2001-2006 Python Software Foundation
# Author: Barry Warsaw # Author: Barry Warsaw
# Contact: email-sig@python.org # Contact: email-sig@python.org
"""Class representing text/* type MIME documents.""" """Class representing text/* type MIME documents."""
from email.MIMENonMultipart import MIMENonMultipart __all__ = ['MIMEText']
from email.Encoders import encode_7or8bit
from email.encoders import encode_7or8bit
from email.mime.nonmultipart import MIMENonMultipart
......
# Copyright (C) 2001-2004 Python Software Foundation # Copyright (C) 2001-2006 Python Software Foundation
# Author: Barry Warsaw, Thomas Wouters, Anthony Baxter # Author: Barry Warsaw, Thomas Wouters, Anthony Baxter
# Contact: email-sig@python.org # Contact: email-sig@python.org
"""A parser of RFC 2822 and MIME email messages.""" """A parser of RFC 2822 and MIME email messages."""
__all__ = ['Parser', 'HeaderParser']
import warnings import warnings
from cStringIO import StringIO from cStringIO import StringIO
from email.FeedParser import FeedParser
from email.Message import Message from email.feedparser import FeedParser
from email.message import Message
......
# Copyright (C) 2001-2004 Python Software Foundation # Copyright (C) 2001-2006 Python Software Foundation
# Author: Ben Gertzfield # Author: Ben Gertzfield
# Contact: email-sig@python.org # Contact: email-sig@python.org
...@@ -26,9 +26,27 @@ does dumb encoding and decoding. To deal with the various line ...@@ -26,9 +26,27 @@ does dumb encoding and decoding. To deal with the various line
wrapping issues, use the email.Header module. 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 import re
from string import hexdigits from string import hexdigits
from email.Utils import fix_eols from email.utils import fix_eols
CRLF = '\r\n' CRLF = '\r\n'
NL = '\n' NL = '\n'
......
This diff is collapsed.
...@@ -10,6 +10,13 @@ from email.Charset import Charset ...@@ -10,6 +10,13 @@ from email.Charset import Charset
from email.Header import Header, decode_header from email.Header import Header, decode_header
from email.Message import Message 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): class TestEmailAsianCodecs(TestEmailBase):
......
# 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')
This diff is collapsed.
# Copyright (C) 2001-2004 Python Software Foundation # Copyright (C) 2001-2006 Python Software Foundation
# Author: Barry Warsaw # Author: Barry Warsaw
# Contact: email-sig@python.org # Contact: email-sig@python.org
"""Miscellaneous utilities.""" """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 os
import re import re
import time import time
...@@ -24,7 +39,7 @@ from email._parseaddr import parsedate_tz as _parsedate_tz ...@@ -24,7 +39,7 @@ from email._parseaddr import parsedate_tz as _parsedate_tz
from quopri import decodestring as _qdecode from quopri import decodestring as _qdecode
# Intrapackage imports # Intrapackage imports
from email.Encoders import _bencode, _qencode from email.encoders import _bencode, _qencode
COMMASPACE = ', ' COMMASPACE = ', '
EMPTYSTRING = '' EMPTYSTRING = ''
......
...@@ -170,7 +170,7 @@ class PyclbrTest(TestCase): ...@@ -170,7 +170,7 @@ class PyclbrTest(TestCase):
cm('pydoc') cm('pydoc')
# Tests for modules inside packages # Tests for modules inside packages
cm('email.Parser') cm('email.parser')
cm('test.test_pyclbr') cm('test.test_pyclbr')
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment