Siv Scripts

Solving Problems Using Code

Sun 25 February 2018

Sending Emails using Python 3.6+

Posted by Aly Sivji in Tutorials   

A Python 3.6 feature that has flown under the radar is the revamped email package.

In this post we will explore email and walk through an example of creating and sending an email using the Python Standard Library.


Exploring email

The email package is comprised of three major components:

To send emails we have to interact with email.message.EmailMessage; this class provides a high level abstraction of an email message. Everytime we want to write a new email, we create a new instance of the object:

from email.message import EmailMessage

msg = EmailMessage()

We can then modify msg using a dictionary-like interface:

msg['From'] = 'alysivji@gmail.com'
msg['To'] = 'alysivji+test@gmail.com'
msg['Subject'] = 'Test Email'

And set the email body using .set_content():

msg.set_content('Email body')

For HTML emails, we will need to specify subtype=html:

msg.set_content('HTML email with <a href="https://alysivji.github.io">link</a>', subtype='html')

Sending Plain Text Emails

We can leverage smtplib to send messages programatically from Python. I used this article as a template to get started.

# send_email.py

from email.headerregistry import Address
from email.message import EmailMessage
import os
import smtplib

# Gmail details
email_address = os.getenv('GMAIL_ADDRESS', None)
email_password = os.getenv('GMAIL_APPLICATION_PASSWORD', None)

# Recipent
to_address = (
    Address(display_name='Aly Sivji', username='alysivji', domain='gmail.com'),
)


def create_email_message(from_address, to_address, subject, body):
    msg = EmailMessage()
    msg['From'] = from_address
    msg['To'] = to_address
    msg['Subject'] = subject
    msg.set_content(body)
    return msg


if __name__ == '__main__':
    msg = create_email_message(
        from_address=email_address,
        to_address=to_address,
        subject='Hello World',
        body="Test sending the body.",
    )

    with smtplib.SMTP('smtp.gmail.com', port=587) as smtp_server:
        smtp_server.ehlo()
        smtp_server.starttls()
        smtp_server.login(email_address, email_password)
        smtp_server.send_message(msg)

    print('Email sent successfully')

Set a couple of environment variables before running the script:

$ export GMAIL_ADDRESS='alysivji@gmail.com'
$ export GMAIL_APPLICATION_PASSWORD='[INSERT PASSWORD HERE]'
$ python send_email.py
Email sent successfully

Let's check our email
Test Email in Inbox

Perfect

Notes
  • Emails are represented using Address
  • You will need to look up the SMTP details of your email provider, this is easily done by searching for "[Email Provider] SMTP" on Google.
    • Gmail: smtp.gmail.com port 587
    • Yahoo: smtp.mail.yahoo.com port 587
    • Live: smtp.live.com port 25
  • Two-factor Authentication complicates this process; we will need an application password to send emails. Setting up Application Password for a Google Account.
  • We can use smtplib.SMTP() as a context manager. I covered context managers in a previous post.

Sending HTML Emails

In order to send HTML emails, we need to specify the MIME type as text/html.

It is considered good practice to send a text/plain version along with a text/html version for the small minority of users who have disabled HTML emails. Also for our Pine users, can't forget about them!

We can attach additional MIME types with the .add_alternative() function.

# send_html_email.py

from email.headerregistry import Address
from email.message import EmailMessage
import os
import smtplib

# Gmail details
email_address = os.getenv('GMAIL_ADDRESS', None)
email_password = os.getenv('GMAIL_APPLICATION_PASSWORD', None)

# Recipent
to_address = (
    Address(display_name='Aly Sivji', username='alysivji', domain='gmail.com'),
)


def create_email_message(from_address, to_address, subject,
                         plaintext, html=None):
    msg = EmailMessage()
    msg['From'] = from_address
    msg['To'] = to_address
    msg['Subject'] = subject
    msg.set_content(plaintext)
    if html is not None:
        msg.add_alternative(html, subtype='html')
    return msg


HTML_MESSAGE = """\
<p>
  HTML Version!
</p>

<p>
  Link to <a href="https://alysivji.github.io">my blog</a>!
</p>
"""


if __name__ == '__main__':
    msg = create_email_message(
        from_address=email_address,
        to_address=to_address,
        subject='Hello World',
        plaintext="Plain text version.",
        html=HTML_MESSAGE
    )

    with smtplib.SMTP('smtp.gmail.com', port=587) as smtp_server:
        smtp_server.ehlo()
        smtp_server.starttls()
        smtp_server.login(email_address, email_password)
        smtp_server.send_message(msg)

    print('Email sent successfully')
$ export GMAIL_ADDRESS='alysivji@gmail.com'
$ export GMAIL_APPLICATION_PASSWORD='[INSERT PASSWORD HERE]'
$ python send_html_email.py
Email sent successfully

Test Email in Inbox

Looks good!


Additional Resources


 
    
 
 

Comments