diff --git a/Lib/email/_parseaddr.py b/Lib/email/_parseaddr.py index 29718957a68a9b..304d69c6ef0161 100644 --- a/Lib/email/_parseaddr.py +++ b/Lib/email/_parseaddr.py @@ -352,6 +352,8 @@ def getrouteaddr(self): elif self.field[self.pos] == '@': self.pos += 1 expectroute = True + elif self.field[self.pos] == ',': + self.pos += 1 elif self.field[self.pos] == ':': self.pos += 1 else: diff --git a/Lib/test/test_email/test_email.py b/Lib/test/test_email/test_email.py index 2a5794a5b66fc5..69e7b2644657c7 100644 --- a/Lib/test/test_email/test_email.py +++ b/Lib/test/test_email/test_email.py @@ -3344,6 +3344,14 @@ def test_parseaddr_preserves_spaces_in_local_part(self): self.assertEqual(('', 'merwok.wok.wok@xample.com'), utils.parseaddr('merwok. wok . wok@xample.com')) + def test_parseaddr_handles_obsolete_addressing(self): + self.assertEqual(('foobar', 'foo@bar.com'), + utils.parseaddr('"foobar" <@hop.org:foo@bar.com>')) + self.assertEqual(('foobar', 'foo@bar.com'), + utils.parseaddr('"foobar" <@hop1.org,@hop2.org:foo@bar.com>')) + self.assertEqual(('foobar', 'foo@bar.com'), + utils.parseaddr('"foobar" <@hop1.org, @hop2.org, @hop3.org:foo@bar.com>')) + def test_formataddr_does_not_quote_parens_in_quoted_string(self): addr = ("'foo@example.com' (foo@example.com)", 'foo@example.com') diff --git a/Misc/NEWS.d/next/Library/2026-10-28-01-34-00.gh-issue-40889.route-hops.rst b/Misc/NEWS.d/next/Library/2026-10-28-01-34-00.gh-issue-40889.route-hops.rst new file mode 100644 index 00000000000000..9670c6577ee2f3 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-10-28-01-34-00.gh-issue-40889.route-hops.rst @@ -0,0 +1,2 @@ +Fix :func:`email.utils.parseaddr` to handle routes with multiple intermediate hops +(see :rfc:`RFC 2822, Section 4.4 <2822#section-4.4>`).