python 40 lines · 8 steps

Normalizing signup emails in Python

A single function cleans, validates, and canonicalizes email addresses while rejecting blanks and disposable domains.

Explained by highlit
1import re
2from email_validator import validate_email, EmailNotValidError
3 
4_DISPOSABLE_DOMAINS = {
5 "mailinator.com",
6 "guerrillamail.com",
7 "10minutemail.com",
8 "tempmail.com",
9}
10 
11_WHITESPACE = re.compile(r"\s+")
12 
13 
14class EmailValidationError(Exception):
15 pass
16 
17 
18def normalize_signup_email(raw: str, *, allow_disposable: bool = False) -> str:
19 if not raw or not raw.strip():
20 raise EmailValidationError("Email is required.")
21 
22 candidate = _WHITESPACE.sub("", raw).strip("<>").lower()
23 
24 try:
25 result = validate_email(candidate, check_deliverability=True)
26 except EmailNotValidError as exc:
27 raise EmailValidationError(str(exc)) from exc
28 
29 normalized = result.normalized
30 domain = result.domain.lower()
31 
32 if not allow_disposable and domain in _DISPOSABLE_DOMAINS:
33 raise EmailValidationError("Disposable email addresses are not allowed.")
34 
35 local, _, host = normalized.partition("@")
36 if host in {"gmail.com", "googlemail.com"}:
37 local = local.split("+", 1)[0].replace(".", "")
38 normalized = f"{local}@gmail.com"
39 
40 return normalized
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Wrapping a library's exception in your own domain error keeps callers decoupled from third-party details.
  2. 2Canonicalizing provider-specific quirks like Gmail dots and plus-tags prevents duplicate accounts for the same person.
  3. 3Normalizing input before validating catches sloppy but salvageable data instead of rejecting it outright.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Normalizing signup emails in Python — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code