Browse Source

Refs #31670 -- Renamed whitelist argument and attribute of EmailValidator.

David Smith 4 years ago
parent
commit
27c09043da

+ 35 - 6
django/core/validators.py

@@ -1,10 +1,12 @@
 import ipaddress
 import re
+import warnings
 from pathlib import Path
 from urllib.parse import urlsplit, urlunsplit
 
 from django.core.exceptions import ValidationError
 from django.utils.deconstruct import deconstructible
+from django.utils.deprecation import RemovedInDjango41Warning
 from django.utils.encoding import punycode
 from django.utils.ipv6 import is_valid_ipv6_address
 from django.utils.regex_helper import _lazy_re_compile
@@ -167,15 +169,42 @@ class EmailValidator:
         # literal form, ipv4 or ipv6 address (SMTP 4.1.3)
         r'\[([A-f0-9:.]+)\]\Z',
         re.IGNORECASE)
-    domain_whitelist = ['localhost']
+    domain_allowlist = ['localhost']
+
+    @property
+    def domain_whitelist(self):
+        warnings.warn(
+            'The domain_whitelist attribute is deprecated in favor of '
+            'domain_allowlist.',
+            RemovedInDjango41Warning,
+            stacklevel=2,
+        )
+        return self.domain_allowlist
+
+    @domain_whitelist.setter
+    def domain_whitelist(self, allowlist):
+        warnings.warn(
+            'The domain_whitelist attribute is deprecated in favor of '
+            'domain_allowlist.',
+            RemovedInDjango41Warning,
+            stacklevel=2,
+        )
+        self.domain_allowlist = allowlist
 
-    def __init__(self, message=None, code=None, whitelist=None):
+    def __init__(self, message=None, code=None, allowlist=None, *, whitelist=None):
+        if whitelist is not None:
+            allowlist = whitelist
+            warnings.warn(
+                'The whitelist argument is deprecated in favor of allowlist.',
+                RemovedInDjango41Warning,
+                stacklevel=2,
+            )
         if message is not None:
             self.message = message
         if code is not None:
             self.code = code
-        if whitelist is not None:
-            self.domain_whitelist = whitelist
+        if allowlist is not None:
+            self.domain_allowlist = allowlist
 
     def __call__(self, value):
         if not value or '@' not in value:
@@ -186,7 +215,7 @@ class EmailValidator:
         if not self.user_regex.match(user_part):
             raise ValidationError(self.message, code=self.code)
 
-        if (domain_part not in self.domain_whitelist and
+        if (domain_part not in self.domain_allowlist and
                 not self.validate_domain_part(domain_part)):
             # Try for possible IDN domain-part
             try:
@@ -215,7 +244,7 @@ class EmailValidator:
     def __eq__(self, other):
         return (
             isinstance(other, EmailValidator) and
-            (self.domain_whitelist == other.domain_whitelist) and
+            (self.domain_allowlist == other.domain_allowlist) and
             (self.message == other.message) and
             (self.code == other.code)
         )

+ 3 - 0
docs/internals/deprecation.txt

@@ -21,6 +21,9 @@ details on these changes.
 
 * ``BaseCommand.requires_system_checks`` won't support boolean values.
 
+* The ``whitelist`` argument and ``domain_whitelist`` attribute of
+  ``django.core.validators.EmailValidator`` will be removed.
+
 .. _deprecation-removed-in-4.0:
 
 4.0

+ 17 - 9
docs/ref/validators.txt

@@ -119,11 +119,11 @@ to, or in lieu of custom ``field.clean()`` methods.
 ``EmailValidator``
 ------------------
 
-.. class:: EmailValidator(message=None, code=None, whitelist=None)
+.. class:: EmailValidator(message=None, code=None, allowlist=None)
 
     :param message: If not ``None``, overrides :attr:`.message`.
     :param code: If not ``None``, overrides :attr:`code`.
-    :param whitelist: If not ``None``, overrides :attr:`whitelist`.
+    :param allowlist: If not ``None``, overrides :attr:`allowlist`.
 
     .. attribute:: message
 
@@ -136,14 +136,22 @@ to, or in lieu of custom ``field.clean()`` methods.
         The error code used by :exc:`~django.core.exceptions.ValidationError`
         if validation fails. Defaults to ``"invalid"``.
 
-    .. attribute:: whitelist
+    .. attribute:: allowlist
 
-        Whitelist of email domains to allow. By default, a regular expression
-        (the ``domain_regex`` attribute) is used to validate whatever appears
-        after the @ sign. However, if that string appears in the whitelist, this
-        validation is bypassed. If not provided, the default whitelist is
-        ``['localhost']``. Other domains that don't contain a dot won't pass
-        validation, so you'd need to whitelist them as necessary.
+        Allowlist of email domains. By default, a regular expression (the
+        ``domain_regex`` attribute) is used to validate whatever appears after
+        the ``@`` sign. However, if that string appears in the ``allowlist``,
+        this validation is bypassed. If not provided, the default ``allowlist``
+        is ``['localhost']``. Other domains that don't contain a dot won't pass
+        validation, so you'd need to add them to the ``allowlist`` as
+        necessary.
+
+    .. deprecated:: 3.2
+
+        The ``whitelist`` parameter is deprecated. Use :attr:`allowlist`
+        instead.
+        The undocumented ``domain_whitelist`` attribute is deprecated. Use
+        ``domain_allowlist`` instead.
 
 ``URLValidator``
 ----------------

+ 6 - 0
docs/releases/3.2.txt

@@ -351,3 +351,9 @@ Miscellaneous
 * Using a boolean value in :attr:`.BaseCommand.requires_system_checks` is
   deprecated. Use ``'__all__'`` instead of ``True``, and ``[]`` (an empty list)
   instead of ``False``.
+
+* The ``whitelist`` argument and ``domain_whitelist`` attribute of
+  :class:`~django.core.validators.EmailValidator` are deprecated. Use
+  ``allowlist`` instead of ``whitelist``, and ``domain_allowlist`` instead of
+  ``domain_whitelist``. You may need to rename ``whitelist`` in existing
+  migrations.

+ 1 - 1
docs/spelling_wordlist

@@ -10,6 +10,7 @@ affordances
 aggregator
 Ai
 Alchin
+allowlist
 alphanumerics
 amet
 analytics
@@ -780,7 +781,6 @@ vertices
 viewable
 virtualized
 Weblog
-whitelist
 whitespace
 whitespaces
 whizbang

+ 42 - 2
tests/validators/tests.py

@@ -16,7 +16,8 @@ from django.core.validators import (
     validate_ipv4_address, validate_ipv6_address, validate_ipv46_address,
     validate_slug, validate_unicode_slug,
 )
-from django.test import SimpleTestCase
+from django.test import SimpleTestCase, ignore_warnings
+from django.utils.deprecation import RemovedInDjango41Warning
 
 try:
     from PIL import Image  # noqa
@@ -50,7 +51,7 @@ TEST_DATA = [
     (validate_email, 'example@valid-with-hyphens.com', None),
     (validate_email, 'test@domain.with.idn.tld.उदाहरण.परीक्षा', None),
     (validate_email, 'email@localhost', None),
-    (EmailValidator(whitelist=['localdomain']), 'email@localdomain', None),
+    (EmailValidator(allowlist=['localdomain']), 'email@localdomain', None),
     (validate_email, '"test@test"@example.com', None),
     (validate_email, 'example@atm.%s' % ('a' * 63), None),
     (validate_email, 'example@%s.atm' % ('a' * 63), None),
@@ -510,3 +511,42 @@ class TestValidatorEquality(TestCase):
             ProhibitNullCharactersValidator(message='message', code='code1'),
             ProhibitNullCharactersValidator(message='message', code='code2')
         )
+
+
+class DeprecationTests(SimpleTestCase):
+    @ignore_warnings(category=RemovedInDjango41Warning)
+    def test_whitelist(self):
+        validator = EmailValidator(whitelist=['localdomain'])
+        self.assertEqual(validator.domain_allowlist, ['localdomain'])
+        self.assertIsNone(validator('email@localdomain'))
+        self.assertEqual(validator.domain_allowlist, validator.domain_whitelist)
+
+    def test_whitelist_warning(self):
+        msg = "The whitelist argument is deprecated in favor of allowlist."
+        with self.assertRaisesMessage(RemovedInDjango41Warning, msg):
+            EmailValidator(whitelist='localdomain')
+
+    @ignore_warnings(category=RemovedInDjango41Warning)
+    def test_domain_whitelist(self):
+        validator = EmailValidator()
+        validator.domain_whitelist = ['mydomain']
+        self.assertEqual(validator.domain_allowlist, ['mydomain'])
+        self.assertEqual(validator.domain_allowlist, validator.domain_whitelist)
+
+    def test_domain_whitelist_access_warning(self):
+        validator = EmailValidator()
+        msg = (
+            'The domain_whitelist attribute is deprecated in favor of '
+            'domain_allowlist.'
+        )
+        with self.assertRaisesMessage(RemovedInDjango41Warning, msg):
+            validator.domain_whitelist
+
+    def test_domain_whitelist_set_warning(self):
+        validator = EmailValidator()
+        msg = (
+            'The domain_whitelist attribute is deprecated in favor of '
+            'domain_allowlist.'
+        )
+        with self.assertRaisesMessage(RemovedInDjango41Warning, msg):
+            validator.domain_whitelist = ['mydomain']