email-validator 2.1.2__tar.gz → 2.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {email_validator-2.1.2/email_validator.egg-info → email_validator-2.3.0}/PKG-INFO +51 -71
- {email_validator-2.1.2 → email_validator-2.3.0}/README.md +48 -69
- {email_validator-2.1.2 → email_validator-2.3.0}/email_validator/__init__.py +13 -6
- {email_validator-2.1.2 → email_validator-2.3.0}/email_validator/__main__.py +9 -7
- {email_validator-2.1.2 → email_validator-2.3.0}/email_validator/deliverability.py +16 -10
- email_validator-2.3.0/email_validator/exceptions.py +13 -0
- {email_validator-2.1.2 → email_validator-2.3.0}/email_validator/rfc_constants.py +16 -5
- {email_validator-2.1.2 → email_validator-2.3.0}/email_validator/syntax.py +372 -113
- email_validator-2.1.2/email_validator/exceptions_types.py → email_validator-2.3.0/email_validator/types.py +16 -31
- {email_validator-2.1.2 → email_validator-2.3.0}/email_validator/validate_email.py +89 -26
- email_validator-2.3.0/email_validator/version.py +1 -0
- {email_validator-2.1.2 → email_validator-2.3.0/email_validator.egg-info}/PKG-INFO +51 -71
- {email_validator-2.1.2 → email_validator-2.3.0}/email_validator.egg-info/SOURCES.txt +2 -1
- email_validator-2.3.0/pyproject.toml +17 -0
- {email_validator-2.1.2 → email_validator-2.3.0}/setup.cfg +1 -1
- {email_validator-2.1.2 → email_validator-2.3.0}/tests/mocked_dns_response.py +29 -23
- {email_validator-2.1.2 → email_validator-2.3.0}/tests/test_deliverability.py +11 -9
- {email_validator-2.1.2 → email_validator-2.3.0}/tests/test_main.py +6 -6
- {email_validator-2.1.2 → email_validator-2.3.0}/tests/test_syntax.py +160 -48
- email_validator-2.1.2/email_validator/version.py +0 -1
- email_validator-2.1.2/pyproject.toml +0 -4
- {email_validator-2.1.2 → email_validator-2.3.0}/LICENSE +0 -0
- {email_validator-2.1.2 → email_validator-2.3.0}/MANIFEST.in +0 -0
- {email_validator-2.1.2 → email_validator-2.3.0}/email_validator/py.typed +0 -0
- {email_validator-2.1.2 → email_validator-2.3.0}/email_validator.egg-info/dependency_links.txt +0 -0
- {email_validator-2.1.2 → email_validator-2.3.0}/email_validator.egg-info/entry_points.txt +0 -0
- {email_validator-2.1.2 → email_validator-2.3.0}/email_validator.egg-info/requires.txt +0 -0
- {email_validator-2.1.2 → email_validator-2.3.0}/email_validator.egg-info/top_level.txt +0 -0
- {email_validator-2.1.2 → email_validator-2.3.0}/setup.py +0 -0
- {email_validator-2.1.2 → email_validator-2.3.0}/tests/mocked-dns-answers.json +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
2
|
-
Name:
|
|
3
|
-
Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: email-validator
|
|
3
|
+
Version: 2.3.0
|
|
4
4
|
Summary: A robust email address syntax and deliverability validation library.
|
|
5
5
|
Home-page: https://github.com/JoshData/python-email-validator
|
|
6
6
|
Author: Joshua Tauberer
|
|
@@ -22,6 +22,7 @@ Description-Content-Type: text/markdown
|
|
|
22
22
|
License-File: LICENSE
|
|
23
23
|
Requires-Dist: dnspython>=2.0.0
|
|
24
24
|
Requires-Dist: idna>=2.0.0
|
|
25
|
+
Dynamic: license-file
|
|
25
26
|
|
|
26
27
|
email-validator: Validate Email Addresses
|
|
27
28
|
=========================================
|
|
@@ -32,30 +33,31 @@ Python 3.8+ by [Joshua Tauberer](https://joshdata.me).
|
|
|
32
33
|
This library validates that a string is of the form `name@example.com`
|
|
33
34
|
and optionally checks that the domain name is set up to receive email.
|
|
34
35
|
This is the sort of validation you would want when you are identifying
|
|
35
|
-
users by their email address like on a registration
|
|
36
|
-
necessarily for composing an email message, see below).
|
|
36
|
+
users by their email address like on a registration form.
|
|
37
37
|
|
|
38
38
|
Key features:
|
|
39
39
|
|
|
40
40
|
* Checks that an email address has the correct syntax --- great for
|
|
41
|
-
email-based registration/login forms or
|
|
41
|
+
email-based registration/login forms or validating data.
|
|
42
42
|
* Gives friendly English error messages when validation fails that you
|
|
43
43
|
can display to end-users.
|
|
44
44
|
* Checks deliverability (optional): Does the domain name resolve?
|
|
45
45
|
(You can override the default DNS resolver to add query caching.)
|
|
46
|
-
* Supports internationalized domain names
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
46
|
+
* Supports internationalized domain names (like `@ツ.life`),
|
|
47
|
+
internationalized local parts (like `ツ@example.com`),
|
|
48
|
+
and optionally parses display names (e.g. `"My Name" <me@example.com>`).
|
|
49
|
+
* Rejects addresses with invalid or unsafe Unicode characters,
|
|
50
|
+
obsolete email address syntax that you'd find unexpected,
|
|
51
|
+
special use domain names like `@localhost`,
|
|
52
|
+
and domains without a dot by default.
|
|
53
|
+
This is an opinionated library!
|
|
51
54
|
* Normalizes email addresses (important for internationalized
|
|
52
55
|
and quoted-string addresses! see below).
|
|
53
56
|
* Python type annotations are used.
|
|
54
57
|
|
|
55
58
|
This is an opinionated library. You should definitely also consider using
|
|
56
|
-
the less-opinionated [pyIsEmail](https://github.com/michaelherold/pyIsEmail)
|
|
57
|
-
|
|
58
|
-
use case.
|
|
59
|
+
the less-opinionated [pyIsEmail](https://github.com/michaelherold/pyIsEmail)
|
|
60
|
+
if it works better for you.
|
|
59
61
|
|
|
60
62
|
[](https://github.com/JoshData/python-email-validator/actions/workflows/test_and_build.yaml)
|
|
61
63
|
|
|
@@ -167,11 +169,14 @@ The `validate_email` function also accepts the following keyword arguments
|
|
|
167
169
|
|
|
168
170
|
`allow_quoted_local=False`: Set to `True` to 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 by `validate_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 set `email_validator.ALLOW_QUOTED_LOCAL` to `True` to turn this on for all calls by default.
|
|
169
171
|
|
|
170
|
-
`allow_domain_literal=False`: Set to `True` to allow bracketed IPv4 and "IPv6:"-
|
|
172
|
+
`allow_domain_literal=False`: Set to `True` to allow bracketed IPv4 and "IPv6:"-prefixed IPv6 addresses in the domain part of the email address. No deliverability checks are performed for these addresses. In the object returned by `validate_email`, the normalized domain will use the condensed IPv6 format, if applicable. The object's `domain_address` attribute will hold the parsed `ipaddress.IPv4Address` or `ipaddress.IPv6Address` object if applicable. You can also set `email_validator.ALLOW_DOMAIN_LITERAL` to `True` to turn this on for all calls by default.
|
|
173
|
+
|
|
174
|
+
`allow_display_name=False`: Set to `True` to allow a display name and bracketed address in the input string, like `My Name <me@example.org>`. It's implemented in the spirit but not the letter of RFC 5322 3.4, so it may be stricter or more relaxed than what you want. The display name, if present, is provided in the returned object's `display_name` field after being unquoted and unescaped. You can also set `email_validator.ALLOW_DISPLAY_NAME` to `True` to turn this on for all calls by default.
|
|
171
175
|
|
|
172
176
|
`allow_empty_local=False`: Set to `True` to allow an empty local part (i.e.
|
|
173
177
|
`@example.com`), e.g. for validating Postfix aliases.
|
|
174
178
|
|
|
179
|
+
`strict=False`: Set to `True` to perform additional syntax checks (currently only a local part length check). This should be used by mail service providers at address creation to ensure email addresses meet broad compatibility requirements.
|
|
175
180
|
|
|
176
181
|
### DNS timeout and cache
|
|
177
182
|
|
|
@@ -207,8 +212,12 @@ Internationalized email addresses
|
|
|
207
212
|
The email protocol SMTP and the domain name system DNS have historically
|
|
208
213
|
only allowed English (ASCII) characters in email addresses and domain names,
|
|
209
214
|
respectively. Each has adapted to internationalization in a separate
|
|
210
|
-
way, creating two separate aspects to email address
|
|
211
|
-
|
|
215
|
+
way, creating two separate aspects to email address internationalization.
|
|
216
|
+
|
|
217
|
+
(If your mail submission library doesn't support Unicode at all, then
|
|
218
|
+
immediately prior to mail submission you must replace the email address with
|
|
219
|
+
its ASCII-ized form. This library gives you back the ASCII-ized form in the
|
|
220
|
+
`ascii_email` field in the returned object.)
|
|
212
221
|
|
|
213
222
|
### Internationalized domain names (IDN)
|
|
214
223
|
|
|
@@ -231,6 +240,19 @@ email addresses, only English letters, numbers, and some punctuation
|
|
|
231
240
|
(`._!#$%&'^``*+-=~/?{|}`) are allowed. In internationalized email address
|
|
232
241
|
local parts, a wider range of Unicode characters are allowed.
|
|
233
242
|
|
|
243
|
+
Email addresses with these non-ASCII characters require that your mail
|
|
244
|
+
submission library and all the mail servers along the route to the destination,
|
|
245
|
+
including your own outbound mail server, all support the
|
|
246
|
+
[SMTPUTF8 (RFC 6531)](https://tools.ietf.org/html/rfc6531) extension.
|
|
247
|
+
Support for SMTPUTF8 varies. If you know ahead of time that SMTPUTF8 is not
|
|
248
|
+
supported by your mail submission stack, then you must filter out addresses that
|
|
249
|
+
require SMTPUTF8 using the `allow_smtputf8=False` keyword argument (see above).
|
|
250
|
+
This will cause the validation function to raise a `EmailSyntaxError` if
|
|
251
|
+
delivery would require SMTPUTF8. If you do not set `allow_smtputf8=False`,
|
|
252
|
+
you can also check the value of the `smtputf8` field in the returned object.
|
|
253
|
+
|
|
254
|
+
### Unsafe Unicode characters are rejected
|
|
255
|
+
|
|
234
256
|
A surprisingly large number of Unicode characters are not safe to display,
|
|
235
257
|
especially when the email address is concatenated with other text, so this
|
|
236
258
|
library tries to protect you by not permitting reserved, non-, private use,
|
|
@@ -241,48 +263,10 @@ cannot combine with something outside of the email address string or with
|
|
|
241
263
|
the @-sign). See https://qntm.org/safe and https://trojansource.codes/
|
|
242
264
|
for relevant prior work. (Other than whitespace, these are checks that
|
|
243
265
|
you should be applying to nearly all user inputs in a security-sensitive
|
|
244
|
-
context.)
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
so you are only fully protected if you replace all user-provided email addresses
|
|
248
|
-
with the normalized email address string returned by this library. This does not
|
|
249
|
-
guard against the well known problem that many Unicode characters look alike
|
|
250
|
-
(or are identical), which can be used to fool humans reading displayed text.
|
|
251
|
-
|
|
252
|
-
Email addresses with these non-ASCII characters require that your mail
|
|
253
|
-
submission library and the mail servers along the route to the destination,
|
|
254
|
-
including your own outbound mail server, all support the
|
|
255
|
-
[SMTPUTF8 (RFC 6531)](https://tools.ietf.org/html/rfc6531) extension.
|
|
256
|
-
Support for SMTPUTF8 varies. See the `allow_smtputf8` parameter.
|
|
257
|
-
|
|
258
|
-
### If you know ahead of time that SMTPUTF8 is not supported by your mail submission stack
|
|
266
|
+
context.) This does not guard against the well known problem that many
|
|
267
|
+
Unicode characters look alike, which can be used to fool humans reading
|
|
268
|
+
displayed text.
|
|
259
269
|
|
|
260
|
-
By default all internationalized forms are accepted by the validator.
|
|
261
|
-
But if you know ahead of time that SMTPUTF8 is not supported by your
|
|
262
|
-
mail submission stack, then you must filter out addresses that require
|
|
263
|
-
SMTPUTF8 using the `allow_smtputf8=False` keyword argument (see above).
|
|
264
|
-
This will cause the validation function to raise a `EmailSyntaxError` if
|
|
265
|
-
delivery would require SMTPUTF8. That's just in those cases where
|
|
266
|
-
non-ASCII characters appear before the @-sign. If you do not set
|
|
267
|
-
`allow_smtputf8=False`, you can also check the value of the `smtputf8`
|
|
268
|
-
field in the returned object.
|
|
269
|
-
|
|
270
|
-
If your mail submission library doesn't support Unicode at all --- even
|
|
271
|
-
in the domain part of the address --- then immediately prior to mail
|
|
272
|
-
submission you must replace the email address with its ASCII-ized form.
|
|
273
|
-
This library gives you back the ASCII-ized form in the `ascii_email`
|
|
274
|
-
field in the returned object, which you can get like this:
|
|
275
|
-
|
|
276
|
-
```python
|
|
277
|
-
emailinfo = validate_email(email, allow_smtputf8=False)
|
|
278
|
-
email = emailinfo.ascii_email
|
|
279
|
-
```
|
|
280
|
-
|
|
281
|
-
The local part is left alone (if it has internationalized characters
|
|
282
|
-
`allow_smtputf8=False` will force validation to fail) and the domain
|
|
283
|
-
part is converted to [IDNA ASCII](https://tools.ietf.org/html/rfc5891).
|
|
284
|
-
(You probably should not do this at account creation time so you don't
|
|
285
|
-
change the user's login information without telling them.)
|
|
286
270
|
|
|
287
271
|
Normalization
|
|
288
272
|
-------------
|
|
@@ -297,7 +281,7 @@ address.
|
|
|
297
281
|
|
|
298
282
|
For example, the CJK fullwidth Latin letters are considered semantically
|
|
299
283
|
equivalent in domain names to their ASCII counterparts. This library
|
|
300
|
-
normalizes them to their ASCII counterparts:
|
|
284
|
+
normalizes them to their ASCII counterparts (as required by IDNA):
|
|
301
285
|
|
|
302
286
|
```python
|
|
303
287
|
emailinfo = validate_email("me@Domain.com")
|
|
@@ -310,9 +294,7 @@ Because an end-user might type their email address in different (but
|
|
|
310
294
|
equivalent) un-normalized forms at different times, you ought to
|
|
311
295
|
replace what they enter with the normalized form immediately prior to
|
|
312
296
|
going into your database (during account creation), querying your database
|
|
313
|
-
(during login), or sending outbound mail.
|
|
314
|
-
the length of an email address, and this may affect whether it is valid
|
|
315
|
-
and acceptable by your SMTP provider.
|
|
297
|
+
(during login), or sending outbound mail.
|
|
316
298
|
|
|
317
299
|
The normalizations include lowercasing the domain part of the email
|
|
318
300
|
address (domain names are case-insensitive), [Unicode "NFC"
|
|
@@ -326,6 +308,11 @@ in the domain part, possibly other
|
|
|
326
308
|
[UTS46](http://unicode.org/reports/tr46) mappings on the domain part,
|
|
327
309
|
and conversion from Punycode to Unicode characters.
|
|
328
310
|
|
|
311
|
+
Normalization may change the characters in the email address and the
|
|
312
|
+
length of the email address, such that a string might be a valid address
|
|
313
|
+
before normalization but invalid after, or vice versa. This library only
|
|
314
|
+
permits addresses that are valid both before and after normalization.
|
|
315
|
+
|
|
329
316
|
(See [RFC 6532 (internationalized email) section
|
|
330
317
|
3.1](https://tools.ietf.org/html/rfc6532#section-3.1) and [RFC 5895
|
|
331
318
|
(IDNA 2008) section 2](http://www.ietf.org/rfc/rfc5895.txt).)
|
|
@@ -340,13 +327,6 @@ they are unnecessary. For IPv6 domain literals, the IPv6 address is
|
|
|
340
327
|
normalized to condensed form. [RFC 2142](https://datatracker.ietf.org/doc/html/rfc2142)
|
|
341
328
|
also requires lowercase normalization for some specific mailbox names like `postmaster@`.
|
|
342
329
|
|
|
343
|
-
### Length checks
|
|
344
|
-
|
|
345
|
-
This library checks that the length of the email address is not longer than
|
|
346
|
-
the maximum length. The check is performed on the normalized form of the
|
|
347
|
-
address, which might be different from a string provided by a user. If you
|
|
348
|
-
send email to the original string and not the normalized address, the email
|
|
349
|
-
might be rejected because the original address could be too long.
|
|
350
330
|
|
|
351
331
|
Examples
|
|
352
332
|
--------
|
|
@@ -420,6 +400,7 @@ are:
|
|
|
420
400
|
| `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. |
|
|
421
401
|
| `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. |
|
|
422
402
|
| `domain_address` | If domain literals are allowed and if the email address contains one, an `ipaddress.IPv4Address` or `ipaddress.IPv6Address` object. |
|
|
403
|
+
| `display_name` | If no display name was present and angle brackets do not surround the address, this will be `None`; otherwise, it will be set to the display name, or the empty string if there were angle brackets but no display name. If the display name was quoted, it will be unquoted and unescaped. |
|
|
423
404
|
| `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. |
|
|
424
405
|
| `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. |
|
|
425
406
|
| `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. |
|
|
@@ -470,8 +451,7 @@ To release:
|
|
|
470
451
|
* Update CHANGELOG.md.
|
|
471
452
|
* Update the version number in `email_validator/version.py`.
|
|
472
453
|
* Make & push a commit with the new version number and make sure tests pass.
|
|
473
|
-
* Make
|
|
474
|
-
* Make a release at https://github.com/JoshData/python-email-validator/releases/new.
|
|
454
|
+
* Make a release at https://github.com/JoshData/python-email-validator/releases/new creating a new tag (or use command below).
|
|
475
455
|
* Publish a source and wheel distribution to pypi (see command below).
|
|
476
456
|
|
|
477
457
|
```sh
|
|
@@ -7,30 +7,31 @@ Python 3.8+ by [Joshua Tauberer](https://joshdata.me).
|
|
|
7
7
|
This library validates that a string is of the form `name@example.com`
|
|
8
8
|
and optionally checks that the domain name is set up to receive email.
|
|
9
9
|
This is the sort of validation you would want when you are identifying
|
|
10
|
-
users by their email address like on a registration
|
|
11
|
-
necessarily for composing an email message, see below).
|
|
10
|
+
users by their email address like on a registration form.
|
|
12
11
|
|
|
13
12
|
Key features:
|
|
14
13
|
|
|
15
14
|
* Checks that an email address has the correct syntax --- great for
|
|
16
|
-
email-based registration/login forms or
|
|
15
|
+
email-based registration/login forms or validating data.
|
|
17
16
|
* Gives friendly English error messages when validation fails that you
|
|
18
17
|
can display to end-users.
|
|
19
18
|
* Checks deliverability (optional): Does the domain name resolve?
|
|
20
19
|
(You can override the default DNS resolver to add query caching.)
|
|
21
|
-
* Supports internationalized domain names
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
20
|
+
* Supports internationalized domain names (like `@ツ.life`),
|
|
21
|
+
internationalized local parts (like `ツ@example.com`),
|
|
22
|
+
and optionally parses display names (e.g. `"My Name" <me@example.com>`).
|
|
23
|
+
* Rejects addresses with invalid or unsafe Unicode characters,
|
|
24
|
+
obsolete email address syntax that you'd find unexpected,
|
|
25
|
+
special use domain names like `@localhost`,
|
|
26
|
+
and domains without a dot by default.
|
|
27
|
+
This is an opinionated library!
|
|
26
28
|
* Normalizes email addresses (important for internationalized
|
|
27
29
|
and quoted-string addresses! see below).
|
|
28
30
|
* Python type annotations are used.
|
|
29
31
|
|
|
30
32
|
This is an opinionated library. You should definitely also consider using
|
|
31
|
-
the less-opinionated [pyIsEmail](https://github.com/michaelherold/pyIsEmail)
|
|
32
|
-
|
|
33
|
-
use case.
|
|
33
|
+
the less-opinionated [pyIsEmail](https://github.com/michaelherold/pyIsEmail)
|
|
34
|
+
if it works better for you.
|
|
34
35
|
|
|
35
36
|
[](https://github.com/JoshData/python-email-validator/actions/workflows/test_and_build.yaml)
|
|
36
37
|
|
|
@@ -142,11 +143,14 @@ The `validate_email` function also accepts the following keyword arguments
|
|
|
142
143
|
|
|
143
144
|
`allow_quoted_local=False`: Set to `True` to 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 by `validate_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 set `email_validator.ALLOW_QUOTED_LOCAL` to `True` to turn this on for all calls by default.
|
|
144
145
|
|
|
145
|
-
`allow_domain_literal=False`: Set to `True` to allow bracketed IPv4 and "IPv6:"-
|
|
146
|
+
`allow_domain_literal=False`: Set to `True` to allow bracketed IPv4 and "IPv6:"-prefixed IPv6 addresses in the domain part of the email address. No deliverability checks are performed for these addresses. In the object returned by `validate_email`, the normalized domain will use the condensed IPv6 format, if applicable. The object's `domain_address` attribute will hold the parsed `ipaddress.IPv4Address` or `ipaddress.IPv6Address` object if applicable. You can also set `email_validator.ALLOW_DOMAIN_LITERAL` to `True` to turn this on for all calls by default.
|
|
147
|
+
|
|
148
|
+
`allow_display_name=False`: Set to `True` to allow a display name and bracketed address in the input string, like `My Name <me@example.org>`. It's implemented in the spirit but not the letter of RFC 5322 3.4, so it may be stricter or more relaxed than what you want. The display name, if present, is provided in the returned object's `display_name` field after being unquoted and unescaped. You can also set `email_validator.ALLOW_DISPLAY_NAME` to `True` to turn this on for all calls by default.
|
|
146
149
|
|
|
147
150
|
`allow_empty_local=False`: Set to `True` to allow an empty local part (i.e.
|
|
148
151
|
`@example.com`), e.g. for validating Postfix aliases.
|
|
149
152
|
|
|
153
|
+
`strict=False`: Set to `True` to perform additional syntax checks (currently only a local part length check). This should be used by mail service providers at address creation to ensure email addresses meet broad compatibility requirements.
|
|
150
154
|
|
|
151
155
|
### DNS timeout and cache
|
|
152
156
|
|
|
@@ -182,8 +186,12 @@ Internationalized email addresses
|
|
|
182
186
|
The email protocol SMTP and the domain name system DNS have historically
|
|
183
187
|
only allowed English (ASCII) characters in email addresses and domain names,
|
|
184
188
|
respectively. Each has adapted to internationalization in a separate
|
|
185
|
-
way, creating two separate aspects to email address
|
|
186
|
-
|
|
189
|
+
way, creating two separate aspects to email address internationalization.
|
|
190
|
+
|
|
191
|
+
(If your mail submission library doesn't support Unicode at all, then
|
|
192
|
+
immediately prior to mail submission you must replace the email address with
|
|
193
|
+
its ASCII-ized form. This library gives you back the ASCII-ized form in the
|
|
194
|
+
`ascii_email` field in the returned object.)
|
|
187
195
|
|
|
188
196
|
### Internationalized domain names (IDN)
|
|
189
197
|
|
|
@@ -206,6 +214,19 @@ email addresses, only English letters, numbers, and some punctuation
|
|
|
206
214
|
(`._!#$%&'^``*+-=~/?{|}`) are allowed. In internationalized email address
|
|
207
215
|
local parts, a wider range of Unicode characters are allowed.
|
|
208
216
|
|
|
217
|
+
Email addresses with these non-ASCII characters require that your mail
|
|
218
|
+
submission library and all the mail servers along the route to the destination,
|
|
219
|
+
including your own outbound mail server, all support the
|
|
220
|
+
[SMTPUTF8 (RFC 6531)](https://tools.ietf.org/html/rfc6531) extension.
|
|
221
|
+
Support for SMTPUTF8 varies. If you know ahead of time that SMTPUTF8 is not
|
|
222
|
+
supported by your mail submission stack, then you must filter out addresses that
|
|
223
|
+
require SMTPUTF8 using the `allow_smtputf8=False` keyword argument (see above).
|
|
224
|
+
This will cause the validation function to raise a `EmailSyntaxError` if
|
|
225
|
+
delivery would require SMTPUTF8. If you do not set `allow_smtputf8=False`,
|
|
226
|
+
you can also check the value of the `smtputf8` field in the returned object.
|
|
227
|
+
|
|
228
|
+
### Unsafe Unicode characters are rejected
|
|
229
|
+
|
|
209
230
|
A surprisingly large number of Unicode characters are not safe to display,
|
|
210
231
|
especially when the email address is concatenated with other text, so this
|
|
211
232
|
library tries to protect you by not permitting reserved, non-, private use,
|
|
@@ -216,48 +237,10 @@ cannot combine with something outside of the email address string or with
|
|
|
216
237
|
the @-sign). See https://qntm.org/safe and https://trojansource.codes/
|
|
217
238
|
for relevant prior work. (Other than whitespace, these are checks that
|
|
218
239
|
you should be applying to nearly all user inputs in a security-sensitive
|
|
219
|
-
context.)
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
so you are only fully protected if you replace all user-provided email addresses
|
|
223
|
-
with the normalized email address string returned by this library. This does not
|
|
224
|
-
guard against the well known problem that many Unicode characters look alike
|
|
225
|
-
(or are identical), which can be used to fool humans reading displayed text.
|
|
226
|
-
|
|
227
|
-
Email addresses with these non-ASCII characters require that your mail
|
|
228
|
-
submission library and the mail servers along the route to the destination,
|
|
229
|
-
including your own outbound mail server, all support the
|
|
230
|
-
[SMTPUTF8 (RFC 6531)](https://tools.ietf.org/html/rfc6531) extension.
|
|
231
|
-
Support for SMTPUTF8 varies. See the `allow_smtputf8` parameter.
|
|
232
|
-
|
|
233
|
-
### If you know ahead of time that SMTPUTF8 is not supported by your mail submission stack
|
|
240
|
+
context.) This does not guard against the well known problem that many
|
|
241
|
+
Unicode characters look alike, which can be used to fool humans reading
|
|
242
|
+
displayed text.
|
|
234
243
|
|
|
235
|
-
By default all internationalized forms are accepted by the validator.
|
|
236
|
-
But if you know ahead of time that SMTPUTF8 is not supported by your
|
|
237
|
-
mail submission stack, then you must filter out addresses that require
|
|
238
|
-
SMTPUTF8 using the `allow_smtputf8=False` keyword argument (see above).
|
|
239
|
-
This will cause the validation function to raise a `EmailSyntaxError` if
|
|
240
|
-
delivery would require SMTPUTF8. That's just in those cases where
|
|
241
|
-
non-ASCII characters appear before the @-sign. If you do not set
|
|
242
|
-
`allow_smtputf8=False`, you can also check the value of the `smtputf8`
|
|
243
|
-
field in the returned object.
|
|
244
|
-
|
|
245
|
-
If your mail submission library doesn't support Unicode at all --- even
|
|
246
|
-
in the domain part of the address --- then immediately prior to mail
|
|
247
|
-
submission you must replace the email address with its ASCII-ized form.
|
|
248
|
-
This library gives you back the ASCII-ized form in the `ascii_email`
|
|
249
|
-
field in the returned object, which you can get like this:
|
|
250
|
-
|
|
251
|
-
```python
|
|
252
|
-
emailinfo = validate_email(email, allow_smtputf8=False)
|
|
253
|
-
email = emailinfo.ascii_email
|
|
254
|
-
```
|
|
255
|
-
|
|
256
|
-
The local part is left alone (if it has internationalized characters
|
|
257
|
-
`allow_smtputf8=False` will force validation to fail) and the domain
|
|
258
|
-
part is converted to [IDNA ASCII](https://tools.ietf.org/html/rfc5891).
|
|
259
|
-
(You probably should not do this at account creation time so you don't
|
|
260
|
-
change the user's login information without telling them.)
|
|
261
244
|
|
|
262
245
|
Normalization
|
|
263
246
|
-------------
|
|
@@ -272,7 +255,7 @@ address.
|
|
|
272
255
|
|
|
273
256
|
For example, the CJK fullwidth Latin letters are considered semantically
|
|
274
257
|
equivalent in domain names to their ASCII counterparts. This library
|
|
275
|
-
normalizes them to their ASCII counterparts:
|
|
258
|
+
normalizes them to their ASCII counterparts (as required by IDNA):
|
|
276
259
|
|
|
277
260
|
```python
|
|
278
261
|
emailinfo = validate_email("me@Domain.com")
|
|
@@ -285,9 +268,7 @@ Because an end-user might type their email address in different (but
|
|
|
285
268
|
equivalent) un-normalized forms at different times, you ought to
|
|
286
269
|
replace what they enter with the normalized form immediately prior to
|
|
287
270
|
going into your database (during account creation), querying your database
|
|
288
|
-
(during login), or sending outbound mail.
|
|
289
|
-
the length of an email address, and this may affect whether it is valid
|
|
290
|
-
and acceptable by your SMTP provider.
|
|
271
|
+
(during login), or sending outbound mail.
|
|
291
272
|
|
|
292
273
|
The normalizations include lowercasing the domain part of the email
|
|
293
274
|
address (domain names are case-insensitive), [Unicode "NFC"
|
|
@@ -301,6 +282,11 @@ in the domain part, possibly other
|
|
|
301
282
|
[UTS46](http://unicode.org/reports/tr46) mappings on the domain part,
|
|
302
283
|
and conversion from Punycode to Unicode characters.
|
|
303
284
|
|
|
285
|
+
Normalization may change the characters in the email address and the
|
|
286
|
+
length of the email address, such that a string might be a valid address
|
|
287
|
+
before normalization but invalid after, or vice versa. This library only
|
|
288
|
+
permits addresses that are valid both before and after normalization.
|
|
289
|
+
|
|
304
290
|
(See [RFC 6532 (internationalized email) section
|
|
305
291
|
3.1](https://tools.ietf.org/html/rfc6532#section-3.1) and [RFC 5895
|
|
306
292
|
(IDNA 2008) section 2](http://www.ietf.org/rfc/rfc5895.txt).)
|
|
@@ -315,13 +301,6 @@ they are unnecessary. For IPv6 domain literals, the IPv6 address is
|
|
|
315
301
|
normalized to condensed form. [RFC 2142](https://datatracker.ietf.org/doc/html/rfc2142)
|
|
316
302
|
also requires lowercase normalization for some specific mailbox names like `postmaster@`.
|
|
317
303
|
|
|
318
|
-
### Length checks
|
|
319
|
-
|
|
320
|
-
This library checks that the length of the email address is not longer than
|
|
321
|
-
the maximum length. The check is performed on the normalized form of the
|
|
322
|
-
address, which might be different from a string provided by a user. If you
|
|
323
|
-
send email to the original string and not the normalized address, the email
|
|
324
|
-
might be rejected because the original address could be too long.
|
|
325
304
|
|
|
326
305
|
Examples
|
|
327
306
|
--------
|
|
@@ -395,6 +374,7 @@ are:
|
|
|
395
374
|
| `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. |
|
|
396
375
|
| `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. |
|
|
397
376
|
| `domain_address` | If domain literals are allowed and if the email address contains one, an `ipaddress.IPv4Address` or `ipaddress.IPv6Address` object. |
|
|
377
|
+
| `display_name` | If no display name was present and angle brackets do not surround the address, this will be `None`; otherwise, it will be set to the display name, or the empty string if there were angle brackets but no display name. If the display name was quoted, it will be unquoted and unescaped. |
|
|
398
378
|
| `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. |
|
|
399
379
|
| `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. |
|
|
400
380
|
| `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. |
|
|
@@ -445,8 +425,7 @@ To release:
|
|
|
445
425
|
* Update CHANGELOG.md.
|
|
446
426
|
* Update the version number in `email_validator/version.py`.
|
|
447
427
|
* Make & push a commit with the new version number and make sure tests pass.
|
|
448
|
-
* Make
|
|
449
|
-
* Make a release at https://github.com/JoshData/python-email-validator/releases/new.
|
|
428
|
+
* Make a release at https://github.com/JoshData/python-email-validator/releases/new creating a new tag (or use command below).
|
|
450
429
|
* Publish a source and wheel distribution to pypi (see command below).
|
|
451
430
|
|
|
452
431
|
```sh
|
|
@@ -458,4 +437,4 @@ git push --tags
|
|
|
458
437
|
License
|
|
459
438
|
-------
|
|
460
439
|
|
|
461
|
-
This project is free of any copyright restrictions per the [Unlicense](https://unlicense.org/). (Prior to Feb. 4, 2024, the project was made available under the terms of the [CC0 1.0 Universal public domain dedication](http://creativecommons.org/publicdomain/zero/1.0/).) See [LICENSE](LICENSE) and [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
440
|
+
This project is free of any copyright restrictions per the [Unlicense](https://unlicense.org/). (Prior to Feb. 4, 2024, the project was made available under the terms of the [CC0 1.0 Universal public domain dedication](http://creativecommons.org/publicdomain/zero/1.0/).) See [LICENSE](LICENSE) and [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING
|
|
2
|
+
|
|
1
3
|
# Export the main method, helper methods, and the public data types.
|
|
2
|
-
from .
|
|
3
|
-
|
|
4
|
+
from .exceptions import EmailNotValidError, EmailSyntaxError, EmailUndeliverableError
|
|
5
|
+
from .types import ValidatedEmail
|
|
4
6
|
from .validate_email import validate_email
|
|
5
7
|
from .version import __version__
|
|
6
8
|
|
|
@@ -9,12 +11,14 @@ __all__ = ["validate_email",
|
|
|
9
11
|
"EmailSyntaxError", "EmailUndeliverableError",
|
|
10
12
|
"caching_resolver", "__version__"]
|
|
11
13
|
|
|
12
|
-
|
|
13
|
-
def caching_resolver(*args, **kwargs):
|
|
14
|
-
# Lazy load `deliverability` as it is slow to import (due to dns.resolver)
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
15
|
from .deliverability import caching_resolver
|
|
16
|
+
else:
|
|
17
|
+
def caching_resolver(*args, **kwargs):
|
|
18
|
+
# Lazy load `deliverability` as it is slow to import (due to dns.resolver)
|
|
19
|
+
from .deliverability import caching_resolver
|
|
16
20
|
|
|
17
|
-
|
|
21
|
+
return caching_resolver(*args, **kwargs)
|
|
18
22
|
|
|
19
23
|
|
|
20
24
|
# These global attributes are a part of the library's API and can be
|
|
@@ -23,8 +27,11 @@ def caching_resolver(*args, **kwargs):
|
|
|
23
27
|
# Default values for keyword arguments.
|
|
24
28
|
|
|
25
29
|
ALLOW_SMTPUTF8 = True
|
|
30
|
+
ALLOW_EMPTY_LOCAL = False
|
|
26
31
|
ALLOW_QUOTED_LOCAL = False
|
|
27
32
|
ALLOW_DOMAIN_LITERAL = False
|
|
33
|
+
ALLOW_DISPLAY_NAME = False
|
|
34
|
+
STRICT = False
|
|
28
35
|
GLOBALLY_DELIVERABLE = True
|
|
29
36
|
CHECK_DELIVERABILITY = True
|
|
30
37
|
TEST_ENVIRONMENT = False
|
|
@@ -12,23 +12,25 @@
|
|
|
12
12
|
# When using STDIN, no output will be given for valid email addresses.
|
|
13
13
|
#
|
|
14
14
|
# Keyword arguments to validate_email can be set in environment variables
|
|
15
|
-
# of the same name but
|
|
15
|
+
# of the same name but uppercase (see below).
|
|
16
16
|
|
|
17
17
|
import json
|
|
18
18
|
import os
|
|
19
19
|
import sys
|
|
20
|
+
from typing import Any, Dict, Optional
|
|
20
21
|
|
|
21
|
-
from .validate_email import validate_email
|
|
22
|
+
from .validate_email import validate_email, _Resolver
|
|
22
23
|
from .deliverability import caching_resolver
|
|
23
|
-
from .
|
|
24
|
+
from .exceptions import EmailNotValidError
|
|
24
25
|
|
|
25
26
|
|
|
26
|
-
def main(dns_resolver=None):
|
|
27
|
+
def main(dns_resolver: Optional[_Resolver] = None) -> None:
|
|
27
28
|
# The dns_resolver argument is for tests.
|
|
28
29
|
|
|
29
30
|
# Set options from environment variables.
|
|
30
|
-
options = {}
|
|
31
|
-
for varname in ('ALLOW_SMTPUTF8', 'ALLOW_QUOTED_LOCAL', 'ALLOW_DOMAIN_LITERAL',
|
|
31
|
+
options: Dict[str, Any] = {}
|
|
32
|
+
for varname in ('ALLOW_SMTPUTF8', 'ALLOW_EMPTY_LOCAL', 'ALLOW_QUOTED_LOCAL', 'ALLOW_DOMAIN_LITERAL',
|
|
33
|
+
'ALLOW_DISPLAY_NAME',
|
|
32
34
|
'GLOBALLY_DELIVERABLE', 'CHECK_DELIVERABILITY', 'TEST_ENVIRONMENT'):
|
|
33
35
|
if varname in os.environ:
|
|
34
36
|
options[varname.lower()] = bool(os.environ[varname])
|
|
@@ -37,7 +39,7 @@ def main(dns_resolver=None):
|
|
|
37
39
|
options[varname.lower()] = float(os.environ[varname])
|
|
38
40
|
|
|
39
41
|
if len(sys.argv) == 1:
|
|
40
|
-
# Validate the email addresses
|
|
42
|
+
# Validate the email addresses passed line-by-line on STDIN.
|
|
41
43
|
dns_resolver = dns_resolver or caching_resolver()
|
|
42
44
|
for line in sys.stdin:
|
|
43
45
|
email = line.strip()
|
|
@@ -1,24 +1,31 @@
|
|
|
1
|
-
from typing import Optional,
|
|
1
|
+
from typing import Any, List, Optional, Tuple, TypedDict
|
|
2
2
|
|
|
3
3
|
import ipaddress
|
|
4
4
|
|
|
5
|
-
from .
|
|
5
|
+
from .exceptions import EmailUndeliverableError
|
|
6
6
|
|
|
7
7
|
import dns.resolver
|
|
8
8
|
import dns.exception
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
def caching_resolver(*, timeout: Optional[int] = None, cache=None, dns_resolver=None):
|
|
11
|
+
def caching_resolver(*, timeout: Optional[int] = None, cache: Any = None, dns_resolver: Optional[dns.resolver.Resolver] = None) -> dns.resolver.Resolver:
|
|
12
12
|
if timeout is None:
|
|
13
13
|
from . import DEFAULT_TIMEOUT
|
|
14
14
|
timeout = DEFAULT_TIMEOUT
|
|
15
15
|
resolver = dns_resolver or dns.resolver.Resolver()
|
|
16
|
-
resolver.cache = cache or dns.resolver.LRUCache()
|
|
17
|
-
resolver.lifetime = timeout #
|
|
16
|
+
resolver.cache = cache or dns.resolver.LRUCache()
|
|
17
|
+
resolver.lifetime = timeout # timeout, in seconds
|
|
18
18
|
return resolver
|
|
19
19
|
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
DeliverabilityInfo = TypedDict("DeliverabilityInfo", {
|
|
22
|
+
"mx": List[Tuple[int, str]],
|
|
23
|
+
"mx_fallback_type": Optional[str],
|
|
24
|
+
"unknown-deliverability": str,
|
|
25
|
+
}, total=False)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def validate_email_deliverability(domain: str, domain_i18n: str, timeout: Optional[int] = None, dns_resolver: Optional[dns.resolver.Resolver] = None) -> DeliverabilityInfo:
|
|
22
29
|
# Check that the domain resolves to an MX record. If there is no MX record,
|
|
23
30
|
# try an A or AAAA record which is a deprecated fallback for deliverability.
|
|
24
31
|
# Raises an EmailUndeliverableError on failure. On success, returns a dict
|
|
@@ -36,7 +43,7 @@ def validate_email_deliverability(domain: str, domain_i18n: str, timeout: Option
|
|
|
36
43
|
elif timeout is not None:
|
|
37
44
|
raise ValueError("It's not valid to pass both timeout and dns_resolver.")
|
|
38
45
|
|
|
39
|
-
deliverability_info:
|
|
46
|
+
deliverability_info: DeliverabilityInfo = {}
|
|
40
47
|
|
|
41
48
|
try:
|
|
42
49
|
try:
|
|
@@ -69,9 +76,9 @@ def validate_email_deliverability(domain: str, domain_i18n: str, timeout: Option
|
|
|
69
76
|
# https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml
|
|
70
77
|
# https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml
|
|
71
78
|
# (Issue #134.)
|
|
72
|
-
def is_global_addr(
|
|
79
|
+
def is_global_addr(address: Any) -> bool:
|
|
73
80
|
try:
|
|
74
|
-
ipaddr = ipaddress.ip_address(
|
|
81
|
+
ipaddr = ipaddress.ip_address(address)
|
|
75
82
|
except ValueError:
|
|
76
83
|
return False
|
|
77
84
|
return ipaddr.is_global
|
|
@@ -115,7 +122,6 @@ def validate_email_deliverability(domain: str, domain_i18n: str, timeout: Option
|
|
|
115
122
|
for rec in response:
|
|
116
123
|
value = b"".join(rec.strings)
|
|
117
124
|
if value.startswith(b"v=spf1 "):
|
|
118
|
-
deliverability_info["spf"] = value.decode("ascii", errors='replace')
|
|
119
125
|
if value == b"v=spf1 -all":
|
|
120
126
|
raise EmailUndeliverableError(f"The domain name {domain_i18n} does not send email.")
|
|
121
127
|
except dns.resolver.NoAnswer:
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
class EmailNotValidError(ValueError):
|
|
2
|
+
"""Parent class of all exceptions raised by this module."""
|
|
3
|
+
pass
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class EmailSyntaxError(EmailNotValidError):
|
|
7
|
+
"""Exception raised when an email address fails validation because of its form."""
|
|
8
|
+
pass
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class EmailUndeliverableError(EmailNotValidError):
|
|
12
|
+
"""Exception raised when an email address fails validation because its domain name does not appear deliverable."""
|
|
13
|
+
pass
|