Made MailSender non IO-blocking, and improved MailSender documentation

This commit is contained in:
Pablo Hoffman 2010-03-22 13:37:37 -03:00
parent 1dfc79b5d0
commit 87e68e7438
2 changed files with 50 additions and 40 deletions

View File

@ -5,14 +5,17 @@ Sending email
=============
.. module:: scrapy.mail
:synopsis: Helpers to easily send e-mail.
:synopsis: Email sending facility
Although Python makes sending e-mail relatively easy via the `smtplib`_
library, Scrapy provides its own class for sending emails which is very easy to
use and it's implemented using `Twisted non-blocking IO`_, to avoid affecting
the crawling performance.
library, Scrapy provides its own facility for sending emails which is very easy
to use and it's implemented using `Twisted non-blocking IO`_, to avoid
interfering with the non-blocking IO of the crawler.
It's also very easy to configure by just configuring a few settings.
.. _smtplib: http://docs.python.org/library/smtplib.html
.. _Twisted non-blocking IO: http://twistedmatrix.com/projects/core/documentation/howto/async.html
It also has built-in support for sending attachments.
@ -34,30 +37,45 @@ uses `Twisted non-blocking IO`_, like the rest of the framework.
.. class:: MailSender(smtphost, mailfrom)
``smtphost`` is a string with the SMTP host to use for sending the emails.
If omitted, :setting:`MAIL_HOST` will be used.
:param smtphost: the SMTP host to use for sending the emails. If omitted,
:setting:`MAIL_HOST` setting will be used.
:type smtphost: str
``mailfrom`` is a string with the email address to use for sending messages
(in the ``From:`` header). If omitted, :setting:`MAIL_FROM` will be used.
:param mailfrom: the address used to send emails (in the ``From:`` header).
If omitted, :setting:`MAIL_FROM` setting will be used.
:type mailfrom: str
.. method:: MailSender.send(to, subject, body, cc=None, attachs=())
.. method:: send(to, subject, body, cc=None, attachs=())
Send mail to the given recipients
Send email to the given recipients
``to`` is a list of email recipients
:param to: the email recipients
:type to: list
``subject`` is a string with the subject of the message
:param subject: the subject of the email
:type subject: str
``cc`` is a list of emails to CC
:param cc: the emails to CC
:type cc: list
``body`` is a string with the body of the message
:param body: the email body
:type body: str
``attachs`` is an iterable of tuples (attach_name, mimetype, file_object)
where:
``attach_name`` is a string with the name will appear on the emails attachment
``mimetype`` is the mimetype of the attachment
``file_object`` is a readable file object
:param attachs: an iterable of tuples ``(attach_name, mimetype,
file_object)`` where ``attach_name`` is a string with the name will
appear on the emails attachment, ``mimetype`` is the mimetype of the
attachment and ``file_object`` is a readable file object with the
contents of the attachment
:type attachs: iterable
.. _Twisted non-blocking IO: http://twistedmatrix.com/projects/core/documentation/howto/async.html
MailSender settings
===================
These settings define the default constructor values of the :class:`MailSender`
class, and can be used to configure email notifications in your project without
writing any code (for those extensions that use the :class:`MailSender` class):
* :setting:`MAIL_FROM`
* :setting:`MAIL_HOST`

View File

@ -47,34 +47,26 @@ class MailSender(object):
part = MIMEBase(*mimetype.split('/'))
part.set_payload(f.read())
Encoders.encode_base64(part)
part.add_header('Content-Disposition', 'attachment; filename="%s"' % attach_name)
part.add_header('Content-Disposition', 'attachment; filename="%s"' \
% attach_name)
msg.attach(part)
else:
msg.set_payload(body)
# FIXME ---------------------------------------------------------------------
# There seems to be a problem with sending emails using deferreds when
# the last thing left to do is sending the mail, cause the engine stops
# the reactor and the email don't get send. we need to fix this. until
# then, we'll revert to use Python standard (IO-blocking) smtplib.
#dfd = self._sendmail(self.smtphost, self.mailfrom, rcpts, msg.as_string())
#dfd.addCallbacks(self._sent_ok, self._sent_failed,
# callbackArgs=[to, cc, subject, len(attachs)],
# errbackArgs=[to, cc, subject, len(attachs)])
import smtplib
smtp = smtplib.SMTP(self.smtphost)
smtp.sendmail(self.mailfrom, rcpts, msg.as_string())
log.msg('Mail sent: To=%s Cc=%s Subject="%s"' % (to, cc, subject))
smtp.close()
# ---------------------------------------------------------------------------
dfd = self._sendmail(self.smtphost, self.mailfrom, rcpts, msg.as_string())
dfd.addCallbacks(self._sent_ok, self._sent_failed,
callbackArgs=[to, cc, subject, len(attachs)],
errbackArgs=[to, cc, subject, len(attachs)])
reactor.addSystemEventTrigger('before', 'shutdown', lambda: dfd)
def _sent_ok(self, result, to, cc, subject, nattachs):
log.msg('Mail sent OK: To=%s Cc=%s Subject="%s" Attachs=%d' % (to, cc, subject, nattachs))
log.msg('Mail sent OK: To=%s Cc=%s Subject="%s" Attachs=%d' % \
(to, cc, subject, nattachs))
def _sent_failed(self, failure, to, cc, subject, nattachs):
errstr = str(failure.value)
log.msg('Unable to send mail: To=%s Cc=%s Subject="%s" Attachs=%d - %s' % (to, cc, subject, nattachs, errstr), level=log.ERROR)
log.msg('Unable to send mail: To=%s Cc=%s Subject="%s" Attachs=%d - %s' % \
(to, cc, subject, nattachs, errstr), level=log.ERROR)
def _sendmail(self, smtphost, from_addr, to_addrs, msg, port=25):
""" This is based on twisted.mail.smtp.sendmail except that it