Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions docs/03-basic-validators.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand All @@ -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")
```


Expand Down
30 changes: 23 additions & 7 deletions src/validataclass/validators/email_validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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`)
Expand All @@ -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:
"""
Expand Down Expand Up @@ -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
27 changes: 24 additions & 3 deletions tests/validators/email_validator_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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):
Expand All @@ -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):
Expand Down