A production-quality, zero-dependency Python package for parsing email addresses exactly as RFC 5322 (Internet Message Format) defines them — not a regex validator, but a real grammar-driven recursive-descent parser implementing the complete ABNF from §3.2 through §3.4, plus optional obsolete-syntax support from §4.4.
Stop validating email addresses with regex you found on Stack Overflow in
2011. Regex validators either reject valid addresses ("john..doe"@example.com
is valid) or accept garbage — nobody knows which until a bounce. A grammar
parser gives you structured access to every part: display name, local part,
domain, comments, quoting state. Parse, inspect, normalize, re-serialize — correctly.
- Full ABNF coverage:
address,mailbox,name-addr,angle-addr,addr-spec,group,phrase,dot-atom,quoted-string,domain-literal(IPv4 + IPv6), CFWS with nested comments and FWS. - Strict / permissive modes: strict mode rejects every
obs-*production; permissive mode accepts §4.4 obsolete forms (obs-local-part,obs-domain, unclosed angle-addr recovery, …). - Comments extracted, never lost: every CFWS comment is collected in order on the result object.
- Semantic values: quoted-pairs are unescaped; FWS inside quoted
strings is preserved;
addr_specre-quotes only when required. - Fully typed, ships
py.typed, no third-party dependencies, Python ≥ 3.10. - 100-test suite, organized by RFC section, all green on CI across Python 3.10–3.13.
pip install rfc5322-parserOr from source:
pip install .from rfc5322 import AddressParser, ParseError
parser = AddressParser(strict=True) # strict: RFC 5322 §3.2–3.4 only
loose = AddressParser(strict=False) # permissive: also accepts §4.4 obs-* forms
a = parser.parse('"Jane Doe" <[email protected]>')
a.display_name # 'Jane Doe'
a.local_part # 'jane'
a.domain # 'example.com'
a.addr_spec # '[email protected]'
a.comments # []
a.source # '"Jane Doe" <[email protected]>'
g = parser.parse("A Group:[email protected], [email protected];")
g.is_group # True
[m.addr_spec for m in g.group_members] # ['[email protected]', '[email protected]']a = parser.parse('(created by mutt)user(mid)@(end)example.com')
a.comments # ['created by mutt', 'mid', 'end']
q = parser.parse(r'"very.(),:;<>\"@\\ long"@example.com')
q.local_part # 'very.(),:;<>["@\\ long'parser.parse('user+tag@[192.168.1.1]').domain # '[192.168.1.1]'
parser.parse('user@[IPv6:2001:db8::1]').domain # '[IPv6:2001:db8::1]'parser.parse_address_list('"A" <[email protected]>, [email protected], G:[email protected];')
# commas inside quotes/comments/angle-addr are handled correctly
parser.parse_mailbox_list('[email protected], [email protected]') # groups rejected hereloose.parse('user."quoted"@example.com') # obs-local-part
loose.parse('[email protected]') # obs-domain
AddressParser(strict=True).parse('user."quoted"@example.com') # raises ParseErrorInvalid input raises ParseError (a subclass of ValueError).
| Member | Description |
|---|---|
AddressParser(strict=True, max_length=998) |
Construct a parser. |
.parse(raw) -> RFC5322Address |
Parse one mailbox or group. |
.parse_address_list(raw) -> list[RFC5322Address] |
Comma-separated address-list (mailboxes + groups). |
.parse_mailbox_list(raw) -> list[RFC5322Address] |
Comma-separated mailbox-list (groups rejected). |
RFC5322Address |
Dataclass: display_name, local_part, domain, is_group, group_members, comments, source, plus addr_spec. |
ParseError(ValueError) |
Raised on any syntax violation under the active mode. |
The stdlib email package is built for parsing whole messages, is heavily
API-shaped around Message objects, and its address handling has edge-case
quirks. rfc5322 is focused solely on addresses, returns clean typed
dataclasses, and offers an explicit strict/permissive switch.
Note: this is a syntax parser per RFC 5322 — it does not check DNS/MX deliverability, and it implements the ASCII grammar (internationalized addresses are RFC 6531/6532 territory).
uv venv && uv pip install -e . pytest
python -m pytestThe test suite contains 100 cases organized by RFC section: quoted-pair (§3.2.1), FWS (§3.2.2), CFWS/comments incl. nesting (§3.2.3), quoted-string (§3.2.4), atoms/dot-atoms (§3.2.5), address/mailbox/group (§3.4), domain literals with IPv4/IPv6 validation (§3.4.1), obsolete syntax (§4.4), length limits, unicode handling, and rejection cases. See compliance.md for notes on grammar coverage decisions.
The parser core is free and open source (MIT). If you find it useful, please consider supporting development by purchasing the extended edition on Gumroad: Support rfc5322 →