Index: Lib/email/test/test_email.py =================================================================== --- Lib/email/test/test_email.py (revision 87188) +++ Lib/email/test/test_email.py (working copy) @@ -2319,6 +2319,24 @@ eq(utils.parseaddr('"\\\\"example\\\\" example"@example.com'), ('', '"\\\\"example\\\\" example"@example.com')) + def test_parseaddr_preserves_spaces_in_local_part(self): + # issue 9286. A normal RFC5322 local part should not contain any + # folding white space, but legacy local parts can (they are a sequence + # of atoms, not dotatoms). On the other hand we strip whitespace from + # before the @ and around dots, on the assumption that the whitespace + # around the punctuation is a mistake in what would otherwise be + # an RFC5322 local part. Leading whitespace is, usual, stripped as well. + self.assertEqual(('', "merwok wok@rusty.com"), + utils.parseaddr("merwok wok@rusty.com")) + self.assertEqual(('', "merwok wok@rusty.com"), + utils.parseaddr("merwok wok@rusty.com")) + self.assertEqual(('', "merwok wok@rusty.com"), + utils.parseaddr(" merwok wok @rusty.com")) + self.assertEqual(('', 'merwok"wok" wok@rusty.com'), + utils.parseaddr('merwok"wok" wok@rusty.com')) + self.assertEqual(('', 'merwok.wok.wok@rusty.com'), + utils.parseaddr('merwok. wok . wok@rusty.com')) + def test_multiline_from_comment(self): x = """\ Foo Index: Lib/email/_parseaddr.py =================================================================== --- Lib/email/_parseaddr.py (revision 87188) +++ Lib/email/_parseaddr.py (working copy) @@ -199,14 +199,18 @@ self.commentlist = [] def gotonext(self): - """Parse up to the start of the next address.""" + """Skip white space and extract comments.""" + wslist = [] while self.pos < len(self.field): if self.field[self.pos] in self.LWS + '\n\r': + if self.field[self.pos] not in '\n\r': + wslist.append(self.field[self.pos]) self.pos += 1 elif self.field[self.pos] == '(': self.commentlist.append(self.getcomment()) else: break + return EMPTYSTRING.join(wslist) def getaddrlist(self): """Parse all addresses. @@ -319,16 +323,24 @@ self.gotonext() while self.pos < len(self.field): + preserve_ws = True if self.field[self.pos] == '.': + if aslist and not aslist[-1].strip(): + aslist.pop() aslist.append('.') self.pos += 1 + preserve_ws = False elif self.field[self.pos] == '"': aslist.append('"%s"' % quote(self.getquote())) elif self.field[self.pos] in self.atomends: + if aslist and not aslist[-1].strip(): + aslist.pop() break else: aslist.append(self.getatom()) - self.gotonext() + ws = self.gotonext() + if preserve_ws and ws: + aslist.append(ws) if self.pos >= len(self.field) or self.field[self.pos] != '@': return EMPTYSTRING.join(aslist)