diff -r 3ae2cd85a908 Doc/library/email.policy.rst --- a/Doc/library/email.policy.rst Sun Mar 09 11:18:16 2014 +0100 +++ b/Doc/library/email.policy.rst Sun Mar 09 23:23:29 2014 +0100 @@ -187,6 +187,15 @@ :const:`False` (the default), defects will be passed to the :meth:`register_defect` method. + + .. versionadded:: 3.4 + The *mangle_from* parameter. + + .. attribute:: mangle_from + + If :const:`True`, lines starting with *"From "* in the body are escaped + by putting a ``>`` in front of them. Default: :const:`True`. + The following :class:`Policy` method is intended to be called by code using the email library to create policy instances with custom settings: diff -r 3ae2cd85a908 Lib/email/_policybase.py --- a/Lib/email/_policybase.py Sun Mar 09 11:18:16 2014 +0100 +++ b/Lib/email/_policybase.py Sun Mar 09 23:23:29 2014 +0100 @@ -149,12 +149,17 @@ 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. Default: True. + """ raise_on_defect = False linesep = '\n' cte_type = '8bit' max_line_length = 78 + mangle_from = True def handle_defect(self, obj, defect): """Based on policy, either raise defect or call register_defect. diff -r 3ae2cd85a908 Lib/email/generator.py --- a/Lib/email/generator.py Sun Mar 09 11:18:16 2014 +0100 +++ b/Lib/email/generator.py Sun Mar 09 23:23:29 2014 +0100 @@ -36,16 +36,16 @@ # 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 @@ -59,8 +59,11 @@ backward compatibility. """ + + 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._mangle_from = mangle_from self.maxheaderlen = maxheaderlen self.policy = policy @@ -118,7 +121,7 @@ def clone(self, fp): """Clone this generator with the exact same options.""" return self.__class__(fp, - self._mangle_from_, + self._mangle_from, None, # Use policy setting, which we've adjusted policy=self.policy) @@ -242,7 +245,7 @@ payload = msg.get_payload() self._munge_cte = (msg['content-transfer-encoding'], msg['content-type']) - if self._mangle_from_: + if self._mangle_from: payload = fcre.sub('>From ', payload) self._write_lines(payload) @@ -279,7 +282,7 @@ msg.set_boundary(boundary) # If there's a preamble, write it out, with a trailing CRLF if msg.preamble is not None: - if self._mangle_from_: + if self._mangle_from: preamble = fcre.sub('>From ', msg.preamble) else: preamble = msg.preamble @@ -301,7 +304,7 @@ # close-delimiter transport-padding self.write(self._NL + '--' + boundary + '--' + self._NL) if msg.epilogue is not None: - if self._mangle_from_: + if self._mangle_from: epilogue = fcre.sub('>From ', msg.epilogue) else: epilogue = msg.epilogue @@ -427,7 +430,7 @@ if msg._payload is None: return if _has_surrogates(msg._payload) and not self.policy.cte_type=='7bit': - if self._mangle_from_: + if self._mangle_from: msg._payload = fcre.sub(">From ", msg._payload) self._write_lines(msg._payload) else: @@ -450,7 +453,7 @@ 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. @@ -472,7 +475,7 @@ [Non-text (%(type)s) part of message omitted, filename %(filename)s] """ - Generator.__init__(self, outfp, mangle_from_, maxheaderlen) + Generator.__init__(self, outfp, mangle_from, maxheaderlen) if fmt is None: self._fmt = _FMT else: diff -r 3ae2cd85a908 Lib/email/message.py --- a/Lib/email/message.py Sun Mar 09 11:18:16 2014 +0100 +++ b/Lib/email/message.py Sun Mar 09 23:23:29 2014 +0100 @@ -151,7 +151,7 @@ policy = self.policy if policy is None else policy fp = StringIO() g = Generator(fp, - mangle_from_=False, + mangle_from=False, maxheaderlen=maxheaderlen, policy=policy) g.flatten(self, unixfrom=unixfrom) @@ -173,7 +173,7 @@ from email.generator import BytesGenerator policy = self.policy if policy is None else policy fp = BytesIO() - g = BytesGenerator(fp, mangle_from_=False, policy=policy) + g = BytesGenerator(fp, mangle_from=False, policy=policy) g.flatten(self, unixfrom=unixfrom) return fp.getvalue() diff -r 3ae2cd85a908 Lib/email/policy.py --- a/Lib/email/policy.py Sun Mar 09 11:18:16 2014 +0100 +++ b/Lib/email/policy.py Sun Mar 09 23:23:29 2014 +0100 @@ -75,6 +75,7 @@ refold_source = 'long' header_factory = HeaderRegistry() content_manager = raw_data_manager + mangle_from = False def __init__(self, **kw): # Ensure that each new instance gets a unique header factory diff -r 3ae2cd85a908 Lib/test/test_email/test_email.py --- a/Lib/test/test_email/test_email.py Sun Mar 09 11:18:16 2014 +0100 +++ b/Lib/test/test_email/test_email.py Sun Mar 09 23:23:29 2014 +0100 @@ -1394,7 +1394,7 @@ def test_mangled_from(self): s = StringIO() - g = Generator(s, mangle_from_=True) + g = Generator(s, mangle_from=True) g.flatten(self.msg) self.assertEqual(s.getvalue(), """\ From: aaa@bbb.org @@ -1405,7 +1405,7 @@ def test_dont_mangle_from(self): s = StringIO() - g = Generator(s, mangle_from_=False) + g = Generator(s, mangle_from=False) g.flatten(self.msg) self.assertEqual(s.getvalue(), """\ From: aaa@bbb.org @@ -1416,7 +1416,7 @@ def test_mangle_from_in_preamble_and_epilog(self): s = StringIO() - g = Generator(s, mangle_from_=True) + g = Generator(s, mangle_from=True) msg = email.message_from_string(textwrap.dedent("""\ From: foo@bar.com Mime-Version: 1.0 @@ -1447,7 +1447,7 @@ """).encode('utf-8') msg = email.message_from_bytes(source + b'From R\xc3\xb6lli\n') b = BytesIO() - g = BytesGenerator(b, mangle_from_=True) + g = BytesGenerator(b, mangle_from=True) g.flatten(msg) self.assertEqual(b.getvalue(), source + b'>From R\xc3\xb6lli\n') diff -r 3ae2cd85a908 Lib/test/test_email/test_policy.py --- a/Lib/test/test_email/test_policy.py Sun Mar 09 11:18:16 2014 +0100 +++ b/Lib/test/test_email/test_policy.py Sun Mar 09 23:23:29 2014 +0100 @@ -22,6 +22,7 @@ '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. @@ -31,6 +32,7 @@ '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 @@ -39,7 +41,8 @@ new_policy = email.policy.EmailPolicy() policies = { email.policy.compat32: make_defaults(compat32_defaults, {}), - email.policy.default: make_defaults(policy_defaults, {}), + email.policy.default: make_defaults(policy_defaults, + {'mangle_from': False}), email.policy.SMTP: make_defaults(policy_defaults, {'linesep': '\r\n'}), email.policy.HTTP: make_defaults(policy_defaults,