View the [CHANGELOG / Release Notes](CHANGELOG.md) for the version history of changes in the library. Occasionally this README is ahead of the latest published package --- see the CHANGELOG for details.
---
Installation
------------
This package [is on PyPI](https://pypi.org/project/email-validator/), so:
```sh
pip install email-validator
```
`pip3` also works.
Quick Start
-----------
If you're validating a user's email address before creating a user
account in your application, you might do this:
```python
from email_validator import validate_email, EmailNotValidError
# may not take into account Unicode normalization).
email = validation.email
except EmailNotValidError as e:
# Email is not valid.
# The exception message is human-readable.
print(str(e))
```
This validates the address and gives you its normalized form. You should
**put the normalized form in your database** and always normalize before
checking if an address is in your database. When using this in a login form,
set `check_deliverability` to `False` to avoid unnecessary DNS queries.
Usage
-----
### Overview
The module provides a function `validate_email(email_address)` which
takes an email address (either a `str` or `bytes`, but only non-internationalized
addresses are allowed when passing a `bytes`) and:
- Raises a `EmailNotValidError` with a helpful, human-readable error
message explaining why the email address is not valid, or
- Returns 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_email` raises either an
`EmailSyntaxError` if the form of the address is invalid or an
`EmailUndeliverableError` if the domain name fails DNS checks. Both
exception classes are subclasses of `EmailNotValidError`, which in turn
is a subclass of `ValueError`.
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 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 that.)
The validator checks that the domain name in the email address has a
DNS MX record (except a NULL MX record) indicating that it can receive
email and that it does not have a reject-all SPF record (`v=spf1 -all`)
which would indicate that it cannot send email.
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).
### Options
The `validate_email` function also accepts the following keyword arguments
(defaults are as shown below):
`allow_smtputf8=True`: Set to `False` to prohibit internationalized addresses that would
require the
[SMTPUTF8](https://tools.ietf.org/html/rfc6531) extension. You can also set `email_validator.ALLOW_SMTPUTF8` to `False` to turn it off for all calls by default.
`check_deliverability=True`: If true, DNS queries check that a non-null MX (or A/AAAA record as an MX fallback) is present for the domain-part of the email address and that a reject-all SPF record is not present. Set to `False` to skip these DNS checks. DNS is slow and sometimes unavailable, so consider whether these checks are useful for your use case. It is recommended to pass `False` when performing validation for login pages (but not account creation pages) since re-validation of the domain by querying DNS at every login is probably undesirable. You can also set `email_validator.CHECK_DELIVERABILITY` to `False` to turn this off for all calls by default.
`allow_empty_local=False`: Set to `True` to allow an empty local part (i.e.
`@example.com`), e.g. for validating Postfix aliases.
`dns_resolver=None`: Pass an instance of [dns.resolver.Resolver](https://dnspython.readthedocs.io/en/latest/resolver-class.html) to control the DNS resolver including setting a timeout and [a cache](https://dnspython.readthedocs.io/en/latest/resolver-caching.html). The `caching_resolver` function shown above is a helper function to construct a dns.resolver.Resolver with a [LRUCache](https://dnspython.readthedocs.io/en/latest/resolver-caching.html#dns.resolver.LRUCache). Reuse the same resolver instance across calls to `validate_email` to make use of the cache.
`test_environment=False`: DNS-based deliverability checks are disabled and `test` and `subdomain.test` domain names are permitted (see below). You can also set `email_validator.TEST_ENVIRONMENT` to `True` to turn it on for all calls by default.
### DNS timeout and cache
When validating many email addresses or to control the timeout (the default is 15 seconds), create a caching [dns.resolver.Resolver](https://dnspython.readthedocs.io/en/latest/resolver-class.html) to reuse in each call. The `caching_resolver` function returns one easily for you:
```python
from email_validator import validate_email, caching_resolver
This library rejects email addresess that use the [Special Use Domain Names](https://www.iana.org/assignments/special-use-domain-names/special-use-domain-names.xhtml) `invalid`, `localhost`, `test`, and some others by raising `EmailSyntaxError`. 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 to `localhost`. However, in your non-production test environments you may want to use `@test` or `@myname.test` email addresses. There are three ways you can allow this:
1. Add `test_environment=True` to the call to `validate_email` (see above).
2. Set `email_validator.TEST_ENVIRONMENT` to `True`.
3. Remove the special-use domain name that you want to use from `email_validator.SPECIAL_USE_DOMAIN_NAMES`:
It is tempting to use `@example.com/net/org` in tests. 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 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 `@test` or `@myname.test` instead.
Internationalized email addresses
---------------------------------
The 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 is [internationalized domain names (RFC
5891)](https://tools.ietf.org/html/rfc5891), 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](https://www.rfc-editor.org/rfc/rfc3492.txt)"
form starting with `xn--`. 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. Note that
most web browsers are currently in transition between IDNA 2003 (RFC
3490) and IDNA 2008 (RFC 5891) and [compliance around the web is not
For the email address `test@joshdata.me`, the returned object is:
```python
ValidatedEmail(
email='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 address `example@ツ.life`, which has an
internationalized domain but ASCII local part, the returned object is:
```python
ValidatedEmail(
email='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 that `smtputf8` is `False` even though the domain part is
internationalized because
[SMTPUTF8](https://tools.ietf.org/html/rfc6531) is only needed if the
local part of the address is internationalized (the domain part can be
converted to IDNA ASCII Punycode). Also note that the `email` and `domain`
fields provide a normalized form of the email address and domain name
(casefolding and Unicode normalization as required by IDNA 2008).
Calling `validate_email` with the ASCII form of the above email address,
`example@xn--bdk.life`, returns the exact same information (i.e., the
`email` field always will contain Unicode characters, not Punycode).
For the fictitious address `ツ-test@joshdata.me`, which has an
internationalized local part, the returned object is:
```python
ValidatedEmail(
email='ツ-test@joshdata.me',
local_part='ツ-test',
domain='joshdata.me',
ascii_email=None,
ascii_local_part=None,
ascii_domain='joshdata.me',
smtputf8=True)
```
Now `smtputf8` is `True` and `ascii_email` is `None` because the local
part of the address is internationalized. The `local_part` and `email` fields
return the normalized form of the address: certain Unicode characters
(such as angstrom and ohm) may be replaced by other equivalent code
points (a-with-ring and omega).
Return value
------------
When an email address passes validation, the fields in the returned object
are:
| Field | Value |
| -----:|-------|
| `email` | The normalized form of the email address that you should put in your database. This merely combines the `local_part` and `domain` fields (see below). |
| `ascii_email` | If set, an ASCII-only form of the email address by replacing the domain part with [IDNA](https://tools.ietf.org/html/rfc5891) [Punycode](https://www.rfc-editor.org/rfc/rfc3492.txt). 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_email` will be `None`. If set, it merely combines `ascii_local_part` and `ascii_domain`. |
| `local_part` | The local part of the given email address (before the @-sign) with Unicode NFC normalization applied. |
| `ascii_local_part` | If set, the local part, which is composed of ASCII characters only. |
| `domain` | The canonical internationalized Unicode form of the domain part of the email address. If the returned string contains non-ASCII characters, either the [SMTPUTF8](https://tools.ietf.org/html/rfc6531) feature 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: Use `ascii_domain` field instead. |
| `ascii_domain` | The [IDNA](https://tools.ietf.org/html/rfc5891) [Punycode](https://www.rfc-editor.org/rfc/rfc3492.txt)-encoded form of the domain part of the given email address, as it would be transmitted on the wire. |
| `smtputf8` | A boolean indicating that the [SMTPUTF8](https://tools.ietf.org/html/rfc6531) feature 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). If `allow_smtputf8=False` is passed as an argument, this flag will always be false because an exception is raised if it would have been true. |
| `mx` | A list of (priority, domain) tuples of MX records specified in the DNS for the domain (see [RFC 5321 section 5](https://tools.ietf.org/html/rfc5321#section-5)). May be `None` if the deliverability check could not be completed because of a temporary issue like a timeout. |
| `mx_fallback_type` | `None` if an `MX` record 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 (`A` or `AAAA`). May be `None` if the deliverability check could not be completed because of a temporary issue like a timeout. |
| `spf` | Any SPF record found while checking deliverability. |
Assumptions
-----------
By 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
(without NULL MX or SPF -all DNS records) if deliverability
checks are turned on.
Most [Special Use Domain Names](https://www.iana.org/assignments/special-use-domain-names/special-use-domain-names.xhtml)
and their subdomains and
domain names without a `.` are rejected as a syntax error
(except see the `test_environment` parameter above).
* Obsolete email syntaxes are rejected:
The "quoted string" form of the local part of the email address (RFC
5321 4.1.2) is not permitted.
Quoted forms allow multiple @-signs, space characters, and other
troublesome conditions. The unsual [(comment) syntax](https://github.com/JoshData/python-email-validator/issues/77)
is also rejected. The "literal" form for the domain part of an email address (an
IP address in brackets) is rejected. Other obsolete and deprecated syntaxes are
rejected. No one uses these forms anymore.
Testing
-------
Tests can be run using
```sh
pip install -r test_requirements.txt
make test
```
For Project Maintainers
-----------------------
The package is distributed as a universal wheel and as a source package.
To release:
* Update CHANGELOG.md.
* Update the version number in setup.cfg.
* Make a commit with the new version number.
* Follow the steps below to publish source and a universal wheel to pypi and tag the release.
* Make a release at https://github.com/JoshData/python-email-validator/releases/new.
```sh
./release_to_pypi.sh
git tag v1.0.XXX # replace with version in setup.cfg
git push --tags
```
Notes: The wheel is specified as universal in the file `setup.cfg` by the `universal = 1` key in the
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.