Package mailer :: Module mailer
[hide private]

Source Code for Module mailer.mailer

  1  #coding: UTF8 
  2  """ 
  3  mailer module 
  4   
  5  Simple front end to the smtplib and email modules, 
  6  to simplify sending email. 
  7   
  8  A lot of this code was taken from the online examples in the 
  9  email module documentation: 
 10  http://docs.python.org/library/email-examples.html 
 11   
 12  Released under MIT license. 
 13   
 14  Version 0.5 is based on a patch by Douglas Mayle 
 15   
 16  Sample code: 
 17   
 18      import mailer 
 19       
 20      message = mailer.Message() 
 21      message.From = "me@example.com" 
 22      message.To = "you@example.com" 
 23      message.Subject = "My Vacation" 
 24      message.Body = open("letter.txt", "rb").read() 
 25      message.attach("picture.jpg") 
 26       
 27      sender = mailer.Mailer('mail.example.com') 
 28      sender.send(message) 
 29   
 30  """ 
 31  import smtplib 
 32   
 33  # this is to support name changes 
 34  # from version 2.4 to version 2.5 
 35  try: 
 36      from email import encoders 
 37      from email.header import make_header 
 38      from email.message import Message 
 39      from email.mime.audio import MIMEAudio 
 40      from email.mime.base import MIMEBase 
 41      from email.mime.image import MIMEImage 
 42      from email.mime.multipart import MIMEMultipart 
 43      from email.mime.text import MIMEText 
 44  except ImportError: 
 45      from email import Encoders as encoders 
 46      from email.Header import make_header 
 47      from email.MIMEMessage import Message 
 48      from email.MIMEAudio import MIMEAudio 
 49      from email.MIMEBase import MIMEBase 
 50      from email.MIMEImage import MIMEImage 
 51      from email.MIMEMultipart import MIMEMultipart 
 52      from email.MIMEText import MIMEText 
 53   
 54  # For guessing MIME type based on file name extension 
 55  import mimetypes 
 56   
 57  from os import path 
 58   
 59  __version__ = "0.5" 
 60  __author__ = "Ryan Ginstrom" 
 61  __license__ = "MIT" 
 62  __description__ = "A module to send email simply in Python" 
 63   
64 -class Mailer(object):
65 """ 66 Represents an SMTP connection. 67 68 Use login() to log in with a username and password. 69 """ 70
71 - def __init__(self, host="localhost", port=0):
72 self.host = host 73 self.port = port 74 self._usr = None 75 self._pwd = None
76
77 - def login(self, usr, pwd):
78 self._usr = usr 79 self._pwd = pwd
80
81 - def send(self, msg):
82 """ 83 Send one message or a sequence of messages. 84 85 Every time you call send, the mailer creates a new 86 connection, so if you have several emails to send, pass 87 them as a list: 88 mailer.send([msg1, msg2, msg3]) 89 """ 90 server = smtplib.SMTP(self.host, self.port) 91 92 if self._usr and self._pwd: 93 server.login(self._usr, self._pwd) 94 95 try: 96 num_msgs = len(msg) 97 for m in msg: 98 self._send(server, m) 99 except TypeError: 100 self._send(server, msg) 101 102 server.quit()
103
104 - def _send(self, server, msg):
105 """ 106 Sends a single message using the server 107 we created in send() 108 """ 109 me = msg.From 110 if isinstance(msg.To, basestring): 111 you = [msg.To] 112 else: 113 you = list(msg.To) 114 server.sendmail(me, you, msg.as_string())
115
116 -class Message(object):
117 """ 118 Represents an email message. 119 120 Set the To, From, Subject, and Body attributes as plain-text strings. 121 Optionally, set the Html attribute to send an HTML email, or use the 122 attach() method to attach files. 123 124 Use the charset property to send messages using other than us-ascii 125 126 If you specify an attachments argument, it should be a list of 127 attachment filenames: ["file1.txt", "file2.txt"] 128 129 `To` should be a string for a single address, and a sequence 130 of strings for multiple recipients (castable to list) 131 132 Send using the Mailer class. 133 """ 134
135 - def __init__(self, To=None, From=None, Subject=None, Body=None, Html=None, 136 attachments=None, charset=None):
137 self.attachments = [] 138 if attachments: 139 for attachment in attachments: 140 if isinstance(attachment, basestring): 141 self.attachments.append((attachment, None)) 142 else: 143 try: 144 filename, cid = attachment 145 except (TypeError, IndexError): 146 self.attachments.append((attachment, None)) 147 else: 148 self.attachments.append((filename, cid)) 149 self.To = To 150 """string or iterable""" 151 self.From = From 152 """string""" 153 self.Subject = Subject 154 self.Body = Body 155 self.Html = Html 156 self.charset = charset or 'us-ascii'
157
158 - def as_string(self):
159 """Get the email as a string to send in the mailer""" 160 161 if not self.attachments: 162 return self._plaintext() 163 else: 164 return self._multipart()
165
166 - def _plaintext(self):
167 """Plain text email with no attachments""" 168 169 if not self.Html: 170 msg = MIMEText(self.Body, 'plain', self.charset) 171 else: 172 msg = self._with_html() 173 174 self._set_info(msg) 175 return msg.as_string()
176
177 - def _with_html(self):
178 """There's an html part""" 179 180 outer = MIMEMultipart('alternative') 181 182 part1 = MIMEText(self.Body, 'plain', self.charset) 183 part2 = MIMEText(self.Html, 'html', self.charset) 184 185 outer.attach(part1) 186 outer.attach(part2) 187 188 return outer
189
190 - def _set_info(self, msg):
191 if self.charset == 'us-ascii': 192 msg['Subject'] = self.Subject 193 else: 194 subject = unicode(self.Subject, self.charset) 195 msg['Subject'] = str(make_header([(subject, self.charset)])) 196 msg['From'] = self.From 197 if isinstance(self.To, basestring): 198 msg['To'] = self.To 199 else: 200 self.To = list(self.To) 201 msg['To'] = ", ".join(self.To)
202
203 - def _multipart(self):
204 """The email has attachments""" 205 206 msg = MIMEMultipart('related') 207 208 if self.Html: 209 outer = MIMEMultipart('alternative') 210 211 part1 = MIMEText(self.Body, 'plain', self.charset) 212 part1.add_header('Content-Disposition', 'inline') 213 214 part2 = MIMEText(self.Html, 'html', self.charset) 215 part2.add_header('Content-Disposition', 'inline') 216 217 outer.attach(part1) 218 outer.attach(part2) 219 msg.attach(outer) 220 else: 221 msg.attach(MIMEText(self.Body, 'plain', self.charset)) 222 223 self._set_info(msg) 224 msg.preamble = self.Subject 225 226 for filename, cid in self.attachments: 227 self._add_attachment(msg, filename, cid) 228 229 return msg.as_string()
230
231 - def _add_attachment(self, outer, filename, cid):
232 ctype, encoding = mimetypes.guess_type(filename) 233 if ctype is None or encoding is not None: 234 # No guess could be made, or the file is encoded (compressed), so 235 # use a generic bag-of-bits type. 236 ctype = 'application/octet-stream' 237 maintype, subtype = ctype.split('/', 1) 238 fp = open(filename, 'rb') 239 if maintype == 'text': 240 # Note: we should handle calculating the charset 241 msg = MIMEText(fp.read(), _subtype=subtype) 242 elif maintype == 'image': 243 msg = MIMEImage(fp.read(), _subtype=subtype) 244 elif maintype == 'audio': 245 msg = MIMEAudio(fp.read(), _subtype=subtype) 246 else: 247 msg = MIMEBase(maintype, subtype) 248 msg.set_payload(fp.read()) 249 # Encode the payload using Base64 250 encoders.encode_base64(msg) 251 fp.close() 252 253 # Set the content-ID header 254 if cid: 255 msg.add_header('Content-ID', '<%s>' % cid) 256 msg.add_header('Content-Disposition', 'inline') 257 else: 258 # Set the filename parameter 259 msg.add_header('Content-Disposition', 'attachment', filename=path.basename(filename)) 260 outer.attach(msg)
261
262 - def attach(self, filename, cid=None):
263 """ 264 Attach a file to the email. Specify the name of the file; 265 Message will figure out the MIME type and load the file. 266 """ 267 268 self.attachments.append((filename, cid))
269