Where I work, we run a number of login scripts written in Python. When an error occurs in one of those scripts, we want to know. So we wrote a simple Python script to email the error to us. Since then, I’ve needed to figure out a way to send attachments with some of my more advanced scripts. If you’re a long-time reader of this blog, then you may remember wxPyMail, which was a simple wxPython program that could send email. In this article, you’ll discover how to send email with just Python’s standard library. We will focus on smtplib and the email modules.
Sending Email with smtplib
Sending email with smtplib is super simple. Do you want to see how simple? Of course you do! Let’s take a look:
import smtplib import string SUBJECT = "Test email from Python" TO = "mike@mydomain.com" FROM = "python@mydomain.com" text = "blah blah blah" BODY = string.join(( "From: %s" % FROM, "To: %s" % TO, "Subject: %s" % SUBJECT , "", text ), "\r\n") server = smtplib.SMTP(HOST) server.sendmail(FROM, [TO], BODY) server.quit()
Notice that the actual connection and sending of the email was only two lines of code. The rest of the code was just setting up the message to send. At work, we wrap this all up in a callable function and pass it some information, like what the error was and who to send the error to. If you need to login, add a line right after you create the server variable where you do the following: server.login(username, password)
Send an Email with an Attachment
Now let’s look at how to send an email with an attachment. For this script, we’ll also use the email module. Here’s a simple example based on some code I wrote recently:
import os import smtplib from email import Encoders from email.MIMEBase import MIMEBase from email.MIMEMultipart import MIMEMultipart from email.Utils import formatdate filePath = r'\\some\path\to\a\file' def sendEmail(TO = "mike@mydomain.com", FROM="support@mydomain.com"): HOST = "mail.mydomain.com" msg = MIMEMultipart() msg["From"] = FROM msg["To"] = TO msg["Subject"] = "You've got mail!" msg['Date'] = formatdate(localtime=True) # attach a file part = MIMEBase('application', "octet-stream") part.set_payload( open(filePath,"rb").read() ) Encoders.encode_base64(part) part.add_header('Content-Disposition', 'attachment; filename="%s"' % os.path.basename(filePath)) msg.attach(part) server = smtplib.SMTP(HOST) # server.login(username, password) # optional try: failed = server.sendmail(FROM, TO, msg.as_string()) server.close() except Exception, e: errorMsg = "Unable to send email. Error: %s" % str(e) if __name__ == "__main__": sendEmail()
There’s a fair bit going on here, so let’s step through the new stuff. First we import all the bits and pieces we need from the email module. Then we create a nice function for sending email. Next we create a MIMEMultipart object. This handy object will hold our email. It uses a dict-like interface to add fields like TO, FROM, SUBJECT, etc. You’ll notice that we also have a date field. This just grabs your PC’s current date and converts it to the appropriate MIME email format.
The piece that we’re most interested in is how to attach a file. Here we create a MIMEBase object and set its payload to the file we want to attach. Note that we need to tell it to read the file as a binary file even if the file is in plain text. Next we encode the stream in base 64. The last two steps are to add a header and then attach the MIMEBase object to our MIMEMultipart object. If you had multiple files to attach, then you’d probably want to put this section into some kind of loop and loop over the files. In fact, that’s what I did in that wxPyMail example I mentioned earlier.
Anyway, once you’ve got all that done, then you do basically the same thing as you did in the smtplib example above. The only difference is that we changed the following line:
server.sendmail(FROM, TO, msg.as_string())
Note the msg.as_string. We needed to convert out object to a string to make this work. We also put the sendmail function inside a try/except statement in case something bad happens and our email fails to send. If we wanted to, we could have wrapped the try/except in a while loop so that we could retry sending the email X number of times if it failed.
Wrapping Up
We’ve covered how to send a simple email and how to send one with an attachment. The email module has lots more functionality built into it that is not covered here, so be sure to read the documentation. Have fun coding!
Additional Reading
- The smtplib module
- Python’s email module
Thanks! I've been looking for a clear explanation of how to do this for a while.
Thanks for the hints. That could prove handy.
– Mike
Thanks for the link.
– Mike
I know about MX records as well, but I'm not sure why you'd need to mess with those if all the smtplib wants is the host address, like mail.mydomain.com. I've never had to provide MX record info to Thunderbird or Outlook either. Could you explain some more about what you mean? I don't mind updating the article or writing a second one with additional information if I think it's valuable.
Thanks,
– Mike
He is thinking about creating a program that does not require an external smtp server to relay the messages, but that handles relaying directly. I would be very interest in learning how to do the mx querying for this purpose.