Skip to content
Open
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
19 changes: 18 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Usage
$ pip install keg-mail


Initialize Keg-Mail in you application
Initialize Keg-Mail in your application

.. code::

Expand Down Expand Up @@ -55,6 +55,23 @@ Define email content
)


Validate the recipient

.. code::

import keg_mail.content

address = "user@example.com"
if keg_mail.content.Email.address_might_be_invalid(address):
# the address might be invalid (it might be incomplete, or there
# may be a typo)
. . .

else:
# address should be good, send the email
. . .


Send the email

.. code::
Expand Down
29 changes: 29 additions & 0 deletions keg_mail/content.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

from flask_mail import Message
from flask import current_app
import pymailcheck
import validator_collection

import keg_mail.utils as utils

Expand Down Expand Up @@ -124,6 +126,33 @@ def __init__(self, subject, content, type_=None):
self.content = content
self.type_ = type_

@classmethod
def validate_address(cls, address):
"""Returns `True` if the address looks like a valid e-mail address"""
try:
validator_collection.email(address)
return True

except ValueError:
return False

@classmethod
def address_might_be_invalid(cls, address):
"""Returns `True` if the address is or could be invalid, `False` otherwise"""
if not cls.validate_address(address):
return True

return bool(cls.get_invalid_address_suggestions(address))

@classmethod
def get_invalid_address_suggestions(cls, address):
"""Returns a suggested address if one exists, else `None`"""
suggestion = pymailcheck.suggest(address)
if suggestion is False:
return None

return suggestion['full']

def format(self, *args, **kwargs):
select_text = lambda item: getattr(item, 'text', item) # noqa
return Email(
Expand Down
54 changes: 54 additions & 0 deletions keg_mail/tests/test_content.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,60 @@ def setup_method(self):
self.content = content.EmailContent('{a}', '{a}')
self.content_b = content.EmailContent('{b}', '{b}')

def test_validate_address(self):
validate_address = content.Email.validate_address

assert validate_address('user@example.com') is True
assert validate_address('user@gmail.com') is True

# pet peeve, this address is valid, ensure it's seen that way
assert validate_address('user+foo@example.com') is True

assert validate_address('') is False
assert validate_address('user@invalid') is False
assert validate_address('invalid') is False

assert validate_address('user@example.con') is True
assert validate_address('user@gmaol.com') is True

def test_address_might_be_invalid(self):
address_might_be_invalid = content.Email.address_might_be_invalid

assert address_might_be_invalid('') is True
assert address_might_be_invalid('user@invalid') is True
assert address_might_be_invalid('invalid') is True

assert address_might_be_invalid('user@example.con') is True
assert address_might_be_invalid('user@gmaol.com') is True

# pet peeve, this address is valid, ensure it's seen that way
assert address_might_be_invalid("user+foo@example.com") is False

assert address_might_be_invalid('user@valid.com') is False
assert address_might_be_invalid('user@example.com') is False
assert address_might_be_invalid('user@gmail.com') is False

def test_get_invalid_address_suggestions(self):
get_invalid_address_suggestions = content.Email.get_invalid_address_suggestions

# can't make a suggestion on an empty address
assert get_invalid_address_suggestions('') is None

# no suggestions on a valid address
assert get_invalid_address_suggestions('user@example.com') is None
assert get_invalid_address_suggestions('user@gmail.com') is None
assert get_invalid_address_suggestions("user+foo@example.com") is None

# suggestions...
assert get_invalid_address_suggestions('user@example.con') == 'user@example.com'
assert get_invalid_address_suggestions('user@gmai.com') == 'user@gmail.com'

# the result for this address isn't deterministic
assert get_invalid_address_suggestions('user@gmaol.com') in (
'user@gmail.com',
'user@aol.com'
)

def test_equality(self):
# Same
assert (content.Email('a', self.content, 'a') ==
Expand Down
2 changes: 2 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
'Keg',
'SQLAlchemy-Utils',
'arrow',
'pymailcheck',
'validator-collection',
],
extras_require={
'mailgun': [
Expand Down