Added SMTP-AUTH support to scrapy.mail (closes #149)

This commit is contained in:
Pablo Hoffman 2010-06-13 17:14:46 -03:00
parent 495f23dea2
commit bd16d1cd48
4 changed files with 94 additions and 55 deletions

View File

@ -8,17 +8,15 @@ Sending e-mail
:synopsis: Email sending facility
Although Python makes sending e-mails relatively easy via the `smtplib`_
library, Scrapy provides its own facility for sending e-mails 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, having only a few settings.
library, Scrapy provides its own facility for sending e-mails 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 also provides a
simple API for sending attachments and it's very easy to configure, with a few
:ref:`settings <topics-email-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.
Quick example
=============
@ -35,7 +33,7 @@ MailSender class reference
MailSender is the preferred class to use for sending emails from Scrapy, as it
uses `Twisted non-blocking IO`_, like the rest of the framework.
.. class:: MailSender(smtphost, mailfrom)
.. class:: MailSender(smtphost=None, mailfrom=None, smtpuser=None, smtppass=None, smtpport=None):
:param smtphost: the SMTP host to use for sending the emails. If omitted, the
:setting:`MAIL_HOST` setting will be used.
@ -45,6 +43,17 @@ uses `Twisted non-blocking IO`_, like the rest of the framework.
If omitted, the :setting:`MAIL_FROM` setting will be used.
:type mailfrom: str
:param smtpuser: the SMTP user. If omitted, the :setting:`MAIL_USER`
setting will be used. If not given, no SMTP authentication will be
performed.
:type smtphost: str
:param smtppass: the SMTP pass for authetnication.
:type smtppass: str
:param smtpport: the SMTP port to connect to
:type smtpport: int
.. method:: send(to, subject, body, cc=None, attachs=())
Send email to the given recipients. Emits the :signal:`mail_sent` signal.
@ -72,16 +81,69 @@ uses `Twisted non-blocking IO`_, like the rest of the framework.
:type attachs: iterable
MailSender settings
===================
.. _topics-email-settings:
Mail settings
=============
These settings define the default constructor values of the :class:`MailSender`
class, and can be used to configure e-mail notifications in your project without
writing any code (for those extensions that use the :class:`MailSender` class):
writing any code (for those extensions and code that uses :class:`MailSender`).
* :setting:`MAIL_DEBUG`
* :setting:`MAIL_FROM`
* :setting:`MAIL_HOST`
.. setting:: MAIL_FROM
MAIL_FROM
---------
Default: ``'scrapy@localhost'``
Sender email to use (``From:`` header) for sending emails.
.. setting:: MAIL_HOST
MAIL_HOST
---------
Default: ``'localhost'``
SMTP host to use for sending emails.
.. setting:: MAIL_PORT
MAIL_PORT
---------
Default: ``25``
SMTP port to use for sending emails.
.. setting:: MAIL_USER
MAIL_USER
---------
Default: ``None``
User to use for SMTP authentication. If disabled no SMTP authentication will be
performed.
.. setting:: MAIL_PASS
MAIL_PASS
---------
Default: ``None``
Password to use for SMTP authentication, along with :setting:`MAIL_USER`.
.. setting:: MAIL_DEBUG
MAIL_DEBUG
----------
Default: ``False``
Whether to enable the debugging mode.
Mail signals

View File

@ -615,36 +615,6 @@ If ``True``, all standard output (and error) of your process will be redirected
to the log. For example if you ``print 'hello'`` it will appear in the Scrapy
log.
.. setting:: MAIL_DEBUG
MAIL_DEBUG
----------
Default: ``False``
Whether to enable the debugging mode in the the :ref:`Scrapy e-mail sending
facility <topics-email>`.
.. setting:: MAIL_FROM
MAIL_FROM
---------
Default: ``'scrapy@localhost'``
Email to use as sender address for sending emails using the :ref:`Scrapy e-mail
sending facility <topics-email>`.
.. setting:: MAIL_HOST
MAIL_HOST
---------
Default: ``'localhost'``
Host to use for sending emails using the :ref:`Scrapy e-mail sending facility
<topics-email>`.
.. setting:: MEMDEBUG_ENABLED
MEMDEBUG_ENABLED

View File

@ -138,7 +138,10 @@ LOG_FILE = None
MAIL_DEBUG = False
MAIL_HOST = 'localhost'
MAIL_PORT = 25
MAIL_FROM = 'scrapy@localhost'
MAIL_PASS = None
MAIL_USER = None
MEMDEBUG_ENABLED = False # enable memory debugging
MEMDEBUG_NOTIFY = [] # send memory debugging report by mail at engine shutdown

View File

@ -12,7 +12,7 @@ from email.Utils import COMMASPACE, formatdate
from email import Encoders
from twisted.internet import defer, reactor
from twisted.mail.smtp import SMTPSenderFactory
from twisted.mail.smtp import ESMTPSenderFactory
from scrapy import log
from scrapy.core.exceptions import NotConfigured
@ -27,9 +27,13 @@ mail_sent = object()
class MailSender(object):
def __init__(self, smtphost=None, mailfrom=None):
self.smtphost = smtphost if smtphost else settings['MAIL_HOST']
self.mailfrom = mailfrom if mailfrom else settings['MAIL_FROM']
def __init__(self, smtphost=None, mailfrom=None, smtpuser=None, smtppass=None, \
smtpport=None):
self.smtphost = smtphost or settings['MAIL_HOST']
self.smtpport = smtpport or settings.getint('MAIL_PORT')
self.smtpuser = smtpuser or settings['MAIL_USER']
self.smtppass = smtppass or settings['MAIL_PASS']
self.mailfrom = mailfrom or settings['MAIL_FROM']
if not self.smtphost or not self.mailfrom:
raise NotConfigured("MAIL_HOST and MAIL_FROM settings are required")
@ -68,11 +72,12 @@ class MailSender(object):
(to, cc, subject, len(attachs)), level=log.DEBUG)
return
dfd = self._sendmail(self.smtphost, self.mailfrom, rcpts, msg.as_string())
dfd = self._sendmail(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)
return dfd
def _sent_ok(self, result, to, cc, subject, nattachs):
log.msg('Mail sent OK: To=%s Cc=%s Subject="%s" Attachs=%d' % \
@ -83,13 +88,12 @@ class MailSender(object):
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
instantiates a quiet (noisy=False) SMTPSenderFactory """
def _sendmail(self, to_addrs, msg):
msg = StringIO(msg)
d = defer.Deferred()
factory = SMTPSenderFactory(from_addr, to_addrs, msg, d)
factory = ESMTPSenderFactory(self.smtpuser, self.smtppass, self.mailfrom, \
to_addrs, msg, d, heloFallback=True, requireAuthentication=False, \
requireTransportSecurity=False)
factory.noisy = False
reactor.connectTCP(smtphost, port, factory)
reactor.connectTCP(self.smtphost, self.smtpport, factory)
return d