From 038c9110a3801bc736f1bd52038a362ab6c69f4c Mon Sep 17 00:00:00 2001 From: Lexi Stelter Date: Mon, 22 May 2023 18:27:57 +0200 Subject: [PATCH] EmailValidator: Add to_lowercase parameter --- docs/03-basic-validators.md | 6 ++++ .../validators/email_validator.py | 30 ++++++++++++++----- tests/validators/email_validator_test.py | 27 +++++++++++++++-- 3 files changed, 53 insertions(+), 10 deletions(-) diff --git a/docs/03-basic-validators.md b/docs/03-basic-validators.md index 8267f6f..b9561d4 100644 --- a/docs/03-basic-validators.md +++ b/docs/03-basic-validators.md @@ -235,6 +235,8 @@ mail software does not support those anyway and/or might break with those adress By default, the validator does not accept empty strings as input. To allow them, set the parameter `allow_empty=True`. +To automatically convert the output email address to lowercase, you can set the parameter `to_lowercase=True`. + Also, the default maximum string length is set to 256 characters (which would be a really long, but still valid email address), which can be changed using the `max_length` parameter. @@ -253,6 +255,10 @@ validator.validate("") # will raise StringTooShortError(min_leng validator = EmailValidator(allow_empty=True) validator.validate("foo@example.com") # will return "foo@example.com" validator.validate("") # will return "" (empty string) + +# Converts email addresses to lowercase +validator = EmailValidator(to_lowercase=True) +validator.validate("Foo.Bar@Example.COM") # will return "foo.bar@example.com") ``` diff --git a/src/validataclass/validators/email_validator.py b/src/validataclass/validators/email_validator.py index 7c6353f..e800c51 100644 --- a/src/validataclass/validators/email_validator.py +++ b/src/validataclass/validators/email_validator.py @@ -30,6 +30,8 @@ class EmailValidator(StringValidator): Set the parameter `allow_empty=True` to allow empty strings as input. + To automatically convert the output email address to lowercase, you can set the parameter `to_lowercase=True`. + By default, the maximum string length is set to 256 characters. This can be changed with the `max_length` parameter. Example: @@ -39,6 +41,9 @@ class EmailValidator(StringValidator): # Accepts also empty strings EmailValidator(allow_empty=True) + + # Converts email addresses to lowercase (e.g. "Foo.Bar@Example.COM" -> "foo.bar@example.com") + EmailValidator(to_lowercase=True) ``` Valid input: email address as `str` (also empty strings if `allow_empty=True`) @@ -55,26 +60,33 @@ class EmailValidator(StringValidator): # Whether to accept empty strings allow_empty: bool = False + # Whether to automatically convert strings to lowercase + to_lowercase: bool = False + def __init__( self, *, allow_empty: bool = False, + to_lowercase: bool = False, max_length: int = 256, ): """ Create a `EmailValidator`. Parameters: - allow_empty: Boolean, if True, empty strings are accepted as valid email addresses (default: False) + allow_empty: Boolean, if True, empty strings are accepted as valid input (default: False) + to_lowercase: Boolean, if True, the output will be automatically converted to lowercase (default: False) max_length: Integer, maximum length of input string (default: 256) """ - self.allow_empty = allow_empty - - # Allow string with length 0 if `allow_empty=True` - min_length = 0 if allow_empty else 1 - # Initialize StringValidator with some length requirements - super().__init__(min_length=min_length, max_length=max_length) + super().__init__( + min_length=0 if allow_empty else 1, + max_length=max_length, + ) + + # Save parameters + self.allow_empty = allow_empty + self.to_lowercase = to_lowercase def validate(self, input_data: Any, **kwargs) -> str: """ @@ -102,5 +114,9 @@ def validate(self, input_data: Any, **kwargs) -> str: if not internet_helpers.validate_domain_name(email_domain, require_tld=True): raise InvalidEmailError(reason='Domain not valid.') + # Convert to lowercase (if enabled) + if self.to_lowercase: + input_email = input_email.lower() + # Email address is valid :) return input_email diff --git a/tests/validators/email_validator_test.py b/tests/validators/email_validator_test.py index 3f0e274..ec42b35 100644 --- a/tests/validators/email_validator_test.py +++ b/tests/validators/email_validator_test.py @@ -96,6 +96,25 @@ def test_max_length_parameter(): 'max_length': 16, } + # Test to_lowercase option + + @staticmethod + @pytest.mark.parametrize( + 'to_lowercase, input_string, expected_output', [ + # Default behaviour: Keep uppercase letters intact + (False, 'foo@example.com', 'foo@example.com'), + (False, 'Foo.Bar@Example.COM', 'Foo.Bar@Example.COM'), + + # Set to_lowercase=True + (True, 'foo@example.com', 'foo@example.com'), + (True, 'Foo.Bar@Example.COM', 'foo.bar@example.com'), + ] + ) + def test_email_to_lowercase(to_lowercase, input_string, expected_output): + """ Test EmailValidator with the to_lowercase option. """ + validator = EmailValidator(to_lowercase=to_lowercase) + assert validator.validate(input_string) == expected_output + # Tests for regex validation of email address format @staticmethod @@ -108,7 +127,9 @@ def test_max_length_parameter(): 'foo.bar@example.com', 'a.b.c.d.e@example.com', # Very long local part (up to 64 characters are allowed) - 'fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo@example.com' + 'fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo@example.com', + # Addresses with uppercase letters (should be left intact by default) + 'Foo.Bar@Example.COM', ] ) def test_email_regex_valid(input_string): @@ -129,9 +150,9 @@ def test_email_regex_valid(input_string): 'banana.@example.com', 'bana..na@example.com', # Multiple @ characters - 'foo@bar@example.com' + 'foo@bar@example.com', # Query part - 'foobar@example.com?subject=foo' + 'foobar@example.com?subject=foo', ] ) def test_email_regex_invalid(input_string):