102

I have written a function for adding emails to newsletter base. Until I've added checking validity of sent email it was working flawlessly. Now each time I'm getting "Wrong email" in return. Can anybody see any errors here ? The regex used is :

\b[\w\.-]+@[\w\.-]+\.\w{2,4}\b and it is 100% valid (http://gskinner.com/RegExr/), but I may be using it wrong, or it may be some logic error :

def newsletter_add(request):
    if request.method == "POST":   
        try:
            e = NewsletterEmails.objects.get(email = request.POST['email'])
            message = _(u"Email is already added.")
            type = "error"
        except NewsletterEmails.DoesNotExist:
            if validateEmail(request.POST['email']):
                try:
                    e = NewsletterEmails(email = request.POST['email'])
                except DoesNotExist:
                    pass
                message = _(u"Email added.")
                type = "success"
                e.save()
            else:
                message = _(u"Wrong email")
                type = "error"

import re

def validateEmail(email):
    if len(email) > 6:
        if re.match('\b[\w\.-]+@[\w\.-]+\.\w{2,4}\b', email) != None:
            return 1
    return 0
ivanleoncz
  • 9,070
  • 7
  • 57
  • 49
muntu
  • 2,355
  • 4
  • 19
  • 18
  • 7
    100% valid? Won't match `foo+bar@example.com`, won't match `foo@museum.com`, and tons more. – Kos Dec 23 '13 at 10:37
  • 3
    100% valid? Your regex will match `a..@..............a`. – Zenadix Sep 25 '14 at 21:01
  • Actually @Kos @Zenadix...both of you are wrong with regards to the `\b[\w\.-]+@[\w\.-]+\.\w{2,4}\b` regex. I just tested it and it does the opposite of what both of you say. – theQuestionMan Feb 25 '21 at 03:03

7 Answers7

224
from django.core.exceptions import ValidationError
from django.core.validators import validate_email

value = "foo.bar@baz.qux"

try:
    validate_email(value)
except ValidationError as e:
    print("bad email, details:", e)
else:
    print("good email")
oblalex
  • 5,366
  • 2
  • 24
  • 25
171

UPDATE 2017: the code below is 7 years old and was since modified, fixed and expanded. For anyone wishing to do this now, the correct code lives around here.

Here is part of django.core.validators you may find interesting :)

class EmailValidator(RegexValidator):

    def __call__(self, value):
        try:
            super(EmailValidator, self).__call__(value)
        except ValidationError, e:
            # Trivial case failed. Try for possible IDN domain-part
            if value and u'@' in value:
                parts = value.split(u'@')
                domain_part = parts[-1]
                try:
                    parts[-1] = parts[-1].encode('idna')
                except UnicodeError:
                    raise e
                super(EmailValidator, self).__call__(u'@'.join(parts))
            else:
                raise

email_re = re.compile(
    r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*"  # dot-atom
    r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-011\013\014\016-\177])*"' # quoted-string
    r')@(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+[A-Z]{2,6}\.?$', re.IGNORECASE)  # domain
validate_email = EmailValidator(email_re, _(u'Enter a valid e-mail address.'), 'invalid')

so if you don't want to use forms and form fields, you can import email_re and use it in your function, or even better - import validate_email and use it, catching possible ValidationError.

def validateEmail( email ):
    from django.core.validators import validate_email
    from django.core.exceptions import ValidationError
    try:
        validate_email( email )
        return True
    except ValidationError:
        return False

And here is Mail::RFC822::Address regexp used in PERL, if you really need to be that paranoid.

Matthew Hegarty
  • 3,791
  • 2
  • 27
  • 42
cji
  • 6,635
  • 2
  • 20
  • 16
  • Thanks, had an import with email_re (that's the snippet across the web) but your second code block did the trick – Yogev Shelly Jan 03 '12 at 21:16
  • 2
    This is not valid anymore with the upcoming new top level domain extensions (more than 6 chars for the extensions I think) http://mashable.com/2011/06/21/icann-top-level-domains-change/ – jptsetung Jan 23 '12 at 05:44
  • I believe there may be a bug in the `email_re` expression. Specifically in the quoted string line, I'm guessing the intent is that `\001-011` should actually be `\001-\011`. Any thoughts? – kalefranz Sep 10 '13 at 05:51
  • Indeed, there is a typo in the code above. Source on github has since been fixed: https://github.com/django/django/blob/master/django/core/validators.py Fair warning to those copying and pasting that regex. – kalefranz Sep 10 '13 at 06:57
  • The update part is the best part. :D – Divij Sehgal Jan 27 '19 at 20:01
60

Ick, no, please, don't try to validate email addresses yourself. It's one of those things people never get right.

Your safest option, since you're already using Django, is to just take advantage of its form validation for email. Per the docs:

>>> from django import forms
>>> f = forms.EmailField()
>>> f.clean('foo@example.com')
u'foo@example.com'
>>> f.clean(u'foo@example.com')
u'foo@example.com'
>>> f.clean('invalid e-mail address')
...
ValidationError: [u'Enter a valid e-mail address.']
Amin Mir
  • 640
  • 8
  • 15
Nicholas Knight
  • 15,774
  • 5
  • 45
  • 57
7

You got it wrong, but it is a task that you can't do anyway. There is one and only one way to know if an RFC 2822 address is valid, and that is to send mail to it and get a response. Doing anything else doesn't improve the information content of your datum by even a fractional bit.

You also screw the human factor and acceptance property, for when you give validateEmail my address of

me+valid@mydomain.example.net

and you tell me I've made an error, I tell your application goodbye.

msw
  • 42,753
  • 9
  • 87
  • 112
  • fortunately this regex is not mine :) So you're also for the normal EmailField method ? – muntu Jul 10 '10 at 03:48
  • I don't speak django, but the argument applies to any attempt to syntactically validate an address. http://code.djangoproject.com/ticket/3344 Looks like not even EmailField may be up to the task (but I didn't look real hard) – msw Jul 10 '10 at 04:02
  • that particular ticket is marked as fixed since 07/04/07, and bug there was in unicode handling of translated error message. I think that using Django validators is relatively safe now. – cji Jul 10 '10 at 07:08
3

I can see many answers here are based on django framework of python. But for verifying an email address why to install such an heavy software. We have the Validate_email package for Python that check if an email is valid, properly formatted and really exists. Its a light weight package (size < 1MB).

INSTALLATION :

pip install validate_email

Basic usage:

Checks whether email is in proper format.

from validate_email import validate_email
is_valid = validate_email('example@example.com')

To check the domain mx and verify email exists you can install the pyDNS package along with validate_email.

Verify email exists :

from validate_email import validate_email
is_valid = validate_email('example@example.com',verify=True)

Returns True if the email exist in real world else False.

Himanshu Poddar
  • 7,112
  • 10
  • 47
  • 93
1

This regex will validate an email address with reasonable accuracy.

\w[\w\.-]*@\w[\w\.-]+\.\w+

It allows alphanumeric characters, _, . and -.

Zenadix
  • 15,291
  • 4
  • 26
  • 41
0

Change your code from this:

re.match('\b[\w.-]+@[\w.-]+.\w{2,4}\b', email)

to this:

re.match(r'\b[\w.-]+@[\w.-]+.\w{2,4}\b', email)

works fine with me.