- Notifications
You must be signed in to change notification settings - Fork 46.7k
/
Copy pathis_valid_email_address.py
115 lines (94 loc) · 4.27 KB
/
is_valid_email_address.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
"""
Implements an is valid email address algorithm
@ https://en.wikipedia.org/wiki/Email_address
"""
importstring
email_tests: tuple[tuple[str, bool], ...] = (
("simple@example.com", True),
("very.common@example.com", True),
("disposable.style.email.with+symbol@example.com", True),
("other-email-with-hyphen@and.subdomains.example.com", True),
("fully-qualified-domain@example.com", True),
("user.name+tag+sorting@example.com", True),
("x@example.com", True),
("example-indeed@strange-example.com", True),
("test/test@test.com", True),
(
"123456789012345678901234567890123456789012345678901234567890123@example.com",
True,
),
("admin@mailserver1", True),
("example@s.example", True),
("Abc.example.com", False),
("A@b@c@example.com", False),
("abc@example..com", False),
("a(c)d,e:f;g<h>i[j\\k]l@example.com", False),
(
"12345678901234567890123456789012345678901234567890123456789012345@example.com",
False,
),
("i.like.underscores@but_its_not_allowed_in_this_part", False),
("", False),
)
# The maximum octets (one character as a standard unicode character is one byte)
# that the local part and the domain part can have
MAX_LOCAL_PART_OCTETS=64
MAX_DOMAIN_OCTETS=255
defis_valid_email_address(email: str) ->bool:
"""
Returns True if the passed email address is valid.
The local part of the email precedes the singular @ symbol and
is associated with a display-name. For example, "john.smith"
The domain is stricter than the local part and follows the @ symbol.
Global email checks:
1. There can only be one @ symbol in the email address. Technically if the
@ symbol is quoted in the local-part, then it is valid, however this
implementation ignores "" for now.
(See https://en.wikipedia.org/wiki/Email_address#:~:text=If%20quoted,)
2. The local-part and the domain are limited to a certain number of octets. With
unicode storing a single character in one byte, each octet is equivalent to
a character. Hence, we can just check the length of the string.
Checks for the local-part:
3. The local-part may contain: upper and lowercase latin letters, digits 0 to 9,
and printable characters (!#$%&'*+-/=?^_`{|}~)
4. The local-part may also contain a "." in any place that is not the first or
last character, and may not have more than one "." consecutively.
Checks for the domain:
5. The domain may contain: upper and lowercase latin letters and digits 0 to 9
6. Hyphen "-", provided that it is not the first or last character
7. The domain may also contain a "." in any place that is not the first or
last character, and may not have more than one "." consecutively.
>>> for email, valid in email_tests:
... assert is_valid_email_address(email) == valid
"""
# (1.) Make sure that there is only one @ symbol in the email address
ifemail.count("@") !=1:
returnFalse
local_part, domain=email.split("@")
# (2.) Check octet length of the local part and domain
iflen(local_part) >MAX_LOCAL_PART_OCTETSorlen(domain) >MAX_DOMAIN_OCTETS:
returnFalse
# (3.) Validate the characters in the local-part
ifany(
charnotinstring.ascii_letters+string.digits+".(!#$%&'*+-/=?^_`{|}~)"
forcharinlocal_part
):
returnFalse
# (4.) Validate the placement of "." characters in the local-part
iflocal_part.startswith(".") orlocal_part.endswith(".") or".."inlocal_part:
returnFalse
# (5.) Validate the characters in the domain
ifany(charnotinstring.ascii_letters+string.digits+".-"forcharindomain):
returnFalse
# (6.) Validate the placement of "-" characters
ifdomain.startswith("-") ordomain.endswith("."):
returnFalse
# (7.) Validate the placement of "." characters
returnnot (domain.startswith(".") ordomain.endswith(".") or".."indomain)
if__name__=="__main__":
importdoctest
doctest.testmod()
foremail, validinemail_tests:
is_valid=is_valid_email_address(email)
assertis_valid==valid, f"{email} is {is_valid}"
print(f"Email address {email} is {'not 'ifnotis_validelse''}valid")