Kaydet (Commit) 29d1bc08 authored tarafından R David Murray's avatar R David Murray

#24277: The new email API is no longer provisional.

This is a wholesale reorganization and editing of the email documentation to
make the new API the standard one, and the old API the 'legacy' one.  The
default is still the compat32 policy, for backward compatibility.  We will
change that eventually.
üst 23e86337
#!/usr/bin/env python3
import smtplib
from email.message import EmailMessage
from email.headerregistry import Address
from email.utils import make_msgid
# Create the base text message.
msg = EmailMessage()
msg['Subject'] = "Ayons asperges pour le déjeuner"
msg['From'] = Address("Pepé Le Pew", "pepe", "example.com")
msg['To'] = (Address("Penelope Pussycat", "penelope", "example.com"),
Address("Fabrette Pussycat", "fabrette", "example.com"))
msg.set_content("""\
Salut!
Cela ressemble à un excellent recipie[1] déjeuner.
[1] http://www.yummly.com/recipe/Roasted-Asparagus-Epicurious-203718
--Pepé
""")
# Add the html version. This converts the message into a multipart/alternative
# container, with the original text message as the first part and the new html
# message as the second part.
asparagus_cid = make_msgid()
msg.add_alternative("""\
<html>
<head></head>
<body>
<p>Salut!<\p>
<p>Cela ressemble à un excellent
<a href="http://www.yummly.com/recipe/Roasted-Asparagus-Epicurious-203718>
recipie
</a> déjeuner.
</p>
<img src="cid:{asparagus_cid}" \>
</body>
</html>
""".format(asparagus_cid=asparagus_cid[1:-1]), subtype='html')
# note that we needed to peel the <> off the msgid for use in the html.
# Now add the related image to the html part.
with open("roasted-asparagus.jpg", 'rb') as img:
msg.get_payload()[1].add_related(img.read(), 'image', 'jpeg',
cid=asparagus_cid)
# Make a local copy of what we are going to send.
with open('outgoing.msg', 'wb') as f:
f.write(bytes(msg))
# Send the message via local SMTP server.
with smtplib.SMTP('localhost') as s:
s.send_message(msg)
......@@ -2,47 +2,55 @@
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
# me == my email address
# you == recipient's email address
me = "my@email.com"
you = "your@email.com"
# Create message container - the correct MIME type is multipart/alternative.
msg = MIMEMultipart('alternative')
msg['Subject'] = "Link"
msg['From'] = me
msg['To'] = you
# Create the body of the message (a plain-text and an HTML version).
text = "Hi!\nHow are you?\nHere is the link you wanted:\nhttps://www.python.org"
html = """\
from email.message import EmailMessage
from email.headerregistry import Address
from email.utils import make_msgid
# Create the base text message.
msg = EmailMessage()
msg['Subject'] = "Ayons asperges pour le déjeuner"
msg['From'] = Address("Pepé Le Pew", "pepe", "example.com")
msg['To'] = (Address("Penelope Pussycat", "penelope", "example.com"),
Address("Fabrette Pussycat", "fabrette", "example.com"))
msg.set_content("""\
Salut!
Cela ressemble à un excellent recipie[1] déjeuner.
[1] http://www.yummly.com/recipe/Roasted-Asparagus-Epicurious-203718
--Pepé
""")
# Add the html version. This converts the message into a multipart/alternative
# container, with the original text message as the first part and the new html
# message as the second part.
asparagus_cid = make_msgid()
msg.add_alternative("""\
<html>
<head></head>
<body>
<p>Hi!<br>
How are you?<br>
Here is the <a href="https://www.python.org">link</a> you wanted.
<p>Salut!<\p>
<p>Cela ressemble à un excellent
<a href="http://www.yummly.com/recipe/Roasted-Asparagus-Epicurious-203718>
recipie
</a> déjeuner.
</p>
<img src="cid:{asparagus_cid}" \>
</body>
</html>
"""
""".format(asparagus_cid=asparagus_cid[1:-1]), subtype='html')
# note that we needed to peel the <> off the msgid for use in the html.
# Record the MIME types of both parts - text/plain and text/html.
part1 = MIMEText(text, 'plain')
part2 = MIMEText(html, 'html')
# Now add the related image to the html part.
with open("roasted-asparagus.jpg", 'rb') as img:
msg.get_payload()[1].add_related(img.read(), 'image', 'jpeg',
cid=asparagus_cid)
# Attach parts into message container.
# According to RFC 2046, the last part of a multipart message, in this case
# the HTML message, is best and preferred.
msg.attach(part1)
msg.attach(part2)
# Make a local copy of what we are going to send.
with open('outgoing.msg', 'wb') as f:
f.write(bytes(msg))
# Send the message via local SMTP server.
s = smtplib.SMTP('localhost')
# sendmail function takes 3 arguments: sender's address, recipient's address
# and message to send - here it is sent as one string.
s.sendmail(me, you, msg.as_string())
s.quit()
with smtplib.SMTP('localhost') as s:
s.send_message(msg)
......@@ -3,22 +3,14 @@
"""Send the contents of a directory as a MIME message."""
import os
import sys
import smtplib
# For guessing MIME type based on file name extension
import mimetypes
from argparse import ArgumentParser
from email import encoders
from email.message import Message
from email.mime.audio import MIMEAudio
from email.mime.base import MIMEBase
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
COMMASPACE = ', '
from email.message import EmailMessage
from email.policy import SMTP
def main():
......@@ -47,12 +39,12 @@ must be running an SMTP server.
directory = args.directory
if not directory:
directory = '.'
# Create the enclosing (outer) message
outer = MIMEMultipart()
outer['Subject'] = 'Contents of directory %s' % os.path.abspath(directory)
outer['To'] = COMMASPACE.join(args.recipients)
outer['From'] = args.sender
outer.preamble = 'You will not see this in a MIME-aware mail reader.\n'
# Create the message
msg = EmailMessage()
msg['Subject'] = 'Contents of directory %s' % os.path.abspath(directory)
msg['To'] = ', '.join(args.recipients)
msg['From'] = args.sender
msg.preamble = 'You will not see this in a MIME-aware mail reader.\n'
for filename in os.listdir(directory):
path = os.path.join(directory, filename)
......@@ -67,33 +59,18 @@ must be running an SMTP server.
# use a generic bag-of-bits type.
ctype = 'application/octet-stream'
maintype, subtype = ctype.split('/', 1)
if maintype == 'text':
with open(path) as fp:
# Note: we should handle calculating the charset
msg = MIMEText(fp.read(), _subtype=subtype)
elif maintype == 'image':
with open(path, 'rb') as fp:
msg = MIMEImage(fp.read(), _subtype=subtype)
elif maintype == 'audio':
with open(path, 'rb') as fp:
msg = MIMEAudio(fp.read(), _subtype=subtype)
else:
with open(path, 'rb') as fp:
msg = MIMEBase(maintype, subtype)
msg.set_payload(fp.read())
# Encode the payload using Base64
encoders.encode_base64(msg)
# Set the filename parameter
msg.add_header('Content-Disposition', 'attachment', filename=filename)
outer.attach(msg)
with open(path, 'rb') as fp:
msg.add_attachment(fp.read(),
maintype=maintype,
subtype=subtype,
filename=filename)
# Now send or store the message
composed = outer.as_string()
if args.output:
with open(args.output, 'w') as fp:
fp.write(composed)
with open(args.output, 'wb') as fp:
fp.write(msg.as_bytes(policy=SMTP))
else:
with smtplib.SMTP('localhost') as s:
s.sendmail(args.sender, args.recipients, composed)
s.send_message(msg)
if __name__ == '__main__':
......
# Import the email modules we'll need
from email.parser import Parser
from email.parser import BytesParser, Parser
from email.policy import default
# If the e-mail headers are in a file, uncomment these two lines:
# with open(messagefile) as fp:
# headers = Parser().parse(fp)
# with open(messagefile, 'rb') as fp:
# headers = BytesParser(policy=default).parse(fp)
# Or for parsing headers in a string, use:
headers = Parser().parsestr('From: <user@example.com>\n'
# Or for parsing headers in a string (this is an uncommon operation), use:
headers = Parser(policy=default).parsestr(
'From: Foo Bar <user@example.com>\n'
'To: <someone_else@example.com>\n'
'Subject: Test message\n'
'\n'
'Body would go here\n')
# Now the header items can be accessed as a dictionary:
print('To: %s' % headers['to'])
print('From: %s' % headers['from'])
print('Subject: %s' % headers['subject'])
print('To: {}'.format(headers['to']))
print('From: {}'.format(headers['from']))
print('Subject: {}'.format(headers['subject']))
# You can also access the parts of the addresses:
print('Recipient username: {}'.format(headers['to'].addresses[0].username))
print('Sender name: {}'.format(headers['from'].addresses[0].display_name))
# Import smtplib for the actual sending function
import smtplib
# Here are the email package modules we'll need
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart
# And imghdr to find the types of our images
import imghdr
COMMASPACE = ', '
# Here are the email package modules we'll need
from email.message import EmailMessage
# Create the container (outer) email message.
msg = MIMEMultipart()
# Create the container email message.
msg = EmailMessage()
msg['Subject'] = 'Our family reunion'
# me == the sender's email address
# family = the list of all recipients' email addresses
msg['From'] = me
msg['To'] = COMMASPACE.join(family)
msg['To'] = ', '.join(family)
msg.preamble = 'Our family reunion'
# Assume we know that the image files are all in PNG format
# Open the files in binary mode. Use imghdr to figure out the
# MIME subtype for each specific image.
for file in pngfiles:
# Open the files in binary mode. Let the MIMEImage class automatically
# guess the specific image type.
with open(file, 'rb') as fp:
img = MIMEImage(fp.read())
msg.attach(img)
img_data = fp.read()
msg.add_attachment(img_data, maintype='image',
subtype=imghdr.what(None, img_data))
# Send the email via our own SMTP server.
s = smtplib.SMTP('localhost')
s.send_message(msg)
s.quit()
with smtplib.SMTP('localhost') as s:
s.send_message(msg)
......@@ -2,13 +2,13 @@
import smtplib
# Import the email modules we'll need
from email.mime.text import MIMEText
from email.message import EmailMessage
# Open a plain text file for reading. For this example, assume that
# the text file contains only ASCII characters.
# Open the plain text file whose name is in textfile for reading.
with open(textfile) as fp:
# Create a text/plain message
msg = MIMEText(fp.read())
msg = EmailMessage()
msg.set_content(fp.read())
# me == the sender's email address
# you == the recipient's email address
......
......@@ -3,11 +3,11 @@
"""Unpack a MIME message into a directory of files."""
import os
import sys
import email
import errno
import mimetypes
from email.policy import default
from argparse import ArgumentParser
......@@ -22,8 +22,8 @@ Unpack a MIME message into a directory of files.
parser.add_argument('msgfile')
args = parser.parse_args()
with open(args.msgfile) as fp:
msg = email.message_from_file(fp)
with open(args.msgfile, 'rb') as fp:
msg = email.message_from_binary_file(fp, policy=default)
try:
os.mkdir(args.directory)
......
......@@ -8,6 +8,11 @@
--------------
This module is part of the legacy (``Compat32``) email API. In the new
API only the aliases table is used.
The remaining text in this section is the original documentation of the module.
This module provides a class :class:`Charset` for representing character sets
and character set conversions in email messages, as well as a character set
registry and several convenience methods for manipulating this registry.
......
This diff is collapsed.
......@@ -8,6 +8,12 @@
--------------
This module is part of the legacy (``Compat32``) email API. In the
new API the functionality is provided by the *cte* parameter of
the :meth:`~email.message.EmailMessage.set_content` method.
The remaining text in this section is the original documentation of the module.
When creating :class:`~email.message.Message` objects from scratch, you often
need to encode the payloads for transport through compliant mail servers. This
is especially true for :mimetype:`image/\*` and :mimetype:`text/\*` type messages
......
......@@ -20,33 +20,27 @@ The following exception classes are defined in the :mod:`email.errors` module:
.. exception:: MessageParseError()
This is the base class for exceptions raised by the :class:`~email.parser.Parser`
class. It is derived from :exc:`MessageError`.
This is the base class for exceptions raised by the
:class:`~email.parser.Parser` class. It is derived from
:exc:`MessageError`. This class is also used internally by the parser used
by :mod:`~email.headerregistry`.
.. exception:: HeaderParseError()
Raised under some error conditions when parsing the :rfc:`2822` headers of a
message, this class is derived from :exc:`MessageParseError`. It can be raised
from the :meth:`Parser.parse <email.parser.Parser.parse>` or
:meth:`Parser.parsestr <email.parser.Parser.parsestr>` methods.
Situations where it can be raised include finding an envelope header after the
first :rfc:`2822` header of the message, finding a continuation line before the
first :rfc:`2822` header is found, or finding a line in the headers which is
neither a header or a continuation line.
Raised under some error conditions when parsing the :rfc:`5322` headers of a
message, this class is derived from :exc:`MessageParseError`. The
:meth:`~email.message.EmailMessage.set_boundary` method will raise this
error if the content type is unknown when the method is called.
:class:`~email.header.Header` may raise this error for certain base64
decoding errors, and when an attempt is made to create a header that appears
to contain an embedded header (that is, there is what is supposed to be a
continuation line that has no leading whitespace and looks like a header).
.. exception:: BoundaryError()
Raised under some error conditions when parsing the :rfc:`2822` headers of a
message, this class is derived from :exc:`MessageParseError`. It can be raised
from the :meth:`Parser.parse <email.parser.Parser.parse>` or
:meth:`Parser.parsestr <email.parser.Parser.parsestr>` methods.
Situations where it can be raised include not being able to find the starting or
terminating boundary in a :mimetype:`multipart/\*` message when strict parsing
is used.
Deprecated and no longer used.
.. exception:: MultipartConversionError()
......@@ -64,14 +58,14 @@ The following exception classes are defined in the :mod:`email.errors` module:
:class:`~email.mime.nonmultipart.MIMENonMultipart` (e.g.
:class:`~email.mime.image.MIMEImage`).
Here's the list of the defects that the :class:`~email.parser.FeedParser`
Here is the list of the defects that the :class:`~email.parser.FeedParser`
can find while parsing messages. Note that the defects are added to the message
where the problem was found, so for example, if a message nested inside a
:mimetype:`multipart/alternative` had a malformed header, that nested message
object would have a defect, but the containing messages would not.
All defect classes are subclassed from :class:`email.errors.MessageDefect`, but
this class is *not* an exception!
All defect classes are subclassed from :class:`email.errors.MessageDefect`.
* :class:`NoBoundaryInMultipartDefect` -- A message claimed to be a multipart,
but had no :mimetype:`boundary` parameter.
......
......@@ -6,13 +6,14 @@
Here are a few examples of how to use the :mod:`email` package to read, write,
and send simple email messages, as well as more complex MIME messages.
First, let's see how to create and send a simple text message:
First, let's see how to create and send a simple text message (both the
text content and the addresses may contain unicode characters):
.. literalinclude:: ../includes/email-simple.py
And parsing RFC822 headers can easily be done by the parse(filename) or
parsestr(message_as_string) methods of the Parser() class:
Parsing RFC822 headers can easily be done by the using the classes
from the :mod:`~email.parser` module:
.. literalinclude:: ../includes/email-headers.py
......@@ -34,30 +35,19 @@ above, into a directory of files:
.. literalinclude:: ../includes/email-unpack.py
Here's an example of how to create an HTML message with an alternative plain
text version: [2]_
text version. To make things a bit more interesting, we include a related
image in the html part, and we save a copy of what we are going to send to
disk, as well as sending it.
.. literalinclude:: ../includes/email-alternative.py
.. _email-contentmanager-api-examples:
Examples using the Provisional API
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Here is a reworking of the last example using the provisional API. To make
things a bit more interesting, we include a related image in the html part, and
we save a copy of what we are going to send to disk, as well as sending it.
This example also shows how easy it is to include non-ASCII, and simplifies the
sending of the message using the :meth:`.send_message` method of the
:mod:`smtplib` module.
.. literalinclude:: ../includes/email-alternative-new-api.py
If we were instead sent the message from the last example, here is one
way we could process it:
If we were sent the message from the last example, here is one way we could
process it:
.. literalinclude:: ../includes/email-read-alternative-new-api.py
.. literalinclude:: ../includes/email-read-alternative.py
Up to the prompt, the output from the above is:
......@@ -75,4 +65,3 @@ Up to the prompt, the output from the above is:
.. rubric:: Footnotes
.. [1] Thanks to Matthew Dixon Cowles for the original inspiration and examples.
.. [2] Contributed by Martin Matejek.
This diff is collapsed.
......@@ -8,6 +8,14 @@
--------------
This module is part of the legacy (``Compat32``) email API. In the current API
encoding and decoding of headers is handled transparently by the
dictionary-like API of the :class:`~email.message.EmailMessage` class. In
addition to uses in legacy code, this module can be useful in applications that
need to completely control the character sets used when encoding headers.
The remaining text in this section is the original documentation of the module.
:rfc:`2822` is the base standard that describes the format of email messages.
It derives from the older :rfc:`822` standard which came into widespread use at
a time when most email was composed of ASCII characters only. :rfc:`2822` is a
......
......@@ -7,19 +7,13 @@
.. moduleauthor:: R. David Murray <rdmurray@bitdance.com>
.. sectionauthor:: R. David Murray <rdmurray@bitdance.com>
.. versionadded:: 3.3
as a :term:`provisional module <provisional package>`.
**Source code:** :source:`Lib/email/headerregistry.py`
.. note::
--------------
The headerregistry module has been included in the standard library on a
:term:`provisional basis <provisional package>`. Backwards incompatible
changes (up to and including removal of the module) may occur if deemed
necessary by the core developers.
.. versionadded:: 3.3 as a :term:`provisional module <provisional package>`.
--------------
.. versionchanged:: 3.6 provisonal status removed.
Headers are represented by customized subclasses of :class:`str`. The
particular class used to represent a given header is determined by the
......@@ -86,10 +80,11 @@ headers.
.. method:: fold(*, policy)
Return a string containing :attr:`~email.policy.Policy.linesep`
characters as required to correctly fold the header according
to *policy*. A :attr:`~email.policy.Policy.cte_type` of
``8bit`` will be treated as if it were ``7bit``, since strings
may not contain binary data.
characters as required to correctly fold the header according to
*policy*. A :attr:`~email.policy.Policy.cte_type` of ``8bit`` will be
treated as if it were ``7bit``, since headers may not contain arbitrary
binary data. If :attr:`~email.policy.EmailPolicy.utf8` is ``False``,
non-ASCII data will be :rfc:`2047` encoded.
``BaseHeader`` by itself cannot be used to create a header object. It
......@@ -106,7 +101,7 @@ headers.
values for at least the keys ``decoded`` and ``defects``. ``decoded``
should be the string value for the header (that is, the header value fully
decoded to unicode). The parse method should assume that *string* may
contain transport encoded parts, but should correctly handle all valid
contain content-transfer-encoded parts, but should correctly handle all valid
unicode characters as well so that it can parse un-encoded header values.
``BaseHeader``'s ``__new__`` then creates the header instance, and calls its
......@@ -135,11 +130,10 @@ headers.
mechanism for encoding non-ASCII text as ASCII characters within a header
value. When a *value* containing encoded words is passed to the
constructor, the ``UnstructuredHeader`` parser converts such encoded words
back in to the original unicode, following the :rfc:`2047` rules for
unstructured text. The parser uses heuristics to attempt to decode certain
non-compliant encoded words. Defects are registered in such cases, as well
as defects for issues such as invalid characters within the encoded words or
the non-encoded text.
into unicode, following the :rfc:`2047` rules for unstructured text. The
parser uses heuristics to attempt to decode certain non-compliant encoded
words. Defects are registered in such cases, as well as defects for issues
such as invalid characters within the encoded words or the non-encoded text.
This header type provides no additional attributes.
......@@ -213,15 +207,16 @@ headers.
the list of addresses is "flattened" into a one dimensional list).
The ``decoded`` value of the header will have all encoded words decoded to
unicode. :class:`~encodings.idna` encoded domain names are also decoded to unicode. The
``decoded`` value is set by :attr:`~str.join`\ ing the :class:`str` value of
the elements of the ``groups`` attribute with ``', '``.
unicode. :class:`~encodings.idna` encoded domain names are also decoded to
unicode. The ``decoded`` value is set by :attr:`~str.join`\ ing the
:class:`str` value of the elements of the ``groups`` attribute with ``',
'``.
A list of :class:`.Address` and :class:`.Group` objects in any combination
may be used to set the value of an address header. ``Group`` objects whose
``display_name`` is ``None`` will be interpreted as single addresses, which
allows an address list to be copied with groups intact by using the list
obtained ``groups`` attribute of the source header.
obtained from the ``groups`` attribute of the source header.
.. class:: SingleAddressHeader
......@@ -267,7 +262,7 @@ variant, :attr:`~.BaseHeader.max_count` is set to 1.
.. class:: ParameterizedMIMEHeader
MOME headers all start with the prefix 'Content-'. Each specific header has
MIME headers all start with the prefix 'Content-'. Each specific header has
a certain value, described under the class for that header. Some can
also take a list of supplemental parameters, which have a common format.
This class serves as a base for all the MIME headers that take parameters.
......
This diff is collapsed.
......@@ -8,6 +8,11 @@
--------------
This module is part of the legacy (``Compat32``) email API. Its functionality
is partially replaced by the :mod:`~email.contentmanager` in the new API, but
in certain applications these classes may still be useful, even in non-legacy
code.
Ordinarily, you get a message object structure by passing a file or some text to
a parser, which parses the text and returns the root message object. However
you can also build a complete message structure from scratch, or even individual
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -8,7 +8,43 @@
--------------
There are several useful utilities provided in the :mod:`email.utils` module:
There are a couple of useful utilities provided in the :mod:`email.utils`
module:
.. function:: localtime(dt=None)
Return local time as an aware datetime object. If called without
arguments, return current time. Otherwise *dt* argument should be a
:class:`~datetime.datetime` instance, and it is converted to the local time
zone according to the system time zone database. If *dt* is naive (that
is, ``dt.tzinfo`` is ``None``), it is assumed to be in local time. In this
case, a positive or zero value for *isdst* causes ``localtime`` to presume
initially that summer time (for example, Daylight Saving Time) is or is not
(respectively) in effect for the specified time. A negative value for
*isdst* causes the ``localtime`` to attempt to divine whether summer time
is in effect for the specified time.
.. versionadded:: 3.3
.. function:: make_msgid(idstring=None, domain=None)
Returns a string suitable for an :rfc:`2822`\ -compliant
:mailheader:`Message-ID` header. Optional *idstring* if given, is a string
used to strengthen the uniqueness of the message id. Optional *domain* if
given provides the portion of the msgid after the '@'. The default is the
local hostname. It is not normally necessary to override this default, but
may be useful certain cases, such as a constructing distributed system that
uses a consistent domain name across multiple hosts.
.. versionchanged:: 3.2
Added the *domain* keyword.
The remaining functions are part of the legacy (``Compat32``) email API. There
is no need to directly use these with the new API, since the parsing and
formatting they provide is done automatically by the header parsing machinery
of the new API.
.. function:: quote(str)
......@@ -141,36 +177,6 @@ There are several useful utilities provided in the :mod:`email.utils` module:
.. versionadded:: 3.3
.. function:: localtime(dt=None)
Return local time as an aware datetime object. If called without
arguments, return current time. Otherwise *dt* argument should be a
:class:`~datetime.datetime` instance, and it is converted to the local time
zone according to the system time zone database. If *dt* is naive (that
is, ``dt.tzinfo`` is ``None``), it is assumed to be in local time. In this
case, a positive or zero value for *isdst* causes ``localtime`` to presume
initially that summer time (for example, Daylight Saving Time) is or is not
(respectively) in effect for the specified time. A negative value for
*isdst* causes the ``localtime`` to attempt to divine whether summer time
is in effect for the specified time.
.. versionadded:: 3.3
.. function:: make_msgid(idstring=None, domain=None)
Returns a string suitable for an :rfc:`2822`\ -compliant
:mailheader:`Message-ID` header. Optional *idstring* if given, is a string
used to strengthen the uniqueness of the message id. Optional *domain* if
given provides the portion of the msgid after the '@'. The default is the
local hostname. It is not normally necessary to override this default, but
may be useful certain cases, such as a constructing distributed system that
uses a consistent domain name across multiple hosts.
.. versionchanged:: 3.2
Added the *domain* keyword.
.. function:: decode_rfc2231(s)
Decode the string *s* according to :rfc:`2231`.
......
......@@ -951,6 +951,26 @@ class MIMEPart(Message):
policy = default
Message.__init__(self, policy)
def as_string(self, unixfrom=False, maxheaderlen=None, policy=None):
"""Return the entire formatted message as a string.
Optional 'unixfrom', when true, means include the Unix From_ envelope
header. maxheaderlen is retained for backward compatibility with the
base Message class, but defaults to None, meaning that the policy value
for max_line_length controls the header maximum length. 'policy' is
passed to the Generator instance used to serialize the mesasge; if it
is not specified the policy associated with the message instance is
used.
"""
policy = self.policy if policy is None else policy
if maxheaderlen is None:
maxheaderlen = policy.max_line_length
return super().as_string(maxheaderlen=maxheaderlen, policy=policy)
def __str__(self):
return self.as_string(policy=self.policy.clone(utf8=True))
def is_attachment(self):
c_d = self.get('content-disposition')
return False if c_d is None else c_d.content_disposition == 'attachment'
......
......@@ -764,6 +764,26 @@ class TestEmailMessage(TestEmailMessageBase, TestEmailBase):
m.set_content(content_manager=cm)
self.assertEqual(m['MIME-Version'], '1.0')
def test_as_string_uses_max_header_length_by_default(self):
m = self._str_msg('Subject: long line' + ' ab'*50 + '\n\n')
self.assertEqual(len(m.as_string().strip().splitlines()), 3)
def test_as_string_allows_maxheaderlen(self):
m = self._str_msg('Subject: long line' + ' ab'*50 + '\n\n')
self.assertEqual(len(m.as_string(maxheaderlen=0).strip().splitlines()),
1)
self.assertEqual(len(m.as_string(maxheaderlen=34).strip().splitlines()),
6)
def test_str_defaults_to_policy_max_line_length(self):
m = self._str_msg('Subject: long line' + ' ab'*50 + '\n\n')
self.assertEqual(len(str(m).strip().splitlines()), 3)
def test_str_defaults_to_utf8(self):
m = EmailMessage()
m['Subject'] = 'unicöde'
self.assertEqual(str(m), 'Subject: unicöde\n\n')
class TestMIMEPart(TestEmailMessageBase, TestEmailBase):
# Doing the full test run here may seem a bit redundant, since the two
......
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