Skip to content

Validating Email Addresses

Example of how to use in a script

# -*- coding: utf-8 -*-
"""
This module is used to validate a list of email addresses using various configurations.

The module imports the `validate_email_address` function from the `dsg_lib.common_functions.email_validation`
module and uses it to validate a list of email addresses. The email addresses and configurations are hard-coded
into the module.

The module measures the time taken to validate all the email addresses with all the configurations and prints
the results in a sorted order.

The module can be run as a standalone script.

Example:
    $ python validate_emails.py

Attributes:
    email_addresses (list of str): A list of email addresses to validate.
    configurations (list of dict): A list of configurations to use for validation. Each configuration is a
    dictionary with the following keys:
        - check_deliverability (bool): Whether to check if the email address is deliverable.
        - test_environment (bool): Whether the function is being run in a test environment.
        - allow_smtputf8 (bool): Whether to allow non-ASCII characters in the email address.
        - allow_empty_local (bool): Whether to allow email addresses with an empty local part.
        - allow_quoted_local (bool): Whether to allow email addresses with a quoted local part.
        - allow_display_name (bool): Whether to allow email addresses with a display name.
        - allow_domain_literal (bool): Whether to allow email addresses with a domain literal.
        - globally_deliverable (bool): Whether the email address should be globally deliverable.
        - timeout (int): The timeout for the validation in seconds.
        - dns_type (str): The type of DNS to use for the validation. Can be 'dns' or 'timeout'.

Functions:
    validate_email_address(email: str, **kwargs: dict) -> dict: Validates an email address using the provided
    configuration and returns a dictionary with the results.

Author: Mike Ryan
Date: 2024/05/16
License: MIT
"""
import pprint
import time

from dsg_lib.common_functions.email_validation import validate_email_address

if __name__ == "__main__":

    # create a list of email addresses to check if valid
    email_addresses = [
        "bob@devsetgo.com",
        "bob@devset.go",
        "foo@yahoo.com",
        "bob@gmail.com",
        "very fake@devsetgo.com",
        "jane.doe@example.com",
        "john_doe@example.co.uk",
        "user.name+tag+sorting@example.com",
        "x@example.com",  # shortest possible email address
        "example-indeed@strange-example.com",
        "admin@mailserver1",  # local domain name with no TLD
        "example@s.example",  # see the list of Internet top-level domains
        '" "@example.org',  # space between the quotes
        '"john..doe"@example.org',  # quoted double dot
        "mailhost!username@example.org",  # bangified host route used for uucp mailers
        "user%example.com@example.org",  # percent sign in local part
        "user-@example.org",  # valid due to the last character being an allowed character
        # Invalid email addresses
        "Abc.example.com",  # no @ character
        "A@b@c@example.com",  # only one @ is allowed outside quotation marks
        'a"b(c)d,e:f;g<h>i[j\\k]l@example.com',  # none of the special characters in this local part are allowed outside quotation marks
        'just"not"right@example.com',  # quoted strings must be dot separated or the only element making up the local-part
        'this is"not\\allowed@example.com',  # spaces, quotes, and backslashes may only exist when within quoted strings and preceded by a backslash
        'this\\ still\\"not\\\\allowed@example.com',  # even if escaped (preceded by a backslash), spaces, quotes, and backslashes must still be contained by quotes
        "1234567890123456789012345678901234567890123456789012345678901234+x@example.com",  # local part is longer than 64 characters
        # Emails with empty local part
        "@example.com",  # only valid if allow_empty_local is True
        # Emails with non-ASCII characters
        "üñîçøðé@example.com",  # only valid if allow_smtputf8 is True
        "user@üñîçøðé.com",  # only valid if allow_smtputf8 is True
        # Emails with quoted local part
        '"john.doe"@example.com',  # only valid if allow_quoted_local is True
        '"john..doe"@example.com',  # only valid if allow_quoted_local is True
        # Emails with display name
        "John Doe <john@example.com>",  # only valid if allow_display_name is True
        # Emails with domain literal
        "user@[192.0.2.1]",  # only valid if allow_domain_literal is True
        # Emails with long local part
        "a" * 65 + "@example.com",  # local part is longer than 64 characters
        # Emails with invalid characters
        "john doe@example.com",  # space is not allowed
        "john@doe@example.com",  # only one @ is allowed
        "john.doe@.com",  # domain can't start with a dot
        "john.doe@example..com",  # domain can't have two consecutive dots
        "test@google.com",
    ]

    # create a list of configurations
    configurations = [
        {
            "check_deliverability": True,
            "test_environment": False,
            "allow_smtputf8": False,
            "allow_empty_local": False,
            "allow_quoted_local": False,
            "allow_display_name": False,
            "allow_domain_literal": False,
            "globally_deliverable": None,
            "timeout": 10,
            "dns_type": "timeout",
        },
        {
            "check_deliverability": False,
            "test_environment": True,
            "allow_smtputf8": True,
            "allow_empty_local": True,
            "allow_quoted_local": True,
            "allow_display_name": True,
            "allow_domain_literal": True,
            "globally_deliverable": None,
            "timeout": 5,
            "dns_type": "dns",
        },
        {"check_deliverability": True},
        {
            "check_deliverability": False,
            "test_environment": False,
            "allow_smtputf8": True,
            "allow_empty_local": False,
            "allow_quoted_local": True,
            "allow_display_name": False,
            "allow_domain_literal": True,
            "globally_deliverable": None,
            "timeout": 15,
            "dns_type": "timeout",
        },
        {
            "check_deliverability": True,
            "test_environment": True,
            "allow_smtputf8": False,
            "allow_empty_local": True,
            "allow_quoted_local": False,
            "allow_display_name": True,
            "allow_domain_literal": False,
            "globally_deliverable": None,
            "timeout": 20,
            "dns_type": "dns",
        },
        {
            "check_deliverability": False,
            "test_environment": False,
            "allow_smtputf8": True,
            "allow_empty_local": True,
            "allow_quoted_local": True,
            "allow_display_name": True,
            "allow_domain_literal": True,
            "globally_deliverable": None,
            "timeout": 25,
            "dns_type": "timeout",
        },
        {
            "check_deliverability": True,
            "test_environment": True,
            "allow_smtputf8": False,
            "allow_empty_local": False,
            "allow_quoted_local": False,
            "allow_display_name": False,
            "allow_domain_literal": False,
            "globally_deliverable": None,
            "timeout": 30,
            "dns_type": "dns",
        },
        {
            "check_deliverability": False,
            "test_environment": True,
            "allow_smtputf8": True,
            "allow_empty_local": False,
            "allow_quoted_local": True,
            "allow_display_name": True,
            "allow_domain_literal": False,
            "globally_deliverable": None,
            "timeout": 35,
            "dns_type": "timeout",
        },
        {
            "check_deliverability": True,
            "test_environment": False,
            "allow_smtputf8": False,
            "allow_empty_local": True,
            "allow_quoted_local": True,
            "allow_display_name": False,
            "allow_domain_literal": True,
            "globally_deliverable": None,
            "timeout": 40,
            "dns_type": "dns",
        },
        {
            "check_deliverability": False,
            "test_environment": True,
            "allow_smtputf8": True,
            "allow_empty_local": False,
            "allow_quoted_local": False,
            "allow_display_name": True,
            "allow_domain_literal": True,
            "globally_deliverable": None,
            "timeout": 45,
            "dns_type": "timeout",
        },
    ]

    t0 = time.time()
    validity = []

    for email in email_addresses:
        for config in configurations:

            res = validate_email_address(email, **config)
            validity.append(res)
    t1 = time.time()
    validity = sorted(validity, key=lambda x: x["email"])

    for v in validity:
        pprint.pprint(v, indent=4)

    print(f"Time taken: {t1 - t0:.2f}")