diff -r e0a90b1c4cdf Lib/email/generator.py --- a/Lib/email/generator.py Fri Feb 07 15:04:26 2014 -0500 +++ b/Lib/email/generator.py Sat Feb 08 11:37:47 2014 +0800 @@ -62,6 +62,8 @@ self._mangle_from_ = mangle_from_ self.maxheaderlen = maxheaderlen self.policy = policy + self.replace_cte_after_flatten = False + self.orig_cte = None def write(self, s): # Just delegate to the file object @@ -113,6 +115,9 @@ finally: self.policy = old_gen_policy msg.policy = old_msg_policy + if self.replace_cte_after_flatten: + msg.replace_header('content-transfer-encoding', + self.orig_cte) def clone(self, fp): """Clone this generator with the exact same options.""" @@ -222,12 +227,21 @@ return if not isinstance(payload, str): raise TypeError('string payload expected: %s' % type(payload)) + self.replace_cte_after_flatten = False if _has_surrogates(msg._payload): charset = msg.get_param('charset') if charset is not None: - del msg['content-transfer-encoding'] - msg.set_payload(payload, charset) - payload = msg.get_payload() + orig_payload = msg._payload + self.orig_cte = msg['content-transfer-encoding'] + try: + del msg['content-transfer-encoding'] + msg.set_payload(payload, charset) + payload = msg.get_payload() + finally: + cte = msg['content-transfer-encoding'] + msg._payload = orig_payload + if self.orig_cte == '8bit' and cte == 'base64': + self.replace_cte_after_flatten = True if self._mangle_from_: payload = fcre.sub('>From ', payload) self._write_lines(payload) diff -r e0a90b1c4cdf Lib/test/test_email/test_email.py --- a/Lib/test/test_email/test_email.py Fri Feb 07 15:04:26 2014 -0500 +++ b/Lib/test/test_email/test_email.py Sat Feb 08 11:37:47 2014 +0800 @@ -3782,6 +3782,16 @@ email.generator.Generator(out).flatten(msg) self.assertEqual(out.getvalue(), self.non_latin_bin_msg_as7bit_wrapped) + def test_str_generator_should_not_mutate_msg_when_handling_8bit(self): + msg = email.message_from_bytes(self.non_latin_bin_msg) + out = BytesIO() + BytesGenerator(out).flatten(msg) + orig_value = out.getvalue() + Generator(StringIO()).flatten(msg) # Should not mutate msg! + out = BytesIO() + BytesGenerator(out).flatten(msg) + self.assertEqual(out.getvalue(), orig_value) + def test_bytes_generator_with_unix_from(self): # The unixfrom contains a current date, so we can't check it # literally. Just make sure the first word is 'From' and the