1
2
3 """
4 Introduction
5 ============
6
7 TurboMail is a TurboGears extension - meaning that it starts up and
8 shuts down alongside TurboGears applications you write in the same
9 way that visit tracking and identity do. TurboMail uses built-in
10 Python modules for SMTP communication and MIME e-mail creation, but
11 greatly simplifies these tasks by performing the grunt-work for you.
12
13 Being multi-threaded, TurboMail allows you to enqueue messages to be
14 sent and then immediately continue with processing, resulting in a
15 much more fluid user experience. Threads are handled intelligently
16 (increasing the number of threads as demand increases) and they are
17 automatically recycled. There is only ever one SMTP connection per
18 thread.
19
20 Benchmarking
21 ------------
22
23 Throughput using the default options is sufficient for most use:
24 100 messages in 45 seconds; just over 2 messages a second. Using
25 a greater number of threads, 10 vs. 4, 100 messages take 30
26 seconds; just over 3 messages a second. YMMV. Note that if a
27 thread is idle, it will immediately deliver requests added to
28 the queue, thus increasing the idle time will increase sparse
29 performance.
30
31 TurboMail is heavily inspired by PHPMailer, a very, very handy class
32 for PHP 4 & 5 by Brent R. Matzelle.
33
34
35 Installation
36 ============
37
38 Simply easy_install the package::
39
40 easy_install TurboMail
41
42 TurboMail installs no external scripts.
43
44 Upgrade
45 =======
46
47 Upgrading also uses easy_install::
48
49 easy_install -U TurboMail
50
51 Configuration
52 =============
53
54 TurboMail understands a large number of configuration options, all
55 piggy-backed from your application's configuration. Organized into
56 two groups, the advanced set can be safely ignored in most
57 applications. Each option is listed with its default value.
58
59 Simple Options
60 --------------
61
62 - I{mail.on} (Default: B{False}) Enable TurboMail. B{Required.}
63 - I{mail.server} (Default: B{None}) SMTP server address.
64 B{Required.}
65 - I{mail.username} (Default: B{None})
66 - I{mail.password} (Default: B{None})
67
68 Both a username and password are required to enable
69 authentication.
70
71 Advanced Options
72 ----------------
73
74 - I{mail.debug} (Default: B{False}) Output all SMTP server
75 communications.
76 - I{mail.interval} (Default: B{10}) Polling delay between new
77 thread creation, in seconds.
78 - I{mail.threads} (Default: B{4}) Maximum number of concurrent
79 threads.
80 - I{mail.jobs} (Default: B{10}) Maximum number of job units per
81 thread.
82 - I{mail.timeout} (Default: B{60}) Maximum time a worker thread
83 will wait for additional jobs, in seconds.
84 - I{mail.tls} (Default: None) Enable or disable TLS, None will
85 attempt to auto-detect TLS. This will not always work.
86 - I{mail.encoding} (Default: B{'us-ascii'}) Set the character
87 set and encoding on the MIMEText parts of the message.
88 Common character sets include:
89 - us-ascii - I{Performs no encoding, but is 7bit only.}
90 - iso-8859-1 - I{Uses quoted-printable encoding.}
91 - utf-8 - I{Uses base64 encoding.}
92 Due to the way Python's email package handles character sets,
93 the following additional virtual character sets are provided
94 by TurboMail, and will override the global defaults:
95 - utf-8-qp - I{Sets utf-8 to use quoted-printable encoding.}
96 Headers are not encoded. DIY.
97 - I{mail.polling} (Default: B{False}) If enabled, configures the
98 thread pool to poll every I{mail.interval} seconds for new
99 jobs. This may give performance benefits to the running
100 application. The default behaviour is to create new threads
101 as soon as work is enqueued, resulting in faster delivery.
102
103 In debug mode using a single thread with a maximum of one job
104 can be advantageous. Having a single thread with a maximum of a
105 single job limits TurboMail to a single SMTP connection at a
106 time and automatically disconnects for I{each message}.
107
108 Basic Usage
109 ===========
110
111 To use TurboMail in your TurboGears application, after adding the
112 appropriate configuration options to your application, perform the
113 following steps:
114
115 1. Import TurboMail::
116
117 import turbomail
118
119 2. Create a L{Message} or L{KIDMessage} object::
120
121 message = turbomail.Message(from, to, subject)
122
123 3. Set some content for your message::
124
125 message.plain = "Hello world!"
126
127 4. Enqueue your message::
128
129 turbomail.enqueue(message)
130
131 Your message will now have been enqueued. It will take at most
132 I{mail.interval} seconds for a thread to be created to deliver it.
133 The best case scenario is if there is an idle thread waiting, in
134 which case delivery will be immediate.
135
136 Advanced Usage - Logging
137 ========================
138
139 Additionally, you can configure your application to log TurboMail
140 events differently, in a more loggable and machine parseable way.
141 You do so by adding the following lines to the formatters section
142 of your log.cfg::
143
144 [[[timed_message]]]
145 format='*(asctime)s *(message)s'
146
147 Add the following to the handlers section of log.cfg::
148
149 [[[mail_out]]]
150 class='StreamHandler'
151 level='INFO'
152 args='(sys.stdout,)'
153 formatter='timed_message'
154
155 And finally, add the following to your dev.cfg::
156
157 [[[mail]]]
158 level='INFO'
159 qualname='turbomail.dispatch'
160 handlers=['mail_out']
161 propagate=0
162
163 If you wish to log mail dispatch to a file, for example in your
164 production configuration, use this instead of the above::
165
166 [[handlers]]
167 [[[mail_out]]]
168 args="('mail.log',)"
169 class='StreamHandler'
170 level='INFO'
171 formatter='timed_message'
172
173 [[loggers]]
174 [[[mail]]]
175 level='INFO'
176 qualname='turbomail.dispatch'
177 handlers=['mail_out']
178 propagate=0
179
180 The format of turbomail.dispatch INFO log entries is::
181
182 [user@]server size ("from_name") from_addr ("to_name") to_addr - subject
183
184 Designed for easy parsing and tracking of statistics, the log
185 format uses the following conventions:
186
187 - Entries between square brackets are optional and may be omitted.
188 - Entries between round brackets may be replaced with a dash if
189 unavailable.
190 - The size field is in bytes and represents the total size of the
191 MIME-encoded message including headers, after character set
192 conversion.
193 - When sending to multiple recipients, the to_addr field becomes
194 the number of recipients wrapped in round brackets. E.g. "(3)"
195 - The subject field extends to the EOL - quotes, dashes, and other
196 symbols should be treated as part of the subject.
197
198 Changelog
199 =========
200
201 Version 1.0
202 -----------
203 - Initial release.
204
205 Version 1.0.1
206 -------------
207 - Minor updates to remove unneeded arguments.
208 - Complete source-level epydoc documentation.
209
210 Version 1.0.4.1
211 ---------------
212 - Better auto-detection of TLS capability.
213 - A new configuration directive, mail.tls; True, False, or
214 None to auto-detect.
215 - Fixes a bug in KIDMessage which rendered it
216 non-functional.
217 - Changed the behavior of a worker dying from old age to spawn
218 a new process immediately.
219 - Minor fixes and updates to the documentation.
220 - Benchmark results in the documentation.
221
222 Version 1.0.4.2
223 ---------------
224 - Added encoding configuration directive.
225 - Encoding can be passed to a Message constructor to override
226 the encoding on a message-by-message basis.
227
228 Version 1.1
229 -----------
230 - Cleaned up the log output.
231 - Added documentation for logging.
232 - KID i18n session bug fixed in TurboGears 1.0 and trunk SVN.
233 - Marked as stable for the Python Cheese Shop.
234
235 Version 2.0
236 -----------
237 - Default thread creation mechanism is on-demand, not polling.
238 You can change back to the (old) polling mechanism by
239 setting the following configuration option::
240 mail.polling = True
241 - TemplateMessage has been renamed KIDMessage.
242 - Can now use 'utf-8-qp' to configure the 'utf-8' charset for
243 quoted-printable encoding.
244 - MIME-encoded message generation was re-written. Now, simple
245 plain-text-only messages have almost zero overhead.
246 Complexity of the generated document increases with feature
247 use.
248 - It is now safe to import enqueue from TurboMail - it no
249 longer polymorphs after TurboGears start-up and shutdown.
250 - Enhanced logging output - see above.
251 - Better tracking of when to rebuild the MIME message by using
252 a dirty flag.
253 - Many, many additional headers. Look at the documentation for
254 the message class for more information.
255 - Multiple recipients.
256
257 There is, however, an outstanding bug in KIDMessage. When
258 generating the plain-text alternative KID seems to default
259 to ascii encoding, which bombs out if you use any extended
260 characters in the template, or variables passed to the template.
261
262 Version 2.0.1
263 -------------
264 - Applied patch submitted by Jason Chu to allow overriding of
265 the Sender and Return-Path headers.
266 - Applied patch submitted by Jason Chu to correct the MIME
267 type of dual text & html messages with attachments.
268
269 Version 2.0.2
270 -------------
271 - Added a generic ControllerMessage which uses the output of
272 any function or method that returns HTML.
273 - Changed the behaviour of the attach and embed methods to
274 pull content from an existing file-like object or open an
275 on-disk file.
276 - Corrected a conditional testing for the presense of
277 smptfrom as a message property. Thanks James!
278
279 Version 2.0.3
280 -------------
281 - The plain and rich properties of the Message class can now
282 be callables, executed at delivery-time.
283 - Removed ControllerMessage in favor of using the above.
284 - Deprecated use of KIDMessage in favor of the above.
285
286 @var _queue: After TurboGears startup within an application which has
287 enabled TurboMail, I{queue} is an instance of a MailPool
288 object.
289
290 """
291
292 from turbomail.release import \
293 version as __version__, \
294 author as __author__, \
295 email as __email__, \
296 license as __license__, \
297 copyright as __copyright__
298
299 import logging
300 log = logging.getLogger("turbomail")
301
302 from turbomail.startup import start_extension, shutdown_extension
303 from turbomail.exceptions import *
304 from turbomail.message import Message, KIDMessage
305
306
307 __all__ = ['MailException', 'MailNotEnabledException', 'MailConfigurationException', 'Message', 'KIDMessage', 'enqueue', 'dispatch', 'exceptions', 'message', 'pool', 'startup']
308
309
310 _queue = None
311
320