2
\$\begingroup\$

I want to create a function that checks if an email has a username that is 5 to 20 characters long, starts with an alphabet letter and ends with one domain names of (.org, .net, .org). It also corrects, if the user enters a capital letter. I am fully aware of that the code does not make sense as employing regular expressions is the best way, but this is just an exercises for me to built better codes in python. So I was wondering, which would the best way if a code needs to check multiple things and eventually returns True/False (like in this example).

def is_valid_email(raw_email):

    email = raw_email.lower()

    try:
        name, domain_name = email.split('@')
    except ValueError:
        return False
    else:
        name_check = all([len(name) < 20, len(name) > 5, name[0].isalpha()])
        domains = (".com", ".net", ".org")
        domain_check = any([domain_name.endswith(domain)
                       for domain in domains])
        return name_check and domain_check


print(is_valid_email('[email protected]'))
\$\endgroup\$
6
  • \$\begingroup\$ "if a code needs to check multiple things" Like additional checks for the same purpose (valid email) or multiple purposes? \$\endgroup\$
    – Mast
    Oct 27, 2022 at 20:05
  • \$\begingroup\$ I mean the same purpose. \$\endgroup\$ Oct 27, 2022 at 20:11
  • 1
    \$\begingroup\$ This doesn't seem like a very good address validator. What about .co.uk and the dozens of other TLDs? \$\endgroup\$
    – Reinderien
    Oct 27, 2022 at 21:47
  • \$\begingroup\$ regex101.com/r/fbvPXi/1 \$\endgroup\$ Oct 28, 2022 at 8:56
  • \$\begingroup\$ You are aware that capital letters are allowed in email addresses (and that programs must not change the local part in any way)? \$\endgroup\$ Oct 28, 2022 at 9:00

2 Answers 2

3
\$\begingroup\$

You can make it easier to read by transforming the logic into a series of early returns:

TOP_LEVEL_DOMAINS = ("com", "net", "org")

def is_valid_email(email):

    if '@' not in email:
        return False

    name, domain_name = email.split('@', 1)

    if '.' not in domain_name:
        return False

    if domain_name.split('.')[-1] not in TOP_LEVEL_DOMAINS:
        return False

    if len(name) < 5 or len(name) > 20:
        return False

    if not name[0].isalpha():
        return False

    return True

There is another advantege: since we process each check separately, you can wrap the result into a Result class and send the reason for why the email is invalid instead of plain False.

Don't use all() when you can just do ... and ... and .... To rephrase the same: don't create collections for no reason.

Don't use exceptions for handling normal flow, they are not designed for it. Also it's much slower (if you call this function very often).


Note that real email addresses can contain non-latin letters, be case-sensitive and up to 256 symbols long.

\$\endgroup\$
0
1
\$\begingroup\$
email = raw_email.lower()

This not only changes the domain part (which is valid as domains are DNS names, and therefore case-insensitive), but it also changes the local part.

That will cause [email protected] to be silently converted to the different address [email protected]. You cannot assume those two addresses reach the same mailbox; it's entirely up to the destination MTA.

The only local-part that you may assume matches irrespective of case is postmaster.

You may need to carefully re-read the RFCs that define mail addresses.

\$\endgroup\$
1
  • \$\begingroup\$ I was thinking all email providers have incasesensitive mail addresses. \$\endgroup\$ Oct 29, 2022 at 10:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.