Repository: wtforms/wtforms
Pull request #741 (2022-05-04 16:25:43)

Allow passing of test_environment flag to email_validator.
email_validator rejects most special-use reserved domain names such as `test` as of 1.2. A new `test_environment` option has been added to allow `@*.test` domains. This PR allows passing of the option from wtforms to email_validator.


Patch:
diff --git a/CHANGES.rst b/CHANGES.rst
index f3c7f3e..3043cc1 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -6,6 +6,7 @@ Version 3.0.2
 Unreleased
 
 -   Delayed import of ``email_validator``. :issue:`727`
+-   Pass ``test_environment`` flag to ``email_validator``.
 
 Version 3.0.1
 -------------
diff --git a/setup.cfg b/setup.cfg
index 4140843..3f13006 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -34,7 +34,7 @@ install_requires = MarkupSafe
 where = src
 
 [options.extras_require]
-email = email_validator
+email = email_validator >= 1.2.0
 
 [extract_messages]
 copyright_holder = WTForms Team
diff --git a/src/wtforms/validators.py b/src/wtforms/validators.py
index 86337c9..e44565b 100644
--- a/src/wtforms/validators.py
+++ b/src/wtforms/validators.py
@@ -374,6 +374,12 @@ class Email:
     :param allow_empty_local:
         Allow an empty local part (i.e. @example.com), e.g. for validating
         Postfix aliases (Default False).
+    :param test_environment:
+        DNS-based deliverability checks are disabled and the Special Use Domain
+        Name ``test`` is permitted.
+
+    .. versionadded:: 3.0.2
+       The *test_environment* parameter.
     """
 
     def __init__(
@@ -383,12 +389,14 @@ class Email:
         check_deliverability=False,
         allow_smtputf8=True,
         allow_empty_local=False,
+        test_environment=False,
     ):
         self.message = message
         self.granular_message = granular_message
         self.check_deliverability = check_deliverability
         self.allow_smtputf8 = allow_smtputf8
         self.allow_empty_local = allow_empty_local
+        self.test_environment = test_environment
 
     def __call__(self, form, field):
         try:
@@ -406,6 +414,7 @@ class Email:
                 check_deliverability=self.check_deliverability,
                 allow_smtputf8=self.allow_smtputf8,
                 allow_empty_local=self.allow_empty_local,
+                test_environment=self.test_environment,
             )
         except email_validator.EmailNotValidError as e:
             message = self.message


Test patch:
diff --git a/tests/validators/test_email.py b/tests/validators/test_email.py
index 6a74c9c..4c9024e 100644
--- a/tests/validators/test_email.py
+++ b/tests/validators/test_email.py
@@ -33,6 +33,7 @@ def test_valid_email_passes(email_address, dummy_form, dummy_field):
         "foo.@bar.co",
         "foo@foo@bar.co",
         "fo o@bar.co",
+        "foo@bar.test",
     ],
 )
 def test_invalid_email_raises(email_address, dummy_form, dummy_field):
@@ -47,8 +48,33 @@ def test_invalid_email_raises(email_address, dummy_form, dummy_field):
     assert str(e.value) == "Invalid email address."
 
 
-@pytest.mark.parametrize("email_address", ["foo@"])
-def test_invalid_email_raises_granular_message(email_address, dummy_form, dummy_field):
+@pytest.mark.parametrize("email_address", ["foo@bar.test"])
+def test_invalid_email_passes_in_test_environment(
+    email_address, dummy_form, dummy_field
+):
+    """
+    When test_environment=True email addresses with test domain should pass
+    without raising
+    """
+    validator = email(test_environment=True)
+    dummy_field.data = email_address
+    validator(dummy_form, dummy_field)
+
+
+@pytest.mark.parametrize(
+    "email_address,message",
+    [
+        ("foo@", "There must be something after the @-sign."),
+        (
+            "foo@bar.test",
+            "The domain name bar.test is a special-use or reserved name"
+            " that cannot be used with email.",
+        ),
+    ],
+)
+def test_invalid_email_raises_granular_message(
+    email_address, message, dummy_form, dummy_field
+):
     """
     When granular_message=True uses message from email_validator library.
     """
@@ -57,4 +83,4 @@ def test_invalid_email_raises_granular_message(email_address, dummy_form, dummy_
     with pytest.raises(ValidationError) as e:
         validator(dummy_form, dummy_field)
 
-    assert str(e.value) == "There must be something after the @-sign."
+    assert str(e.value) == message