From 19da6df5d62bc7b0853c38eab82e6d42ec3045ec Mon Sep 17 00:00:00 2001 From: EmmetAVS Date: Sat, 19 Jul 2025 22:55:49 -0700 Subject: [PATCH 1/3] Fixed a bug where emails with `multipart/related` content did not round-trip correctly through `email.parser.parsebytes()` and `as_bytes()`. --- Lib/email/generator.py | 6 +++++- Lib/test/test_email/test_generator.py | 14 ++++++++++++++ .../2025-07-19-22-54-48.gh-issue-136686.fc2Zl0.rst | 2 ++ 3 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2025-07-19-22-54-48.gh-issue-136686.fc2Zl0.rst diff --git a/Lib/email/generator.py b/Lib/email/generator.py index ab5bd0653e440c..53bacd6a8f237b 100644 --- a/Lib/email/generator.py +++ b/Lib/email/generator.py @@ -417,7 +417,11 @@ class BytesGenerator(Generator): """ def write(self, s): - self._fp.write(s.encode('ascii', 'surrogateescape')) + if getattr(self.policy, "utf8", False): + encoded = s.encode('utf-8', 'surrogateescape') + else: + encoded = s.encode('ascii', 'surrogateescape') + self._fp.write(encoded) def _new_buffer(self): return BytesIO() diff --git a/Lib/test/test_email/test_generator.py b/Lib/test/test_email/test_generator.py index c75a842c33578e..182c090b2883de 100644 --- a/Lib/test/test_email/test_generator.py +++ b/Lib/test/test_email/test_generator.py @@ -471,6 +471,20 @@ def test_smtp_policy(self): g = BytesGenerator(s, policy=policy.SMTP) g.flatten(msg) self.assertEqual(s.getvalue(), expected) + def test_utf8_round_trip_preserves_unicode_body(self): + msg = EmailMessage() + msg['From'] = "Páolo " + msg['To'] = 'Dinsdale' + msg['Subject'] = 'Nudge nudge, wink, wink \u1F609' + msg.set_content("oh là là, know what I mean, know what I mean?") + s = io.BytesIO() + g = BytesGenerator(s, policy=policy.SMTPUTF8) + g.flatten(msg) + out = s.getvalue() + parsed = message_from_bytes(out, policy=policy.default.clone(utf8=True)) + + self.assertEqual(parsed["Subject"], 'Nudge nudge, wink, wink \u1F609') + self.assertEqual(parsed.get_body().get_content(), "oh là là, know what I mean, know what I mean?\r\n") if __name__ == '__main__': diff --git a/Misc/NEWS.d/next/Library/2025-07-19-22-54-48.gh-issue-136686.fc2Zl0.rst b/Misc/NEWS.d/next/Library/2025-07-19-22-54-48.gh-issue-136686.fc2Zl0.rst new file mode 100644 index 00000000000000..6bc213e83b3a5a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-07-19-22-54-48.gh-issue-136686.fc2Zl0.rst @@ -0,0 +1,2 @@ +Fixed a bug where emails with `multipart/related` content did not round-trip +correctly through `email.parser.parsebytes()` and `as_bytes()`. From 03c3f01f1f620f35ea782b8a2c34e455c08cb75e Mon Sep 17 00:00:00 2001 From: EmmetAVS Date: Sat, 19 Jul 2025 23:45:14 -0700 Subject: [PATCH 2/3] fix to formatting in documentation --- .../Library/2025-07-19-22-54-48.gh-issue-136686.fc2Zl0.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Misc/NEWS.d/next/Library/2025-07-19-22-54-48.gh-issue-136686.fc2Zl0.rst b/Misc/NEWS.d/next/Library/2025-07-19-22-54-48.gh-issue-136686.fc2Zl0.rst index 6bc213e83b3a5a..0f2510253b6b6c 100644 --- a/Misc/NEWS.d/next/Library/2025-07-19-22-54-48.gh-issue-136686.fc2Zl0.rst +++ b/Misc/NEWS.d/next/Library/2025-07-19-22-54-48.gh-issue-136686.fc2Zl0.rst @@ -1,2 +1,2 @@ -Fixed a bug where emails with `multipart/related` content did not round-trip -correctly through `email.parser.parsebytes()` and `as_bytes()`. +Fixed a bug where emails with ``multipart/related`` content did not round-trip +correctly through ``email.parser.parsebytes()`` and ``as_bytes()``. From d29bf9a732a42265a78430f6f9265ee6ff46dc9c Mon Sep 17 00:00:00 2001 From: Aarush <60299379+EmmetAVS@users.noreply.github.com> Date: Sun, 20 Jul 2025 12:43:39 -0700 Subject: [PATCH 3/3] Update Lib/test/test_email/test_generator.py Co-authored-by: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> --- Lib/test/test_email/test_generator.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Lib/test/test_email/test_generator.py b/Lib/test/test_email/test_generator.py index 182c090b2883de..c2f359e095906d 100644 --- a/Lib/test/test_email/test_generator.py +++ b/Lib/test/test_email/test_generator.py @@ -471,6 +471,7 @@ def test_smtp_policy(self): g = BytesGenerator(s, policy=policy.SMTP) g.flatten(msg) self.assertEqual(s.getvalue(), expected) + def test_utf8_round_trip_preserves_unicode_body(self): msg = EmailMessage() msg['From'] = "Páolo "