philiprehberger-email-validate
Email validation with syntax checking and normalization.
Installation
pip install philiprehberger-email-validate
Usage
from philiprehberger_email_validate import validate_email
result = validate_email("user@example.com")
print(result.valid) # True
print(result.normalized) # "user@example.com"
print(result.domain) # "example.com"
Quick Syntax Check
from philiprehberger_email_validate import is_valid
is_valid("user@example.com") # True
is_valid("not-an-email") # False
Normalization
from philiprehberger_email_validate import normalize
normalize(" User@Example.COM ") # "user@example.com"
normalize("first.last+tag@gmail.com") # "firstlast@gmail.com"
normalize("user+promo@example.com") # "user@example.com"
MX Lookup
from philiprehberger_email_validate import validate_email
result = validate_email("user@example.com", check_mx=True)
if not result.valid:
print(result.error) # "MX lookup failed for domain: example.com"
Disposable Email Detection
from philiprehberger_email_validate import validate_email
result = validate_email("user@mailinator.com")
print(result.is_disposable) # True
Custom Disposable Domains
from philiprehberger_email_validate import validate_email, set_disposable_domains
# Per-call extra domains
result = validate_email("user@tempmail.xyz", extra_disposable=["tempmail.xyz"])
print(result.is_disposable) # True
# Global merge with built-in list
set_disposable_domains({"tempmail.xyz", "fakeemail.org"})
Role-Based Email Detection
from philiprehberger_email_validate import validate_email, is_role_based
result = validate_email("info@example.com")
print(result.is_role_based) # True
is_role_based("admin@example.com") # True
is_role_based("john@example.com") # False
Free Provider Detection
Flag consumer/free mailboxes (useful for filtering B2B signups that should use a company address):
from philiprehberger_email_validate import validate_email, is_free_email
result = validate_email("user@gmail.com")
print(result.is_free) # True
is_free_email("user@gmail.com") # True
is_free_email("user@acme-corp.com") # False
Email Domain Suggestions
from philiprehberger_email_validate import validate_email, suggest_domain, suggest_email
result = validate_email("user@gmial.com")
print(result.suggested_domain) # "gmail.com"
suggest_domain("hotmial.com") # "hotmail.com"
suggest_domain("gmail.com") # "" (no suggestion needed)
# Correct the whole address (local part and any +tag are preserved)
suggest_email("first.last+promo@hotmial.com") # "first.last+promo@hotmail.com"
suggest_email("user@gmail.com") # "" (no suggestion needed)
RFC 5321 Strict Mode
from philiprehberger_email_validate import validate_email
result = validate_email("user@example.com", strict=True)
print(result.valid) # True
result = validate_email(".user@example.com", strict=True)
print(result.valid) # False
print(result.error) # "Local part starts or ends with a dot (RFC 5321)"
Bulk Validation
from philiprehberger_email_validate import validate_many
results = validate_many(["user@example.com", "bad@@email", "test@gmail.com"])
for r in results:
print(r.normalized, r.valid)
# With concurrent MX lookups and strict mode
results = validate_many(emails, check_mx=True, concurrent=True, strict=True)
Extract Emails from Text
from philiprehberger_email_validate import extract_emails
emails = extract_emails("Contact hello@example.com or support@test.org")
# ["hello@example.com", "support@test.org"]
Email Parts
from philiprehberger_email_validate import email_parts
parts = email_parts("user@example.com")
print(parts.local) # "user"
print(parts.domain) # "example.com"
print(parts.tld) # "com"
Mask Email
from philiprehberger_email_validate import mask_email
mask_email("john@example.com") # "j***n@example.com"
mask_email("alice@example.com") # "a***e@example.com"
Disposable check
from philiprehberger_email_validate import is_disposable
is_disposable("user@mailinator.com") # True
is_disposable("user@gmail.com") # False
is_disposable("not-an-email") # False
Comparing emails
from philiprehberger_email_validate import compare_emails
compare_emails("Foo@Gmail.com", "foo@gmail.com") # True
compare_emails("foo.bar+tag@gmail.com", "foobar@gmail.com") # True
compare_emails("foo@a.com", "bar@a.com") # False
Deduplicating emails
Remove duplicates by normalized form, keeping the first original spelling and input order:
from philiprehberger_email_validate import dedupe_emails
dedupe_emails(["Foo@Gmail.com", "f.oo+news@gmail.com", "bar@example.com"])
# ["Foo@Gmail.com", "bar@example.com"]
Grouping by domain
Invalid addresses are collected under the None key:
from philiprehberger_email_validate import group_by_domain
group_by_domain(["a@Example.com", "b@test.org", "c@example.com", "oops"])
# {"example.com": ["a@Example.com", "c@example.com"], "test.org": ["b@test.org"], None: ["oops"]}
Serializing results
import json
from philiprehberger_email_validate import validate_email
json.dumps(validate_email("user@example.com").to_dict())
API
| Function / Class | Description |
|---|---|
EmailResult |
Dataclass with valid, normalized, domain, error, is_disposable, is_role_based, is_free, and suggested_domain fields |
normalize(email) |
Normalize an email: lowercase, strip whitespace, Gmail dot-insensitivity, plus-addressing cleanup |
is_valid(email) |
Quick boolean syntax check |
validate_email(email, check_mx, extra_disposable, strict) |
Full validation returning an EmailResult |
validate_many(emails, check_mx, concurrent, extra_disposable, strict) |
Validate multiple emails with optional parallel MX lookups |
is_role_based(email) |
Check if an email uses a role-based local part (info@, admin@, etc.) |
is_disposable(email) |
Check if an email's domain is in the disposable-domains set |
is_free_email(email) |
Check if an email's domain is a known free/public provider (gmail, yahoo, etc.) |
compare_emails(a, b) |
Check if two emails are equivalent after normalization |
suggest_domain(domain) |
Suggest a corrected domain for common typos |
suggest_email(email) |
Suggest a corrected full email address for a domain typo |
set_disposable_domains(domains) |
Merge additional domains into the global disposable domains set |
DISPOSABLE_DOMAINS |
Mutable set of known disposable email domains |
ROLE_PREFIXES |
Frozen set of known role-based email prefixes |
COMMON_DOMAINS |
Frozen set of common email provider domains used for suggestions |
FREE_EMAIL_DOMAINS |
Frozen set of known free/public email provider domains |
extract_emails(text) |
Extract all valid email addresses from text, deduplicated |
email_parts(email) |
Split email into structured parts (local, domain, tld, normalized) |
EmailParts |
Dataclass with local, domain, tld, normalized fields |
mask_email(email, mask_char, visible) |
Mask the local part for privacy display |
dedupe_emails(emails) |
Remove duplicates by normalized form, keeping the first original spelling and input order |
group_by_domain(emails) |
Group addresses by normalized domain; invalid addresses go under the None key |
EmailResult.to_dict() |
Return the result fields as a plain dictionary |
Development
pip install -e .
python -m pytest tests/ -v
Support
If you find this project useful:
License
Metadata
Release files for philiprehberger-email-validate 0.8.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| philiprehberger_email_validate-0.8.0.tar.gz | 190.3 kB | Without provenance | Details for philiprehberger_email_validate-0.8.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| philiprehberger_email_validate-0.8.0-py3-none-any.whl | Python 3 | none | any | Details for philiprehberger_email_validate-0.8.0-py3-none-any.whl |
Total release size: 201.5 kB
Release files / philiprehberger_email_validate-0.8.0.tar.gz
| Download URL | philiprehberger_email_validate-0.8.0.tar.gz |
|---|---|
| Size | 190.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
dd2ec100ce9927940119b59f42de3d1689770dfc9bb3e2e524cfb9f3c9abef1d
|
|
BLAKE2b-256 checksum How to use checksums |
eae95c9e70a6b06070c4b26006777f2873ce4949c096d65331c5e1d7a2b5ba3d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.15
|
Release files / philiprehberger_email_validate-0.8.0-py3-none-any.whl
| Download URL | philiprehberger_email_validate-0.8.0-py3-none-any.whl |
|---|---|
| Size | 11.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
7e250e17bda189468ebc3f24573a72c7c63e010509f269520774b85bd371e5d4
|
|
BLAKE2b-256 checksum How to use checksums |
3cd74d92859d4ef9976fa19e75a98a80354ccef31121e82940bfa3d6bff3e44d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.15
|
Security for philiprehberger-email-validate 0.8.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.