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.
Files changed (30) hide show
  1. {email_validator-2.1.2/email_validator.egg-info → email_validator-2.3.0}/PKG-INFO +51 -71
  2. {email_validator-2.1.2 → email_validator-2.3.0}/README.md +48 -69
  3. {email_validator-2.1.2 → email_validator-2.3.0}/email_validator/__init__.py +13 -6
  4. {email_validator-2.1.2 → email_validator-2.3.0}/email_validator/__main__.py +9 -7
  5. {email_validator-2.1.2 → email_validator-2.3.0}/email_validator/deliverability.py +16 -10
  6. email_validator-2.3.0/email_validator/exceptions.py +13 -0
  7. {email_validator-2.1.2 → email_validator-2.3.0}/email_validator/rfc_constants.py +16 -5
  8. {email_validator-2.1.2 → email_validator-2.3.0}/email_validator/syntax.py +372 -113
  9. email_validator-2.1.2/email_validator/exceptions_types.py → email_validator-2.3.0/email_validator/types.py +16 -31
  10. {email_validator-2.1.2 → email_validator-2.3.0}/email_validator/validate_email.py +89 -26
  11. email_validator-2.3.0/email_validator/version.py +1 -0
  12. {email_validator-2.1.2 → email_validator-2.3.0/email_validator.egg-info}/PKG-INFO +51 -71
  13. {email_validator-2.1.2 → email_validator-2.3.0}/email_validator.egg-info/SOURCES.txt +2 -1
  14. email_validator-2.3.0/pyproject.toml +17 -0
  15. {email_validator-2.1.2 → email_validator-2.3.0}/setup.cfg +1 -1
  16. {email_validator-2.1.2 → email_validator-2.3.0}/tests/mocked_dns_response.py +29 -23
  17. {email_validator-2.1.2 → email_validator-2.3.0}/tests/test_deliverability.py +11 -9
  18. {email_validator-2.1.2 → email_validator-2.3.0}/tests/test_main.py +6 -6
  19. {email_validator-2.1.2 → email_validator-2.3.0}/tests/test_syntax.py +160 -48
  20. email_validator-2.1.2/email_validator/version.py +0 -1
  21. email_validator-2.1.2/pyproject.toml +0 -4
  22. {email_validator-2.1.2 → email_validator-2.3.0}/LICENSE +0 -0
  23. {email_validator-2.1.2 → email_validator-2.3.0}/MANIFEST.in +0 -0
  24. {email_validator-2.1.2 → email_validator-2.3.0}/email_validator/py.typed +0 -0
  25. {email_validator-2.1.2 → email_validator-2.3.0}/email_validator.egg-info/dependency_links.txt +0 -0
  26. {email_validator-2.1.2 → email_validator-2.3.0}/email_validator.egg-info/entry_points.txt +0 -0
  27. {email_validator-2.1.2 → email_validator-2.3.0}/email_validator.egg-info/requires.txt +0 -0
  28. {email_validator-2.1.2 → email_validator-2.3.0}/email_validator.egg-info/top_level.txt +0 -0
  29. {email_validator-2.1.2 → email_validator-2.3.0}/setup.py +0 -0
  30. {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.1
2
- Name: email_validator
3
- Version: 2.1.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/login form (but not
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 validing data.
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 and internationalized local parts.
47
- * Rejects addresses with unsafe Unicode characters, obsolete email address
48
- syntax that you'd find unexpected, special use domain names like
49
- `@localhost`, and domains without a dot by default. This is an
50
- opinionated library!
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) and
57
- [flanker](https://github.com/mailgun/flanker) if they are better for your
58
- use case.
59
+ the less-opinionated [pyIsEmail](https://github.com/michaelherold/pyIsEmail)
60
+ if it works better for you.
59
61
 
60
62
  [![Build Status](https://github.com/JoshData/python-email-validator/actions/workflows/test_and_build.yaml/badge.svg)](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:"-prefixd 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.
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
- internationalization.
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
- These character checks are performed after Unicode normalization (see below),
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. Normalization may also change
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 & push a tag (see command below).
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/login form (but not
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 validing data.
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 and internationalized local parts.
22
- * Rejects addresses with unsafe Unicode characters, obsolete email address
23
- syntax that you'd find unexpected, special use domain names like
24
- `@localhost`, and domains without a dot by default. This is an
25
- opinionated library!
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) and
32
- [flanker](https://github.com/mailgun/flanker) if they are better for your
33
- use case.
33
+ the less-opinionated [pyIsEmail](https://github.com/michaelherold/pyIsEmail)
34
+ if it works better for you.
34
35
 
35
36
  [![Build Status](https://github.com/JoshData/python-email-validator/actions/workflows/test_and_build.yaml/badge.svg)](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:"-prefixd 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.
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
- internationalization.
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
- These character checks are performed after Unicode normalization (see below),
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. Normalization may also change
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 & push a tag (see command below).
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 .exceptions_types import ValidatedEmail, EmailNotValidError, \
3
- EmailSyntaxError, EmailUndeliverableError
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
- return caching_resolver(*args, **kwargs)
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 upprcase (see below).
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 .exceptions_types import EmailNotValidError
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 pased line-by-line on STDIN.
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, Any, Dict
1
+ from typing import Any, List, Optional, Tuple, TypedDict
2
2
 
3
3
  import ipaddress
4
4
 
5
- from .exceptions_types import EmailUndeliverableError
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() # type: ignore
17
- resolver.lifetime = timeout # type: ignore # timeout, in seconds
16
+ resolver.cache = cache or dns.resolver.LRUCache()
17
+ resolver.lifetime = timeout # timeout, in seconds
18
18
  return resolver
19
19
 
20
20
 
21
- def validate_email_deliverability(domain: str, domain_i18n: str, timeout: Optional[int] = None, dns_resolver=None):
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: Dict[str, Any] = {}
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(ipaddr):
79
+ def is_global_addr(address: Any) -> bool:
73
80
  try:
74
- ipaddr = ipaddress.ip_address(ipaddr)
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