Sending email with Python

This is a note from learning Python’s email and smtplib modules.
Most of emails nowadays consists of 3 parts: plain text content, HTML content and attachments. Python’s smtplib library indeed saved us a lot of work.

1. Create a multipart/alternative email envelope.

from email.mime.multipart import MIMEMultipart
alter = MIMEMultipart('alternative')
msg = MIMEMultipart()

This will create an envelope that will contain all 3 mentioned parts. In mutt’s view:

1                     [multipa/alternativ, 7bit, 16K] 
2 ├─>                 [text/plain, base64, utf-8, 0.1K] 
3 ├─>                 [text/html, base64, utf-8, 0.1K]  
4 ca.crt                              [applica/octet-stre, base64, 1.5K]

Some MIME type is truncated, but you get the idea.

2. Unicode
You will have to append the coding part in the source file.

#!/usr/bin/python
# -*- coding: utf-8 -*-

The second line must exist.

Then a unicode title and text:

from email.mime.text import MIMEText

# msg and alter is derived from the previous example
msg['Subject'] = u'中文'
text = u'正在用Python写东西'
alter.attach(MIMEText(text, 'plain', 'utf-8'))

msg.attach(alter)

Appending HTML just change the plain to html when you call MIMEText against HTML content.

3. Add attachments
Mind the relative / absolute path from the location you execute the script. It’s recommend to use absolute path.

from os.path import basename, dirname, realpath

# assuming I will attach all the files in the same location as the script itself, also a file from other relative path.
cwd = dirname(realpath(__file__))
files = ['myfile.bin', 'herfile.bin', '../other/dir/s.bin']

for f in files:
        # use absolute path
        with open(cwd + '/' + f, "rb") as fil:
            msg.attach(MIMEApplication(
                fil.read(),
                Content_Disposition='attachment; filename="%s"' % basename(f),
                Name=basename(f)
            ))

4. Message ID.
Some SMTP server supports adding message id to your email on a specific port, e.g. 25. But when you use starttls to connect the SMTP or use a different port, e.g. 587, Message-Id may not be added automatically. So your email probably won’t be received as it’s required in RFC 2822.

One way to get around that is to generate your own message id. Then you could use starttls() to send your emails securely.

from email.utils import make_msgid

# Inherit the msg from above example.
msg['Message-Id'] = make_msgid('example.com')

smtp = smtplib.SMTP('smtp.example.com', 587)
smtp.starttls()
smtp.login('[email protected]', 'emailpassword')
smtp.sendmail(sender, to, msg.as_string().encode('utf-8'))
smtp.quit()

Note that the message id it generated will use the host name of your current machine if you do not specify.

Leave a comment

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.