A robust email address syntax and deliverability validation library for Python.
npx @tessl/cli install tessl/pypi-email-validator@2.3.0A robust email address syntax and deliverability validation library for Python 3.8+. This library validates that a string is of the form name@example.com and optionally checks that the domain name is set up to receive email, providing friendly error messages and normalized email addresses.
pip install email-validatorfrom email_validator import validate_email, EmailNotValidErrorImport validation exceptions:
from email_validator import EmailSyntaxError, EmailUndeliverableErrorImport helper functions and types:
from email_validator import ValidatedEmail, caching_resolver, __version__from email_validator import validate_email, EmailNotValidError
email = "user@example.com"
try:
# Validate email address with deliverability checking
validated_email = validate_email(email)
# Use the normalized form for storage/comparison
normalized = validated_email.normalized
print(f"Valid email: {normalized}")
# Access additional information
print(f"Local part: {validated_email.local_part}")
print(f"Domain: {validated_email.domain}")
print(f"ASCII email: {validated_email.ascii_email}")
except EmailNotValidError as e:
print(f"Invalid email: {e}")The email-validator library provides a single main validation function that performs comprehensive email address validation in multiple stages:
Core email validation functionality that validates email address syntax and optionally checks deliverability.
def validate_email(
email: Union[str, bytes],
/, # prior arguments are positional-only
*, # subsequent arguments are keyword-only
allow_smtputf8: Optional[bool] = None,
allow_empty_local: Optional[bool] = None,
allow_quoted_local: Optional[bool] = None,
allow_domain_literal: Optional[bool] = None,
allow_display_name: Optional[bool] = None,
strict: Optional[bool] = None,
check_deliverability: Optional[bool] = None,
test_environment: Optional[bool] = None,
globally_deliverable: Optional[bool] = None,
timeout: Optional[int] = None,
dns_resolver: Optional[object] = None
) -> ValidatedEmail:
"""
Validate an email address and return normalized information.
Parameters:
- email: Email address to validate (str or bytes)
- allow_smtputf8: Allow internationalized email addresses (default: True)
- allow_empty_local: Allow empty local part like @example.com (default: False)
- allow_quoted_local: Allow quoted local parts with special characters (default: False)
- allow_domain_literal: Allow bracketed IP addresses in domain (default: False)
- allow_display_name: Allow display name format like "Name <email>" (default: False)
- strict: Enable additional syntax checks like length limits (default: False)
- check_deliverability: Perform DNS checks on domain (default: True)
- test_environment: Allow test domains and disable DNS checks (default: False)
- globally_deliverable: Require globally routable domains (default: True)
- timeout: DNS query timeout in seconds (default: 15)
- dns_resolver: Custom DNS resolver instance (default: None)
Returns:
ValidatedEmail: Object containing normalized email and metadata
Raises:
EmailSyntaxError: Email syntax is invalid
EmailUndeliverableError: Domain fails deliverability checks
"""The email-validator package provides a command-line interface for testing and batch validation of email addresses.
def main(dns_resolver: Optional[object] = None) -> None:
"""
Command-line interface for email validation.
Usage:
python -m email_validator user@example.com
python -m email_validator < email_list.txt
Environment Variables:
ALLOW_SMTPUTF8, ALLOW_EMPTY_LOCAL, ALLOW_QUOTED_LOCAL,
ALLOW_DOMAIN_LITERAL, ALLOW_DISPLAY_NAME, GLOBALLY_DELIVERABLE,
CHECK_DELIVERABILITY, TEST_ENVIRONMENT, DEFAULT_TIMEOUT
"""Helper function for creating cached DNS resolvers for improved performance when validating many email addresses.
def caching_resolver(
*,
timeout: Optional[int] = None,
cache: Optional[object] = None,
dns_resolver: Optional[object] = None
) -> object:
"""
Create a DNS resolver with caching for improved performance.
Parameters:
- timeout: DNS query timeout in seconds (default: 15)
- cache: Cache instance to use (default: LRUCache)
- dns_resolver: Base resolver to configure (default: system resolver)
Returns:
dns.resolver.Resolver: Configured resolver with caching
"""class ValidatedEmail:
"""
Result object containing normalized email address and validation metadata.
"""
# Core email components
original: str # Original input email address
normalized: str # Normalized email address (recommended for use)
local_part: str # Local part after normalization
domain: str # Domain part after normalization
# ASCII representations
ascii_email: Optional[str] # ASCII-only form if available
ascii_local_part: Optional[str] # ASCII-only local part if available
ascii_domain: str # ASCII-only domain part
# Metadata
smtputf8: bool # True if SMTPUTF8 extension required
display_name: Optional[str] # Display name if present
domain_address: Optional[object] # IPv4Address/IPv6Address for domain literals
# Deliverability information (if checked)
mx: Optional[List[Tuple[int, str]]] # MX records as (priority, domain) tuples
mx_fallback_type: Optional[str] # Fallback record type ('A' or 'AAAA')
spf: Optional[str] # SPF record if found during deliverability check
def as_dict(self) -> Dict[str, Any]:
"""Convert to dictionary representation."""
def __repr__(self) -> str:
"""String representation showing normalized email."""class EmailNotValidError(ValueError):
"""
Base exception for all email validation failures.
Contains human-readable error message explaining why validation failed.
"""
class EmailSyntaxError(EmailNotValidError):
"""
Exception raised when email address has invalid syntax.
Includes detailed explanation of syntax problems.
"""
class EmailUndeliverableError(EmailNotValidError):
"""
Exception raised when email domain fails deliverability checks.
Indicates DNS resolution problems or undeliverable domains.
"""Global configuration constants that set default behavior for all validation calls:
# Validation behavior defaults
ALLOW_SMTPUTF8: bool = True # Allow internationalized addresses
ALLOW_EMPTY_LOCAL: bool = False # Allow empty local parts
ALLOW_QUOTED_LOCAL: bool = False # Allow quoted local parts
ALLOW_DOMAIN_LITERAL: bool = False # Allow IP address domains
ALLOW_DISPLAY_NAME: bool = False # Allow display name format
STRICT: bool = False # Enable strict validation mode
# Deliverability defaults
CHECK_DELIVERABILITY: bool = True # Perform DNS deliverability checks
GLOBALLY_DELIVERABLE: bool = True # Require globally routable domains
TEST_ENVIRONMENT: bool = False # Enable test environment mode
DEFAULT_TIMEOUT: int = 15 # DNS query timeout in seconds
# Special domain handling
SPECIAL_USE_DOMAIN_NAMES: List[str] # List of rejected special-use domains
# Version information
__version__: str # Package version stringfrom email_validator import validate_email, EmailNotValidError
def validate_user_email(email_input):
try:
result = validate_email(email_input)
return result.normalized
except EmailNotValidError as e:
print(f"Invalid email: {e}")
return None
# Usage
email = validate_user_email("user@example.com")
if email:
print(f"Validated email: {email}")from email_validator import validate_email, EmailNotValidError
def validate_registration_email(email):
"""Validate email for new user registration with deliverability checking."""
try:
result = validate_email(
email,
check_deliverability=True, # Check DNS for registration
allow_quoted_local=False, # Reject confusing quoted formats
allow_display_name=False # Reject display name format
)
return result.normalized
except EmailNotValidError as e:
raise ValueError(f"Please enter a valid email address: {e}")from email_validator import validate_email, EmailSyntaxError
def validate_login_email(email):
"""Validate email for login with minimal checks for performance."""
try:
result = validate_email(
email,
check_deliverability=False # Skip DNS checks for login
)
return result.normalized
except EmailSyntaxError as e:
raise ValueError("Invalid email format")from email_validator import validate_email, caching_resolver, EmailNotValidError
def validate_email_list(email_list):
"""Validate multiple emails efficiently using cached DNS resolver."""
# Create cached resolver for better performance
resolver = caching_resolver(timeout=10)
validated_emails = []
for email in email_list:
try:
result = validate_email(email, dns_resolver=resolver)
validated_emails.append(result.normalized)
except EmailNotValidError as e:
print(f"Skipping invalid email {email}: {e}")
return validated_emailsfrom email_validator import validate_email, EmailNotValidError
def validate_international_email(email):
"""Validate international email addresses with full Unicode support."""
try:
result = validate_email(
email,
allow_smtputf8=True, # Allow international characters
globally_deliverable=True # Ensure globally routable
)
print(f"Normalized: {result.normalized}")
print(f"ASCII form: {result.ascii_email}")
print(f"Requires SMTPUTF8: {result.smtputf8}")
return result.normalized
except EmailNotValidError as e:
print(f"Invalid international email: {e}")
return None
# Examples
validate_international_email("用户@example.com") # Chinese characters
validate_international_email("user@測試.com") # International domainimport email_validator
from email_validator import validate_email
# Modify global defaults
email_validator.CHECK_DELIVERABILITY = False # Disable DNS checks globally
email_validator.ALLOW_QUOTED_LOCAL = True # Allow quoted formats globally
# Allow test domains for development
email_validator.SPECIAL_USE_DOMAIN_NAMES.remove("test")
# Now all validation calls use these defaults
result = validate_email("developer@test") # Would normally be rejectedfrom email_validator import (
validate_email,
EmailNotValidError,
EmailSyntaxError,
EmailUndeliverableError
)
def comprehensive_email_validation(email):
"""Demonstrate different error handling approaches."""
try:
result = validate_email(email)
return {
'valid': True,
'normalized': result.normalized,
'metadata': {
'local_part': result.local_part,
'domain': result.domain,
'smtputf8': result.smtputf8,
'mx_records': getattr(result, 'mx', None)
}
}
except EmailSyntaxError as e:
return {
'valid': False,
'error_type': 'syntax',
'message': str(e)
}
except EmailUndeliverableError as e:
return {
'valid': False,
'error_type': 'deliverability',
'message': str(e)
}
except EmailNotValidError as e:
return {
'valid': False,
'error_type': 'general',
'message': str(e)
}