2121 ("łemłail@here.com" ,),
2222 ("email@localdomain.org" ,),
2323 ('"\\ \011 "@here.com' ,),
24+ ('"test@test"@example.com' ,),
2425 ],
2526)
2627def test_returns_true_on_valid_email (value : str ):
@@ -45,7 +46,6 @@ def test_returns_true_on_valid_email(value: str):
4546 ("example@inv-.alid-.com" ,),
4647 ("example@inv-.-alid.com" ,),
4748 ("john56789.john56789.john56789.john56789.john56789.john56789.john5@example.com" ,),
48- ('"test@test"@example.com' ,),
4949 # Quoted-string format (CR not allowed)
5050 ('"\\ \012 "@here.com' ,),
5151 # Non-quoted space/semicolon not allowed
@@ -66,3 +66,27 @@ def test_rejects_newline_after_local_part(local_part: str):
6666 # 2026-10-11: A newline before the at-sign is not part of a valid local part.
6767 assert email (f"{ local_part } @example.com" ) is True
6868 assert isinstance (email (f"{ local_part } \n @example.com" ), ValidationError )
69+
70+
71+ @pytest .mark .parametrize ("local_part" , ['"@"' , '"first@last"' , '"first@@last"' ])
72+ def test_accepts_at_signs_inside_quoted_local_part (local_part : str ):
73+ """Treat quoted at-signs as local-part content rather than separators."""
74+ assert email (f"{ local_part } @example.com" ) is True
75+
76+
77+ @pytest .mark .parametrize (
78+ "value" ,
79+ [
80+ "first@last@example.com" ,
81+ "first@@example.com" ,
82+ '"first@last@example.com' ,
83+ 'first@last"@example.com' ,
84+ '"first"@last@example.com' ,
85+ '"first@last"@@example.com' ,
86+ '"first@last"@' ,
87+ '"first@last"@-example.com' ,
88+ ],
89+ )
90+ def test_rejects_invalid_addresses_containing_multiple_at_signs (value : str ):
91+ """Continue validating the complete local part and domain after splitting."""
92+ assert isinstance (email (value ), ValidationError )
0 commit comments