Every Python Application needs to send email at some point. Whether it's for reporting errors, status updates or simply the core functionality of the system this little recipe should help! Documented and Tested.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 | #!/usr/bin/env python
"""Sending Email in Python
A useful set of functions to facilitate the sending of emails in your Python
application. Supports plain text email body and file attachments complete with
Cc and Bcc support.
"""
from smtplib import SMTP
from itertools import chain
from errno import ECONNREFUSED
from mimetypes import guess_type
from subprocess import Popen, PIPE
from email.mime.base import MIMEBase
from email.mime.text import MIMEText
from socket import error as SocketError
from email.encoders import encode_base64
from email.mime.multipart import MIMEMultipart
from os.path import abspath, basename, expanduser
def get_mimetype(filename):
"""Returns the MIME type of the given file.
:param filename: A valid path to a file
:type filename: str
:returns: The file's MIME type
:rtype: tuple
"""
content_type, encoding = guess_type(filename)
if content_type is None or encoding is not None:
content_type = "application/octet-stream"
return content_type.split("/", 1)
def mimify_file(filename):
"""Returns an appropriate MIME object for the given file.
:param filename: A valid path to a file
:type filename: str
:returns: A MIME object for the givne file
:rtype: instance of MIMEBase
"""
filename = abspath(expanduser(filename))
basefilename = basename(filename)
msg = MIMEBase(*get_mimetype(filename))
msg.set_payload(open(filename, "rb").read())
msg.add_header("Content-Disposition", "attachment", filename=basefilename)
encode_base64(msg)
return msg
def send_email(to, subject, text, **params):
"""Send an outgoing email with the given parameters.
This function assumes your system has a valid MTA (Mail Transfer Agent)
or local SMTP server. This function will first try a local SMTP server
and then the system's MTA (/usr/sbin/sendmail) connection refused.
:param to: A list of recipient email addresses.
:type to: list
:param subject: The subject of the email.
:type subject: str
:param test: The text of the email.
:type text: str
:param params: An optional set of parameters. (See below)
:type params; dict
Optional Parameters:
:cc: A list of Cc email addresses.
:bcc: A list of Cc email addresses.
:files: A list of files to attach.
:sender: A custom sender (From:).
"""
# Default Parameters
cc = params.get("cc", [])
bcc = params.get("bcc", [])
files = params.get("files", [])
sender = params.get("sender", "root@localhost")
recipients = list(chain(to, cc, bcc))
# Prepare Message
msg = MIMEMultipart()
msg.preamble = subject
msg.add_header("From", sender)
msg.add_header("Subject", subject)
msg.add_header("To", ", ".join(to))
cc and msg.add_header("Cc", ", ".join(cc))
# Attach the main text
msg.attach(MIMEText(text))
# Attach any files
[msg.attach(mimify_file(filename)) for filename in files]
# Contact local SMTP server and send Message
try:
smtp = SMTP()
smtp.connect()
smtp.sendmail(sender, recipients, msg.as_string())
smtp.quit()
except SocketError as e:
if e.args[0] == ECONNREFUSED:
p = Popen(["/usr/sbin/sendmail", "-t"], stdin=PIPE)
p.communicate(msg.as_string())
else:
raise
def test():
send_email(["root@localhost"], "Test", "Test Message")
if __name__ == "__main__":
test()
|
cool