summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/library/email.policy.rst27
-rw-r--r--Doc/whatsnew/3.5.rst6
-rw-r--r--Lib/email/_policybase.py8
-rw-r--r--Lib/email/generator.py13
-rw-r--r--Lib/test/test_email/test_generator.py22
-rw-r--r--Lib/test/test_email/test_policy.py2
-rw-r--r--Misc/NEWS3
7 files changed, 76 insertions, 5 deletions
diff --git a/Doc/library/email.policy.rst b/Doc/library/email.policy.rst
index 9fadfb3..6e13186 100644
--- a/Doc/library/email.policy.rst
+++ b/Doc/library/email.policy.rst
@@ -187,6 +187,18 @@ added matters. To illustrate::
:const:`False` (the default), defects will be passed to the
:meth:`register_defect` method.
+
+
+ .. attribute:: mangle_from\_
+
+ If :const:`True`, lines starting with *"From "* in the body are
+ escaped by putting a ``>`` in front of them. This parameter is used when
+ the message is being serialized by a generator.
+ Default: :const:`False`.
+
+ .. versionadded:: 3.5
+ The *mangle_from_* parameter.
+
The following :class:`Policy` method is intended to be called by code using
the email library to create policy instances with custom settings:
@@ -319,6 +331,13 @@ added matters. To illustrate::
:const:`compat32`, that is used as the default policy. Thus the default
behavior of the email package is to maintain compatibility with Python 3.2.
+ The following attributes have values that are different from the
+ :class:`Policy` default:
+
+ .. attribute:: mangle_from_
+
+ The default is ``True``.
+
The class provides the following concrete implementations of the
abstract methods of :class:`Policy`:
@@ -356,6 +375,14 @@ added matters. To illustrate::
line breaks and any (RFC invalid) binary data it may contain.
+An instance of :class:`Compat32` is provided as a module constant:
+
+.. data:: compat32
+
+ An instance of :class:`Compat32`, providing backward compatibility with the
+ behavior of the email package in Python 3.2.
+
+
.. note::
The documentation below describes new policies that are included in the
diff --git a/Doc/whatsnew/3.5.rst b/Doc/whatsnew/3.5.rst
index 51a3aa3..1f8d90f 100644
--- a/Doc/whatsnew/3.5.rst
+++ b/Doc/whatsnew/3.5.rst
@@ -351,6 +351,12 @@ doctest
email
-----
+* A new policy option :attr:`~email.policy.Policy.mangle_from_` controls
+ whether or not lines that start with "From " in email bodies are prefixed with
+ a '>' character by generators. The default is ``True`` for
+ :attr:`~email.policy.compat32` and ``False`` for all other policies.
+ (Contributed by Milan Oberkirch in :issue:`20098`.)
+
* A new method :meth:`~email.message.Message.get_content_disposition` provides
easy access to a canonical value for the :mailheader:`Content-Disposition`
header (``None`` if there is no such header). (Contributed by Abhilash Raj
diff --git a/Lib/email/_policybase.py b/Lib/email/_policybase.py
index 8106114..c0d98a4 100644
--- a/Lib/email/_policybase.py
+++ b/Lib/email/_policybase.py
@@ -149,12 +149,18 @@ class Policy(_PolicyBase, metaclass=abc.ABCMeta):
during serialization. None or 0 means no line
wrapping is done. Default is 78.
+ mangle_from_ -- a flag that, when True escapes From_ lines in the
+ body of the message by putting a `>' in front of
+ them. This is used when the message is being
+ serialized by a generator. Default: True.
+
"""
raise_on_defect = False
linesep = '\n'
cte_type = '8bit'
max_line_length = 78
+ mangle_from_ = False
def handle_defect(self, obj, defect):
"""Based on policy, either raise defect or call register_defect.
@@ -266,6 +272,8 @@ class Compat32(Policy):
replicates the behavior of the email package version 5.1.
"""
+ mangle_from_ = True
+
def _sanitize_header(self, name, value):
# If the header value contains surrogates, return a Header using
# the unknown-8bit charset to encode the bytes as encoded words.
diff --git a/Lib/email/generator.py b/Lib/email/generator.py
index 4735721..11ff16d 100644
--- a/Lib/email/generator.py
+++ b/Lib/email/generator.py
@@ -32,16 +32,16 @@ class Generator:
# Public interface
#
- def __init__(self, outfp, mangle_from_=True, maxheaderlen=None, *,
+ def __init__(self, outfp, mangle_from_=None, maxheaderlen=None, *,
policy=None):
"""Create the generator for message flattening.
outfp is the output file-like object for writing the message to. It
must have a write() method.
- Optional mangle_from_ is a flag that, when True (the default), escapes
- From_ lines in the body of the message by putting a `>' in front of
- them.
+ Optional mangle_from_ is a flag that, when True (the default if policy
+ is not set), escapes From_ lines in the body of the message by putting
+ a `>' in front of them.
Optional maxheaderlen specifies the longest length for a non-continued
header. When a header line is longer (in characters, with tabs
@@ -56,6 +56,9 @@ class Generator:
flatten method is used.
"""
+
+ if mangle_from_ is None:
+ mangle_from_ = True if policy is None else policy.mangle_from_
self._fp = outfp
self._mangle_from_ = mangle_from_
self.maxheaderlen = maxheaderlen
@@ -449,7 +452,7 @@ class DecodedGenerator(Generator):
Like the Generator base class, except that non-text parts are substituted
with a format string representing the part.
"""
- def __init__(self, outfp, mangle_from_=True, maxheaderlen=78, fmt=None):
+ def __init__(self, outfp, mangle_from_=None, maxheaderlen=78, fmt=None):
"""Like Generator.__init__() except that an additional optional
argument is allowed.
diff --git a/Lib/test/test_email/test_generator.py b/Lib/test/test_email/test_generator.py
index 920f870..b1cbce2 100644
--- a/Lib/test/test_email/test_generator.py
+++ b/Lib/test/test_email/test_generator.py
@@ -140,6 +140,28 @@ class TestGeneratorBase:
g.flatten(msg, linesep='\n')
self.assertEqual(s.getvalue(), self.typ(expected))
+ def test_set_mangle_from_via_policy(self):
+ source = textwrap.dedent("""\
+ Subject: test that
+ from is mangeld in the body!
+
+ From time to time I write a rhyme.
+ """)
+ variants = (
+ (None, True),
+ (policy.compat32, True),
+ (policy.default, False),
+ (policy.default.clone(mangle_from_=True), True),
+ )
+ for p, mangle in variants:
+ expected = source.replace('From ', '>From ') if mangle else source
+ with self.subTest(policy=p, mangle_from_=mangle):
+ msg = self.msgmaker(self.typ(source))
+ s = self.ioclass()
+ g = self.genclass(s, policy=p)
+ g.flatten(msg)
+ self.assertEqual(s.getvalue(), self.typ(expected))
+
class TestGenerator(TestGeneratorBase, TestEmailBase):
diff --git a/Lib/test/test_email/test_policy.py b/Lib/test/test_email/test_policy.py
index 4b0a04e..9bb32f0 100644
--- a/Lib/test/test_email/test_policy.py
+++ b/Lib/test/test_email/test_policy.py
@@ -22,6 +22,7 @@ class PolicyAPITests(unittest.TestCase):
'linesep': '\n',
'cte_type': '8bit',
'raise_on_defect': False,
+ 'mangle_from_': True,
}
# These default values are the ones set on email.policy.default.
# If any of these defaults change, the docs must be updated.
@@ -32,6 +33,7 @@ class PolicyAPITests(unittest.TestCase):
'header_factory': email.policy.EmailPolicy.header_factory,
'refold_source': 'long',
'content_manager': email.policy.EmailPolicy.content_manager,
+ 'mangle_from_': False,
})
# For each policy under test, we give here what we expect the defaults to
diff --git a/Misc/NEWS b/Misc/NEWS
index 5ae6031..586baa1 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -47,6 +47,9 @@ Core and Builtins
Library
-------
+- Issue #20098: New mangle_from_ policy option for email, default True
+ for compat32, but False for all other policies.
+
- Issue #24211: The email library now supports RFC 6532: it can generate
headers using utf-8 instead of encoded words.