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
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Wrapping a library's exception in your own domain error keeps callers decoupled from third-party details.
- 2Canonicalizing provider-specific quirks like Gmail dots and plus-tags prevents duplicate accounts for the same person.
- 3Normalizing input before validating catches sloppy but salvageable data instead of rejecting it outright.
Related explainers
java
public final class ShippingAddress { private final String recipient; private final String line1; private final String line2;
Building immutable objects with the Builder pattern
builder-pattern
immutability
validation
Intermediate
7 steps
javascript
export function buildPaginationUrl(baseUrl, { page, perPage, filters = {}, sort } = {}) { const url = new URL(baseUrl); const params = url.searchParams;
Building and parsing pagination URLs
url-parsing
query-string
pagination
Intermediate
8 steps
python
import time from concurrent.futures import ThreadPoolExecutor, as_completed from pathlib import Path
Parallel file downloads with a thread pool
concurrency
thread-pool
streaming-io
Intermediate
8 steps
go
package validate import ( "fmt"
Struct validation with reflection in Go
reflection
struct-tags
validation
Intermediate
7 steps
python
from datetime import datetime from zoneinfo import ZoneInfo
Timezone-safe datetime handling in Python
timezones
datetime
normalization
Intermediate
4 steps
python
import shutil import uuid from pathlib import Path
Safe avatar uploads in FastAPI
file-upload
validation
streaming
Intermediate
8 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/normalizing-signup-emails-in-python-explained-python-72c9/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.