MIMEMultipart, MIMEText, MIMEBase, and payloads for sending email with file attachment in Python

Ken Lin picture Ken Lin · Aug 8, 2016 · Viewed 24.8k times · Source

Without much prior knowledge of MIME, I tried to learned how to write a Python script to send an email with a file attachment. After cross-referencing Python documentation, Stack Overflow questions, and general web searching, I settled with the following code [1] and tested it to be working.

import smtplib
from email.MIMEMultipart import MIMEMultipart
from email.MIMEText import MIMEText
from email.MIMEBase import MIMEBase
from email import encoders

fromaddr = "YOUR EMAIL"
toaddr = "EMAIL ADDRESS YOU SEND TO"

msg = MIMEMultipart()

msg['From'] = fromaddr
msg['To'] = toaddr
msg['Subject'] = "SUBJECT OF THE EMAIL"

body = "TEXT YOU WANT TO SEND"

msg.attach(MIMEText(body, 'plain'))

filename = "NAME OF THE FILE WITH ITS EXTENSION"
attachment = open("PATH OF THE FILE", "rb")

part = MIMEBase('application', 'octet-stream')
part.set_payload((attachment).read())
encoders.encode_base64(part)
part.add_header('Content-Disposition', "attachment; filename= %s" % filename)

msg.attach(part)

server = smtplib.SMTP('smtp.gmail.com', 587)
server.starttls()
server.login(fromaddr, "YOUR PASSWORD")
text = msg.as_string()
server.sendmail(fromaddr, toaddr, text)
server.quit()
  1. I have a rough idea of how this script works now, and worked out the following workflow. Please let me know how accurate my flowchart(?) is.

     as.string()  
     |
     +------------MIMEMultipart  
                  |                                                |---content-type  
                  |                                   +---header---+---content disposition  
                  +----.attach()-----+----MIMEBase----|  
                                     |                +---payload (to be encoded in Base64)
                                     +----MIMEText
    
  2. How do I know when to use MIMEMultipart, MIMEText and MIMEBase? This seems like a complicated question, so maybe just offer some general rules-of-thumb to me?

  3. I read that MIME has a tree-like structure[2] , does that mean MIMEMultipart is always at the top?
  4. In the first code block, MIMEMultipart encodes ['From'], ['To'], and ['Subject'], but in the Python documentation, MIMEText can also be used to encode ['From'], ['To'], and ['Subject']. How to do I decide one to use?
  5. What exactly is a "payload"? Is it some content to be transported? If so, what kind of content does this include (I noticed that body text and attachment are treated as payloads)? I thought this would be an easy question but I just could not find a satisfying, reliable, and simple answer.
  6. Is is true that although MIME can attach file formats much simpler than just some texts, at the end all the encoding, header information, and payloads are all turned into strings so that they can be passed into .sendmail()?

[1]http://naelshiab.com/tutorial-send-email-python/
[2]http://blog.magiksys.net/generate-and-send-mail-with-python-tutorial

Answer

zvone picture zvone · Sep 23, 2018

E-mail messages

An e-mail message consists of headers (e.g. "From", "To", "Subject" etc.) and body (see RFC 822, section 3.1).

The body of the message is, by default, treated as plain ASCII text. MIME (RFC 2045, RFC 2046, RFC 2047, RFC 2048, RFC 2049) defines extensions which allow specifying different types of email content.

One very useful thing you are able to with MIME is specify a Content-Type (e.g. text/html or application/octet-stream).

Another useful thing is that you can create a message with multiple parts (for instance, if you want to have both HTML and an image within the HTML). This is done by specifying a multipart Content-Type (RFC 2046, section 5.1).

Multipart messages

If a message has a multipart Content-Type, that means it consists of multiple messages and each of them defines its own Content-Type (which can again be multipart or something else). Multipart messages are in Python represented by MIMEMultipart class.

So, to answer question 3: When MIMEMultipart is used, then yes, it is a tree-like structure, but if only MIMEText is used, then it is not a tree.

Question 4 asks on which class to set the headers ("To", "From" etc.) - that is done the the Message class, but all MIME classes inherit from Message, so it can be done on any of them, but those headers only make sense on the root part of a multipart message.

In other words, if a message consists of only one MIME part, specify headers on that part. If it consists of mutiple parts, then the root is a MIMEMultipart - specify the headers on that part.

Question 2 asks "when to use MIMEMultipart, MIMEText and MIMEBase". - MIMEBase is just a base class. As the specification says: "Ordinarily you won’t create instances specifically of MIMEBase" - MIMEText is for text (e.g. text/plain or text/html), if the whole message is in text format, or if a part of it is. - MIMEMultipart is for saying "I have more than one part", and then listing the parts - you do that if you have attachments, you also do it to provide alternative versions of the same content (e.g. a plain text version plus an HTML version)

Question 5 "What exactly is a "payload"?" - that is just a fancy word for the content of the message (or message part)

Question 6 There is a limitation to using only 7 bits in SMTP. See this answer for more details.

I did not completely understand Question 1, but it seems that the chart is more or less correct. BTW, I would not use MIMEBase here, because there is MIMEApplication which seems more appropriate for the intended purpose.