py3-validate-email/validate_email/validate_email.py

84 lines
3.1 KiB
Python
Raw Normal View History

from logging import getLogger
from ssl import SSLContext
2021-09-15 18:35:51 +02:00
from typing import Optional
2019-03-02 02:35:44 +01:00
2022-06-28 19:11:07 +02:00
from .dns_check import dns_check, DefaultAddressTypes, AddressTypes
2019-11-21 15:41:05 +01:00
from .domainlist_check import domainlist_check
from .email_address import EmailAddress
from .exceptions import (
2021-02-28 15:01:37 +01:00
AddressFormatError, EmailValidationError, FromAddressFormatError,
SMTPTemporaryError)
2019-03-01 23:29:01 +01:00
from .regex_check import regex_check
from .smtp_check import smtp_check
LOGGER = getLogger(name=__name__)
__all__ = ['validate_email', 'validate_email_or_fail']
2021-02-28 15:01:37 +01:00
__doc__ = """\
Verify the given email address by determining the SMTP servers
responsible for the domain and then asking them to deliver an email to
the address. Before the actual message is sent, the process is
interrupted.
PLEASE NOTE: Some email providers only tell the actual delivery failure
AFTER having delivered the body which this module doesn't, while others
simply accept everything and send a bounce notification later. Hence, a
100% proper response is not guaranteed.
"""
def validate_email_or_fail(
2021-03-14 13:24:24 +01:00
email_address: str, *, check_format: bool = True,
check_blacklist: bool = True, check_dns: bool = True,
dns_timeout: float = 10, check_smtp: bool = True,
smtp_timeout: float = 10, smtp_helo_host: Optional[str] = None,
smtp_from_address: Optional[str] = None,
smtp_skip_tls: bool = False, smtp_tls_context: Optional[SSLContext] = None,
2022-06-28 19:11:07 +02:00
smtp_debug: bool = False, address_types: AddressTypes = DefaultAddressTypes
2021-03-14 13:24:24 +01:00
) -> Optional[bool]:
2019-03-02 02:53:53 +01:00
"""
2021-02-28 15:01:37 +01:00
Return `True` if the email address validation is successful, `None`
if the validation result is ambiguous, and raise an exception if the
2021-02-28 15:01:37 +01:00
validation fails.
2019-03-02 02:53:53 +01:00
"""
2021-11-16 23:38:37 +01:00
email_address_to = EmailAddress(address=email_address)
if check_format:
2021-11-16 23:38:37 +01:00
regex_check(email_address=email_address_to)
if check_blacklist:
2021-11-16 23:38:37 +01:00
domainlist_check(email_address=email_address_to)
2021-03-14 13:24:24 +01:00
if not check_dns and not check_smtp: # check_smtp implies check_dns.
return True
2022-06-28 19:11:07 +02:00
mx_records = dns_check(
email_address=email_address_to, timeout=dns_timeout,
address_types=address_types)
if not check_smtp:
return True
2021-11-16 23:38:37 +01:00
try:
email_address_from = None if not smtp_from_address else \
EmailAddress(address=smtp_from_address)
except AddressFormatError:
raise FromAddressFormatError
return smtp_check(
2021-11-16 23:38:37 +01:00
email_address=email_address_to, mx_records=mx_records,
timeout=smtp_timeout, helo_host=smtp_helo_host,
2021-11-16 23:38:37 +01:00
from_address=email_address_from, skip_tls=smtp_skip_tls,
tls_context=smtp_tls_context, debug=smtp_debug)
def validate_email(email_address: str, **kwargs):
"""
Return `True` or `False` depending if the email address exists
or/and can be delivered.
Return `None` if the result is ambiguous.
"""
try:
return validate_email_or_fail(email_address, **kwargs)
2021-02-28 15:01:37 +01:00
except SMTPTemporaryError as error:
LOGGER.info(
msg=f'Validation for {email_address!r} is ambiguous: {error}')
2021-02-28 15:01:37 +01:00
return
except EmailValidationError as error:
LOGGER.info(msg=f'Validation for {email_address!r} failed: {error}')
return False