mirror of https://github.com/scrapy/scrapy.git
refactored MailSender to get rid of scrapy.conf singleton, also removed ill-designed scrapy.mail.mail_sent signal
This commit is contained in:
parent
1e2efe5664
commit
7ef593c5c2
|
|
@ -32,6 +32,7 @@ Scrapy changes:
|
|||
- LogFormatter method now return dicts(instead of strings) to support lazy formatting (:issue:`164`, :commit:`dcef7b0`)
|
||||
- downloader handlers (:setting:`DOWNLOAD_HANDLERS` setting) now receive settings as the first argument of the constructor
|
||||
- replaced memory usage acounting with (more portable) `resource`_ module, removed ``scrapy.utils.memory`` module
|
||||
- removed signal: ``scrapy.mail.mail_sent``
|
||||
|
||||
Scrapyd changes:
|
||||
|
||||
|
|
|
|||
|
|
@ -20,11 +20,19 @@ simple API for sending attachments and it's very easy to configure, with a few
|
|||
Quick example
|
||||
=============
|
||||
|
||||
Here's a quick example of how to send an e-mail (without attachments)::
|
||||
There are two ways to instantiate the mail sender. You can instantiate it using
|
||||
the standard constructor::
|
||||
|
||||
from scrapy.mail import MailSender
|
||||
|
||||
mailer = MailSender()
|
||||
|
||||
Or you can instantiate it passing a Scrapy settings object, which will respect
|
||||
the :ref:`settings <topics-email-settings>`::
|
||||
|
||||
mailer = MailSender.from_settings(settings)
|
||||
|
||||
And here is how to use it to send an e-mail (without attachments)::
|
||||
|
||||
mailer.send(to=["someone@example.com"], subject="Some subject", body="Some body", cc=["another@example.com"])
|
||||
|
||||
MailSender class reference
|
||||
|
|
@ -33,7 +41,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=None, mailfrom=None, smtpuser=None, smtppass=None, smtpport=None):
|
||||
.. 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.
|
||||
|
|
@ -54,9 +62,17 @@ uses `Twisted non-blocking IO`_, like the rest of the framework.
|
|||
:param smtpport: the SMTP port to connect to
|
||||
:type smtpport: int
|
||||
|
||||
.. classmethod:: from_settings(settings)
|
||||
|
||||
Instantiate using a Scrapy settings object, which will respect
|
||||
:ref:`these Scrapy settings <topics-email-settings>`.
|
||||
|
||||
:param settings: the e-mail recipients
|
||||
:type settings: :class:`scrapy.settings.Settings` object
|
||||
|
||||
.. method:: send(to, subject, body, cc=None, attachs=())
|
||||
|
||||
Send email to the given recipients. Emits the :signal:`mail_sent` signal.
|
||||
Send email to the given recipients.
|
||||
|
||||
:param to: the e-mail recipients
|
||||
:type to: list
|
||||
|
|
@ -132,34 +148,3 @@ MAIL_PASS
|
|||
Default: ``None``
|
||||
|
||||
Password to use for SMTP authentication, along with :setting:`MAIL_USER`.
|
||||
|
||||
|
||||
Mail signals
|
||||
============
|
||||
|
||||
.. signal:: mail_sent
|
||||
.. function:: mail_sent(to, subject, body, cc, attachs, msg)
|
||||
|
||||
Emitted by :meth:`MailSender.send` after an email has been sent.
|
||||
|
||||
:param to: the e-mail recipients
|
||||
:type to: list
|
||||
|
||||
:param subject: the subject of the e-mail
|
||||
:type subject: str
|
||||
|
||||
:param cc: the e-mails to CC
|
||||
:type cc: list
|
||||
|
||||
:param body: the e-mail body
|
||||
:type body: str
|
||||
|
||||
:param attachs: an iterable of tuples ``(attach_name, mimetype,
|
||||
file_object)`` where ``attach_name`` is a string with the name that will
|
||||
appear on the e-mail's 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
|
||||
|
||||
:param msg: the generated message
|
||||
:type msg: ``MIMEMultipart`` or ``MIMENonMultipart``
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ class MemoryUsage(object):
|
|||
self.limit = crawler.settings.getint('MEMUSAGE_LIMIT_MB')*1024*1024
|
||||
self.warning = crawler.settings.getint('MEMUSAGE_WARNING_MB')*1024*1024
|
||||
self.report = crawler.settings.getbool('MEMUSAGE_REPORT')
|
||||
self.mail = MailSender()
|
||||
self.mail = MailSender.from_settings(crawler.settings)
|
||||
crawler.signals.connect(self.engine_started, signal=signals.engine_started)
|
||||
crawler.signals.connect(self.engine_stopped, signal=signals.engine_stopped)
|
||||
|
||||
|
|
|
|||
|
|
@ -10,23 +10,25 @@ from scrapy.exceptions import NotConfigured
|
|||
|
||||
class StatsMailer(object):
|
||||
|
||||
def __init__(self, stats, recipients):
|
||||
def __init__(self, stats, recipients, mail):
|
||||
self.stats = stats
|
||||
self.recipients = recipients
|
||||
self.mail = mail
|
||||
|
||||
@classmethod
|
||||
def from_crawler(cls, crawler):
|
||||
recipients = crawler.settings.getlist("STATSMAILER_RCPTS")
|
||||
if not recipients:
|
||||
raise NotConfigured
|
||||
o = cls(crawler.stats, recipients)
|
||||
crawler.connect(o.stats_spider_closed, signal=signals.stats_spider_closed)
|
||||
mail = MailSender.from_settings(crawler.settings)
|
||||
o = cls(crawler.stats, recipients, mail)
|
||||
crawler.signals.connect(o.spider_closed, signal=signals.spider_closed)
|
||||
return o
|
||||
|
||||
def stats_spider_closed(self, spider, spider_stats):
|
||||
mail = MailSender()
|
||||
def spider_closed(self, spider):
|
||||
spider_stats = self.stats.get_stats(spider)
|
||||
body = "Global stats\n\n"
|
||||
body += "\n".join("%-50s : %s" % i for i in self.stats.get_stats().items())
|
||||
body += "\n\n%s stats\n\n" % spider.name
|
||||
body += "\n".join("%-50s : %s" % i for i in spider_stats.items())
|
||||
mail.send(self.recipients, "Scrapy stats for: %s" % spider.name, body)
|
||||
return self.mail.send(self.recipients, "Scrapy stats for: %s" % spider.name, body)
|
||||
|
|
|
|||
|
|
@ -15,31 +15,24 @@ from twisted.internet import defer, reactor
|
|||
from twisted.mail.smtp import ESMTPSenderFactory
|
||||
|
||||
from scrapy import log
|
||||
from scrapy.exceptions import NotConfigured
|
||||
from scrapy.conf import settings
|
||||
|
||||
|
||||
# signal sent when message is sent
|
||||
# args: to, subject, body, cc, attach, msg
|
||||
mail_sent = object()
|
||||
|
||||
|
||||
class MailSender(object):
|
||||
|
||||
def __init__(self, smtphost=None, mailfrom=None, smtpuser=None, smtppass=None, \
|
||||
smtpport=None, debug=False, crawler=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']
|
||||
def __init__(self, smtphost='localhost', mailfrom='scrapy@localhost',
|
||||
smtpuser=None, smtppass=None, smtpport=25, debug=False):
|
||||
self.smtphost = smtphost
|
||||
self.smtpport = smtpport
|
||||
self.smtpuser = smtpuser
|
||||
self.smtppass = smtppass
|
||||
self.mailfrom = mailfrom
|
||||
self.debug = debug
|
||||
self.signals = crawler.signals if crawler else None
|
||||
|
||||
if not self.smtphost or not self.mailfrom:
|
||||
raise NotConfigured("MAIL_HOST and MAIL_FROM settings are required")
|
||||
@classmethod
|
||||
def from_settings(cls, settings):
|
||||
return cls(settings['MAIL_HOST'], settings['MAIL_FROM'], settings['MAIL_USER'],
|
||||
settings['MAIL_PASS'], settings.getint('MAIL_PORT'))
|
||||
|
||||
def send(self, to, subject, body, cc=None, attachs=()):
|
||||
def send(self, to, subject, body, cc=None, attachs=(), _callback=None):
|
||||
if attachs:
|
||||
msg = MIMEMultipart()
|
||||
else:
|
||||
|
|
@ -65,9 +58,8 @@ class MailSender(object):
|
|||
else:
|
||||
msg.set_payload(body)
|
||||
|
||||
if self.signals:
|
||||
self.signals.send_catch_log(signal=mail_sent, to=to, subject=subject, body=body,
|
||||
cc=cc, attach=attachs, msg=msg)
|
||||
if _callback:
|
||||
_callback(to=to, subject=subject, body=body, cc=cc, attach=attachs, msg=msg)
|
||||
|
||||
if self.debug:
|
||||
log.msg(format='Debug mail sent OK: To=%(mailto)s Cc=%(mailcc)s Subject="%(mailsubject)s" Attachs=%(mailattachs)d',
|
||||
|
|
|
|||
|
|
@ -1,23 +1,13 @@
|
|||
from cStringIO import StringIO
|
||||
import unittest
|
||||
|
||||
from scrapy.mail import MailSender, mail_sent
|
||||
from scrapy.utils.test import get_crawler
|
||||
|
||||
from scrapy.mail import MailSender
|
||||
|
||||
class MailSenderTest(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.catched_msg = None
|
||||
self.crawler = get_crawler()
|
||||
self.crawler.signals.connect(self._catch_mail_sent, signal=mail_sent)
|
||||
|
||||
def tearDown(self):
|
||||
self.crawler.signals.disconnect(self._catch_mail_sent, signal=mail_sent)
|
||||
|
||||
def test_send(self):
|
||||
mailsender = MailSender(debug=True, crawler=self.crawler)
|
||||
mailsender.send(to=['test@scrapy.org'], subject='subject', body='body')
|
||||
mailsender = MailSender(debug=True)
|
||||
mailsender.send(to=['test@scrapy.org'], subject='subject', body='body', _callback=self._catch_mail_sent)
|
||||
|
||||
assert self.catched_msg
|
||||
|
||||
|
|
@ -36,9 +26,9 @@ class MailSenderTest(unittest.TestCase):
|
|||
attach.seek(0)
|
||||
attachs = [('attachment', 'text/plain', attach)]
|
||||
|
||||
mailsender = MailSender(debug=True, crawler=self.crawler)
|
||||
mailsender = MailSender(debug=True)
|
||||
mailsender.send(to=['test@scrapy.org'], subject='subject', body='body',
|
||||
attachs=attachs)
|
||||
attachs=attachs, _callback=self._catch_mail_sent)
|
||||
|
||||
assert self.catched_msg
|
||||
self.assertEqual(self.catched_msg['to'], ['test@scrapy.org'])
|
||||
|
|
|
|||
Loading…
Reference in New Issue