0

I've just started learning Python and I'm trying to code a bot where it emails a HTML message along with a .docx (Microsoft Word) attachment. Here's my code and it works fine, but I'm confused with a few of the parameters.

import smtplib

from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.application import MIMEApplication

import os


#Email Variables

myemail = "myemail@hotmail.com"
recipient = "recipientemail@hotmail.com"

#Specifying MIME variables to include in email header

mime_details = MIMEMultipart('mixed')
mime_details['Subject'] = "This is my email subject!"
mime_details['From'] = myemail
mime_details['To'] = recipient

#Creating body of message

html_body = """\
<html>\
<p>\
My email message!
</p>\
</html>\
"""

#Attaching File To Email
os.chdir('C:\\Users\Kyle\Desktop\')
openfile = open('testfile.docx', 'rb')
doc = MIMEApplication(openfile.read(), _subtype='vnd.openxmlformats-officedocument.wordprocessingml.document')
doc.add_header('Content-Disposition', 'attachment', filename='My Content.docx')
mime_details.attach(doc)

openfile.close()

#Recording MIME types of email parts
htmlpart = MIMEText(html_body, _subtype='html')

#Attaching parts to message container
mime_details.attach(htmlpart)

#Sending message via SMTP server
s = smtplib.SMTP(host='smtp.live.com', port=587)
s.ehlo()
s.starttls()
s.login(myemail, 'password')
s.sendmail(myemail, recipient, mime_details.as_string())
s.quit()

I have a few questions regarding the code above, and I would greatly appreciate it if you can help me clear whatever confused thoughts I have regarding the modules above.

A) The add_header module

mime_details['Subject'] = "This is my email subject!"
mime_details['From'] = myemail
mime_details['To'] = recipient

As you can see from the snippet above, I clearly defined each header variable in the MIMEMultipart envelope. According to Python's docs, these values will be added to the header directly. I tried using the add_header() method directly as an alternative, something like:

mime_details.add_header(Subject = "This is my email subject!", To = recipient, From = myemail)

But it gave me errors. Any idea why?


B) At the last few lines of my code, I had to append a .as_string() to the payload I was sending. I tried taking it out but it gave me a TypeError: expected string or buffer.

The Python docs give me this:

as_string(unixfrom=False, maxheaderlen=0, policy=None)

Return the entire message flattened as a string.

I assume that I have to append this in every case? the mime_details object has two parts to it - the HTML message and the .docx file. Both will be converted to strings? What about image files?


C) When I open my file to attach, I read it in 'rb' mode.

Here's the code:

openfile = open('testfile.docx', 'rb')

I tried using 'r' but it raises an error. I've read up on this and 'rb' mode simply opens the file in binary. Why would it make sense to open a .docx file in binary mode? Am I to use 'rb' mode for ALL non.txt files?


D) Finally, I've seen some people use base64 encoding for attachments. I've searched around and a particularly popular script over here is below (from How to send email attachments with Python):

import smtplib, os
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email.mime.text import MIMEText
from email.utils import COMMASPACE, formatdate
from email import encoders

def send_mail( send_from, send_to, subject, text, files=[], server="localhost", port=587, username='', password='', isTls=True):
    msg = MIMEMultipart()
    msg['From'] = send_from
    msg['To'] = COMMASPACE.join(send_to)
    msg['Date'] = formatdate(localtime = True)
    msg['Subject'] = subject

    msg.attach( MIMEText(text) )

    for f in files:
        part = MIMEBase('application', "octet-stream")
        part.set_payload( open(f,"rb").read() )
        encoders.encode_base64(part)
        part.add_header('Content-Disposition', 'attachment; filename="{0}"'.format(os.path.basename(f)))
        msg.attach(part)

    smtp = smtplib.SMTP(server, port)
    if isTls: smtp.starttls()
    smtp.login(username,password)
    smtp.sendmail(send_from, send_to, msg.as_string())
    smtp.quit()

Why does content need to be encoded in base64? Does this apply to all files and is a general thing to be added, or can I ignore this? What situations am I to invoke that method?


Finally, being a noob at Python, some of my code above may be redundant\badly structured. If you have any tips\suggestions on how I can improve my simply script, please do let me know.

Thank you!

Community
  • 1
  • 1
Kyle
  • 598
  • 1
  • 7
  • 25
  • Too many Qs for **one** Q. Anyway, to start: `add_header` takes two positional arguments (name and value) then optional named parameters for the **single** header it adds. So in your case, `mime_details.add_header('Subject', 'This is my email subject!')`, etc -- three separate calls. See https://docs.python.org/3.4/library/email.message.html?highlight=add_header#email.message.Message.add_header . – Alex Martelli Jan 01 '15 at 21:30
  • What do you mean by single header it adds? I tried adding several arguments and it works fine e.g. doc.add_header('Content-Disposition', 'attachment', filename='Your Content.docx') – Kyle Jan 02 '15 at 12:06
  • That call will add a single header `Content-Disposition` with a single value of `attachment;filename='Your Content Dock'`. One call to `add_header` adds **one** header (singular), the optional extra keyword args only become part of the header's value. – Alex Martelli Jan 02 '15 at 14:54
  • @Kyle After 2 years can you solve this question, I think you got it – A.Raouf Aug 26 '17 at 17:01

0 Answers0