Package: email-validator
email-validator: Validate Email AddressesA robust email address syntax and deliverability validation library for
Python 3.8+ byJoshua Tauberer.This library validates that a string is of the formname@example.comand optionally checks that the domain name is set up to receive email.
This is the sort of validation you would want when you are identifying
users by their email address like on a registration/login form (but not
necessarily for composing an email message, see below).Key features:Checks that an email address has the correct syntax --- great for
email-based registration/login forms or validing data.Gives friendly English error messages when validation fails that you
can display to end-users.Checks deliverability (optional): Does the domain name resolve?
(You can override the default DNS resolver to add query caching.)Supports internationalized domain names and internationalized local parts.Rejects addresses with unsafe Unicode characters, obsolete email address
syntax that you'd find unexpected, special use domain names like@localhost, and domains without a dot by default. This is an
opinionated library!Normalizes email addresses (important for internationalized
and quoted-string addresses! see below).Python type annotations are used.This is an opinionated library. You should definitely also consider using
the less-opinionatedpyIsEmailandflankerif they are better for your
use case.View theCHANGELOG / Release Notesfor the version history of changes in the library. Occasionally this README is ahead of the latest published package --- see the CHANGELOG for details.InstallationThis packageis on PyPI, so:pipinstallemail-validator(You might need to usepip3depending on your local environment.)Quick StartIf you're validating a user's email address before creating a user
account in your application, you might do this:fromemail_validatorimportvalidate_email,EmailNotValidErroremail="my+address@example.org"try:# Check that the email address is valid. Turn on check_deliverability# for first-time validations like on account creation pages (but not# login pages).emailinfo=validate_email(email,check_deliverability=False)# After this point, use only the normalized form of the email address,# especially before going to a database query.email=emailinfo.normalizedexceptEmailNotValidErrorase:# The exception message is human-readable explanation of why it's# not a valid (or deliverable) email address.print(str(e))This validates the address and gives you its normalized form. You shouldput the normalized form in your databaseand always normalize before
checking if an address is in your database. When using this in a login form,
setcheck_deliverabilitytoFalseto avoid unnecessary DNS queries.UsageOverviewThe module provides a functionvalidate_email(email_address)which
takes an email address and:Raises aEmailNotValidErrorwith a helpful, human-readable error
message explaining why the email address is not valid, orReturns an object with a normalized form of the email address (which
you should use!) and other information about it.When an email address is not valid,validate_emailraises either anEmailSyntaxErrorif the form of the address is invalid or anEmailUndeliverableErrorif the domain name fails DNS checks. Both
exception classes are subclasses ofEmailNotValidError, which in turn
is a subclass ofValueError.But when an email address is valid, an object is returned containing
a normalized form of the email address (which you should use!) and
other information.The validator doesn't, by default, permit obsoleted forms of email addresses
that no one uses anymore even though they are still valid and deliverable, since
they will probably give you grief if you're using email for login. (See
later in the document about how to allow some obsolete forms.)The validator optionally checks that the domain name in the email address has
a DNS MX record indicating that it can receive email. (Except a Null MX record.
If there is no MX record, a fallback A/AAAA-record is permitted, unless
a reject-all SPF record is present.) DNS is slow and sometimes unavailable or
unreliable, so consider whether these checks are useful for your use case and
turn them off if they aren't.
There is nothing to be gained by trying to actually contact an SMTP server, so
that's not done here. For privacy, security, and practicality reasons, servers
are good at not giving away whether an address is
deliverable or not: email addresses that appear to accept mail at first
can bounce mail after a delay, and bounced mail may indicate a temporary
failure of a good email address (sometimes an intentional failure, like
greylisting).OptionsThevalidate_emailfunction also accepts the following keyword arguments
(defaults are as shown below):check_deliverability=True: If true, DNS queries are made to check that the domain name in the email address (the part after the @-sign) can receive mail, as described above. Set toFalseto skip this DNS-based check. It is recommended to passFalsewhen performing validation for login pages (but not account creation pages) since re-validation of a previously validated domain in your database by querying DNS at every login is probably undesirable. You can also setemail_validator.CHECK_DELIVERABILITYtoFalseto turn this off for all calls by default.dns_resolver=None: Pass an instance ofdns.resolver.Resolverto control the DNS resolver including setting a timeout anda cache. Thecaching_resolverfunction shown below is a helper function to construct a dns.resolver.Resolver with aLRUCache. Reuse the same resolver instance across calls tovalidate_emailto make use of the cache.test_environment=False: IfTrue, DNS-based deliverability checks are disabled andtestand**.testdomain names are permitted (see below). You can also setemail_validator.TEST_ENVIRONMENTtoTrueto turn it on for all calls by default.allow_smtputf8=True: Set toFalseto prohibit internationalized addresses that would
require theSMTPUTF8extension. You can also setemail_validator.ALLOW_SMTPUTF8toFalseto turn it off for all calls by default.allow_quoted_local=False: Set toTrueto allow obscure and potentially problematic email addresses in which the part of the address before the @-sign contains spaces, @-signs, or other surprising characters when the local part is surrounded in quotes (so-called quoted-string local parts). In the object returned byvalidate_email, the normalized local part removes any unnecessary backslash-escaping and even removes the surrounding quotes if the address would be valid without them. You can also setemail_validator.ALLOW_QUOTED_LOCALtoTrueto turn this on for all calls by default.allow_domain_literal=False: Set toTrueto allow bracketed IPv4 and "IPv6:"-prefixd IPv6 addresses in the domain part of the email address. No deliverability checks are performed for these addresses. In the object returned byvalidate_email, the normalized domain will use the condensed IPv6 format, if applicable. The object'sdomain_addressattribute will hold the parsedipaddress.IPv4Addressoripaddress.IPv6Addressobject if applicable. You can also setemail_validator.ALLOW_DOMAIN_LITERALtoTrueto turn this on for all calls by default.allow_empty_local=False: Set toTrueto allow an empty local part (i.e.@example.com), e.g. for validating Postfix aliases.DNS timeout and cacheWhen validating many email addresses or to control the timeout (the default is 15 seconds), create a cachingdns.resolver.Resolverto reuse in each call. Thecaching_resolverfunction returns one easily for you:fromemail_validatorimportvalidate_email,caching_resolverresolver=caching_resolver(timeout=10)whileTrue:validate_email(email,dns_resolver=resolver)Test addressesThis library rejects email addresses that use theSpecial Use Domain Namesinvalid,localhost,test, and some others by raisingEmailSyntaxError. This is to protect your system from abuse: You probably don't want a user to be able to cause an email to be sent tolocalhost(although they might be able to still do so via a malicious MX record). However, in your non-production test environments you may want to use@testor@myname.testemail addresses. There are three ways you can allow this:Addtest_environment=Trueto the call tovalidate_email(see above).Setemail_validator.TEST_ENVIRONMENTtoTrueglobally.Remove the special-use domain name that you want to use fromemail_validator.SPECIAL_USE_DOMAIN_NAMES, e.g.:importemail_validatoremail_validator.SPECIAL_USE_DOMAIN_NAMES.remove("test")It is tempting to use@example.com/net/orgin tests. They arenotin this library'sSPECIAL_USE_DOMAIN_NAMESlist so you can, but shouldn't, use them. These domains are reserved to IANA for use in documentation so there is no risk of accidentally emailing someone at those domains. But beware that this library will nevertheless reject these domain names if DNS-based deliverability checks are not disabled because these domains do not resolve to domains that accept email. In tests, consider using your own domain name or@testor@myname.testinstead.Internationalized email addressesThe email protocol SMTP and the domain name system DNS have historically
only allowed English (ASCII) characters in email addresses and domain names,
respectively. Each has adapted to internationalization in a separate
way, creating two separate aspects to email address
internationalization.Internationalized domain names (IDN)The first isinternationalized domain names (RFC
5891), a.k.a IDNA 2008. The DNS
system has not been updated with Unicode support. Instead, internationalized
domain names are converted into a special IDNA ASCII "Punycode"
form starting withxn--. When an email address has non-ASCII
characters in its domain part, the domain part is replaced with its IDNA
ASCII equivalent form in the process of mail transmission. Your mail
submission library probably does this for you transparently. (Compliance
around the web is not very good though.) This library conforms to IDNA 2008
using theidnamodule by Kim Davies.Internationalized local partsThe second sort of internationalization is internationalization in thelocalpart of the address (before the @-sign). In non-internationalized
email addresses, only English letters, numbers, and some punctuation
(._!#$%&'^``*+-=~/?{|}) are allowed. In internationalized email address
local parts, a wider range of Unicode characters are allowed.A surprisingly large number of Unicode characters are not safe to display,
especially when the email address is concatenated with other text, so this
library tries to protect you by not permitting reserved, non-, private use,
formatting (which can be used to alter the display order of characters),
whitespace, and control characters, and combining characters
as the first character of the local part and the domain name (so that they
cannot combine with something outside of the email address string or with
the @-sign). Seehttps://qntm.org/safeandhttps://trojansource.codes/for relevant prior work. (Other than whitespace, these are checks that
you should be applying to nearly all user inputs in a security-sensitive
context.)These character checks are performed after Unicode normalization (see below),
so you are only fully protected if you replace all user-provided email addresses
with the normalized email address string returned by this library. This does not
guard against the well known problem that many Unicode characters look alike
(or are identical), which can be used to fool humans reading displayed text.Email addresses with these non-ASCII characters require that your mail
submission library and the mail servers along the route to the destination,
including your own outbound mail server, all support theSMTPUTF8 (RFC 6531)extension.
Support for SMTPUTF8 varies. See theallow_smtputf8parameter.If you know ahead of time that SMTPUTF8 is not supported by your mail submission stackBy default all internationalized forms are accepted by the validator.
But if you know ahead of time that SMTPUTF8 is not supported by your
mail submission stack, then you must filter out addresses that require
SMTPUTF8 using theallow_smtputf8=Falsekeyword argument (see above).
This will cause the validation function to raise aEmailSyntaxErrorif
delivery would require SMTPUTF8. That's just in those cases where
non-ASCII characters appear before the @-sign. If you do not setallow_smtputf8=False, you can also check the value of thesmtputf8field in the returned object.If your mail submission library doesn't support Unicode at all --- even
in the domain part of the address --- then immediately prior to mail
submission you must replace the email address with its ASCII-ized form.
This library gives you back the ASCII-ized form in theascii_emailfield in the returned object, which you can get like this:emailinfo=validate_email(email,allow_smtputf8=False)email=emailinfo.ascii_emailThe local part is left alone (if it has internationalized charactersallow_smtputf8=Falsewill force validation to fail) and the domain
part is converted toIDNA ASCII.
(You probably should not do this at account creation time so you don't
change the user's login information without telling them.)NormalizationUnicode NormalizationThe use of Unicode in email addresses introduced a normalization
problem. Different Unicode strings can look identical and have the same
semantic meaning to the user. Thenormalizedfield returned on successful
validation provides the correctly normalized form of the given email
address.For example, the CJK fullwidth Latin letters are considered semantically
equivalent in domain names to their ASCII counterparts. This library
normalizes them to their ASCII counterparts:emailinfo=validate_email("me@Ｄｏｍａｉｎ.com")print(emailinfo.normalized)print(emailinfo.ascii_email)# prints "me@domain.com" twiceBecause an end-user might type their email address in different (but
equivalent) un-normalized forms at different times, you ought to
replace what they enter with the normalized form immediately prior to
going into your database (during account creation), querying your database
(during login), or sending outbound mail. Normalization may also change
the length of an email address, and this may affect whether it is valid
and acceptable by your SMTP provider.The normalizations include lowercasing the domain part of the email
address (domain names are case-insensitive),Unicode "NFC"
normalizationof the
whole address (which turns characters pluscombining
charactersinto
precomposed characters where possible, replacement offullwidth and
halfwidth
charactersin the domain part, possibly otherUTS46mappings on the domain part,
and conversion from Punycode to Unicode characters.(SeeRFC 6532 (internationalized email) section
3.1andRFC 5895
(IDNA 2008) section 2.)Other NormalizationNormalization is also applied to quoted-string local parts and domain
literal IPv6 addresses if you have allowed them by theallow_quoted_localandallow_domain_literaloptions. In quoted-string local parts, unnecessary
backslash escaping is removed and even the surrounding quotes are removed if
they are unnecessary. For IPv6 domain literals, the IPv6 address is
normalized to condensed form.RFC 2142also requires lowercase normalization for some specific mailbox names likepostmaster@.Length checksThis library checks that the length of the email address is not longer than
the maximum length. The check is performed on the normalized form of the
address, which might be different from a string provided by a user. If you
send email to the original string and not the normalized address, the email
might be rejected because the original address could be too long.ExamplesFor the email addresstest@joshdata.me, the returned object is:ValidatedEmail(normalized='test@joshdata.me',local_part='test',domain='joshdata.me',ascii_email='test@joshdata.me',ascii_local_part='test',ascii_domain='joshdata.me',smtputf8=False)For the fictitious but valid addressexample@ツ.ⓁⒾⒻⒺ, which has an
internationalized domain but ASCII local part, the returned object is:ValidatedEmail(normalized='example@ツ.life',local_part='example',domain='ツ.life',ascii_email='example@xn--bdk.life',ascii_local_part='example',ascii_domain='xn--bdk.life',smtputf8=False)Note thatnormalizedand other fields provide a normalized form of the
email address, domain name, and (in other cases) local part (see earlier
discussion of normalization), which you should use in your database.Callingvalidate_emailwith the ASCII form of the above email address,example@xn--bdk.life, returns the exact same information (i.e., thenormalizedfield always will contain Unicode characters, not Punycode).For the fictitious addressツ-test@joshdata.me, which has an
internationalized local part, the returned object is:ValidatedEmail(normalized='ツ-test@joshdata.me',local_part='ツ-test',domain='joshdata.me',ascii_email=None,ascii_local_part=None,ascii_domain='joshdata.me',smtputf8=True)Nowsmtputf8isTrueandascii_emailisNonebecause the local
part of the address is internationalized. Thelocal_partandnormalizedfields
return the normalized form of the address.Return valueWhen an email address passes validation, the fields in the returned object
are:FieldValuenormalizedThe normalized form of the email address that you should put in your database. This combines thelocal_partanddomainfields (see below).ascii_emailIf set, an ASCII-only form of the normalized email address by replacing the domain part withIDNAPunycode. This field will be present when an ASCII-only form of the email address exists (including if the email address is already ASCII). If the local part of the email address contains internationalized characters,ascii_emailwill beNone. If set, it merely combinesascii_local_partandascii_domain.local_partThe normalized local part of the given email address (before the @-sign). Normalization includes Unicode NFC normalization and removing unnecessary quoted-string quotes and backslashes. Ifallow_quoted_localis True and the surrounding quotes are necessary, the quoteswillbe present in this field.ascii_local_partIf set, the local part, which is composed of ASCII characters only.domainThe canonical internationalized Unicode form of the domain part of the email address. If the returned string contains non-ASCII characters, either theSMTPUTF8feature of your mail relay will be required to transmit the message or else the email address's domain part must be converted to IDNA ASCII first: Useascii_domainfield instead.ascii_domainTheIDNAPunycode-encoded form of the domain part of the given email address, as it would be transmitted on the wire.domain_addressIf domain literals are allowed and if the email address contains one, anipaddress.IPv4Addressoripaddress.IPv6Addressobject.smtputf8A boolean indicating that theSMTPUTF8feature of your mail relay will be required to transmit messages to this address because the local part of the address has non-ASCII characters (the local part cannot be IDNA-encoded). Ifallow_smtputf8=Falseis passed as an argument, this flag will always be false because an exception is raised if it would have been true.mxA list of (priority, domain) tuples of MX records specified in the DNS for the domain (seeRFC 5321 section 5). May beNoneif the deliverability check could not be completed because of a temporary issue like a timeout.mx_fallback_typeNoneif anMXrecord is found. If no MX records are actually specified in DNS and instead are inferred, through an obsolete mechanism, from A or AAAA records, the value is the type of DNS record used instead (AorAAAA). May beNoneif the deliverability check could not be completed because of a temporary issue like a timeout.spfAny SPF record found while checking deliverability. Only set if the SPF record is queried.AssumptionsBy design, this validator does not pass all email addresses that
strictly conform to the standards. Many email address forms are obsolete
or likely to cause trouble:The validator assumes the email address is intended to be
usable on the public Internet. The domain part
of the email address must be a resolvable domain name
(see the deliverability checks described above).
MostSpecial Use Domain Namesand their subdomains, as well as
domain names without a., are rejected as a syntax error
(except see thetest_environmentparameter above).Obsolete email syntaxes are rejected:
The unusual"(comment)" syntaxis rejected. Extremely old obsolete syntaxes are
rejected. Quoted-string local parts and domain-literal addresses
are rejected by default, but there are options to allow them (see above).
No one uses these forms anymore, and I can't think of any reason why anyone
using this library would need to accept them.TestingTests can be run usingpipinstall-rtest_requirements.txtmaketestTests run with mocked DNS responses. When adding or changing tests, temporarily turn on theBUILD_MOCKED_DNS_RESPONSE_DATAflag intests/mocked_dns_responses.pyto re-build the database of mocked responses from live queries.For Project MaintainersThe package is distributed as a universal wheel and as a source package.To release:Update CHANGELOG.md.Update the version number inemail_validator/version.py.Make & push a commit with the new version number and make sure tests pass.Make & push a tag (see command below).Make a release athttps://github.com/JoshData/python-email-validator/releases/new.Publish a source and wheel distribution to pypi (see command below).gittagv$(catemail_validator/version.py|sed"s/.* = //"|sed's/"//g')gitpush--tags
./release_to_pypi.shLicenseThis project is free of any copyright restrictions per theUnlicense. (Prior to Feb. 4, 2024, the project was made available under the terms of theCC0 1.0 Universal public domain dedication.) SeeLICENSEandCONTRIBUTING.md.