Skip to content

Commit 7a19c33

Browse files
committed
Fix exception on DNS timeout
In 8243bd2, I re-organized the return value of validate_email to be an object rather than a dict. However I didn't properly handle the case when there's a DNS timeout. I think my original intention was to hide timeout errors because it's unclear if a timeout is a local problem or a problem with the email's domain name. If it's the former, passing through the timeout exception to the caller would make sense. But if it's something that can be triggered by user input, then it shouldn't raise a timeout exception, it should raise a EmailUndeliverableError. Since we can't decide which is the case, we treat it as if the deliverability check was not performed at all. Fixes #41
1 parent 9c811d2 commit 7a19c33

File tree

3 files changed

+24
-6
lines changed

3 files changed

+24
-6
lines changed

README.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -348,12 +348,15 @@ are:
348348

349349
`mx`: A list of (priority, domain) tuples of MX records specified in the
350350
DNS for the domain (see [RFC 5321 section
351-
5](https://tools.ietf.org/html/rfc5321#section-5)).
351+
5](https://tools.ietf.org/html/rfc5321#section-5)). May be `None` if
352+
the deliverability check could not be completed because of a temporary
353+
issue like a timeout.
352354

353355
`mx_fallback_type`: `None` if an `MX` record is found. If no MX records are actually
354356
specified in DNS and instead are inferred, through an obsolete
355357
mechanism, from A or AAAA records, the value is the type of DNS
356-
record used instead (`A` or `AAAA`).
358+
record used instead (`A` or `AAAA`). May be `None` if the deliverability check
359+
could not be completed because of a temporary issue like a timeout.
357360

358361
Assumptions
359362
-----------

email_validator/__init__.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,8 @@ class ValidatedEmail(object):
9292
is False."""
9393
smtputf8 = None
9494

95-
"""If a deliverability check is performed, a list of (priority, domain) tuples of MX
96-
records specified in the DNS for the domain."""
95+
"""If a deliverability check is performed and if it succeeds, a list of (priority, domain)
96+
tuples of MX records specified in the DNS for the domain."""
9797
mx = None
9898

9999
"""If no MX records are actually specified in DNS and instead are inferred, through an obsolete
@@ -263,8 +263,9 @@ def validate_email(
263263
# Validate the email address's deliverability and update the
264264
# return dict with metadata.
265265
deliverability_info = validate_email_deliverability(ret["domain"], ret["domain_i18n"], timeout)
266-
ret.mx = deliverability_info["mx"]
267-
ret.mx_fallback_type = deliverability_info["mx-fallback"]
266+
if "mx" in deliverability_info:
267+
ret.mx = deliverability_info["mx"]
268+
ret.mx_fallback_type = deliverability_info["mx-fallback"]
268269

269270
return ret
270271

@@ -441,6 +442,12 @@ def validate_email_deliverability(domain, domain_i18n, timeout=DEFAULT_TIMEOUT):
441442
domain += '.'
442443

443444
try:
445+
# We need a way to check how timeouts are handled in the tests. So we
446+
# have a secret variable that if set makes this method always test the
447+
# handling of a timeout.
448+
if getattr(validate_email_deliverability, 'TEST_CHECK_TIMEOUT', False):
449+
raise dns.exception.Timeout()
450+
444451
resolver = dns.resolver.get_default_resolver()
445452

446453
if timeout:

tests/test_main.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,3 +268,11 @@ def test_deliverability_fails():
268268
domain = 'xkxufoekjvjfjeodlfmdfjcu.com'
269269
with pytest.raises(EmailUndeliverableError, match='The domain name {} does not exist'.format(domain)):
270270
validate_email_deliverability(domain, domain)
271+
272+
def test_deliverability_dns_timeout():
273+
validate_email_deliverability.TEST_CHECK_TIMEOUT = True
274+
response = validate_email_deliverability('gmail.com', 'gmail.com')
275+
assert "mx" not in response
276+
assert response.get("unknown-deliverability") == "timeout"
277+
email = validate_email('test@gmail.com')
278+
del validate_email_deliverability.TEST_CHECK_TIMEOUT

0 commit comments

Comments
 (0)