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 record AppConfig( String host, int port, String databaseUrl,
Loading typed config from env vars in Java
records
configuration
environment-variables
Intermediate
6 steps
python
from datetime import datetime, timezone _INTERVALS = ( ("year", 60 * 60 * 24 * 365),
Building a human-friendly time_ago helper
datetime
timezones
formatting
Intermediate
5 steps
php
<?php namespace App\Validation;
Building a reusable address form validator in PHP
validation
error-accumulation
regex
Intermediate
9 steps
python
import time import threading from flask import Flask, request, jsonify, g
A token-bucket rate limiter in Flask
rate-limiting
token-bucket
middleware
Intermediate
7 steps
php
<?php namespace App\Http\Controllers;
A cached autocomplete endpoint in Laravel
caching
validation
query-ranking
Intermediate
8 steps
java
public List<DailySample> backfillMissingDates(List<DailySample> samples, double fillValue) { if (samples.isEmpty()) { return List.of(); }
Backfilling gaps in a daily time series in Java
streams
time-series
treemap
Intermediate
5 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.