diff --git a/docs/topics/email.rst b/docs/topics/email.rst index 789fbd4fb75cd118454bdf1482e3c19b0464b112..96487d865d8560129be4bd91a10a9b0b74146162 100644 --- a/docs/topics/email.rst +++ b/docs/topics/email.rst @@ -76,7 +76,7 @@ uses `Twisted non-blocking IO`_, like the rest of the framework. :param settings: the e-mail recipients :type settings: :class:`scrapy.settings.Settings` object - .. method:: send(to, subject, body, cc=None, attachs=(), mimetype='text/plain') + .. method:: send(to, subject, body, cc=None, attachs=(), mimetype='text/plain', charset=None) Send email to the given recipients. @@ -102,6 +102,9 @@ uses `Twisted non-blocking IO`_, like the rest of the framework. :param mimetype: the MIME type of the e-mail :type mimetype: str + :param charset: the character encoding to use for the e-mail contents + :type charset: str + .. _topics-email-settings: diff --git a/scrapy/mail.py b/scrapy/mail.py index ad8ecbe133c03b96e683556fe14f36ee5cdb8088..c6339f25b3fa9dcb0464a14634a43be0b41a1091 100644 --- a/scrapy/mail.py +++ b/scrapy/mail.py @@ -43,7 +43,7 @@ class MailSender(object): settings['MAIL_PASS'], settings.getint('MAIL_PORT'), settings.getbool('MAIL_TLS'), settings.getbool('MAIL_SSL')) - def send(self, to, subject, body, cc=None, attachs=(), mimetype='text/plain', _callback=None): + def send(self, to, subject, body, cc=None, attachs=(), mimetype='text/plain', charset=None, _callback=None): if attachs: msg = MIMEMultipart() else: @@ -57,8 +57,11 @@ class MailSender(object): rcpts.extend(cc) msg['Cc'] = COMMASPACE.join(cc) + if charset: + msg.set_charset(charset) + if attachs: - msg.attach(MIMEText(body)) + msg.attach(MIMEText(body, 'plain', charset or 'us-ascii')) for attach_name, mimetype, f in attachs: part = MIMEBase(*mimetype.split('/')) part.set_payload(f.read()) diff --git a/tests/test_mail.py b/tests/test_mail.py index 25dd35099d3ca10e5f63d7a0b180e75302f5c78d..bd7e4962195242e1bf7843e67c4dacef44d541d1 100644 --- a/tests/test_mail.py +++ b/tests/test_mail.py @@ -1,5 +1,8 @@ +# coding=utf-8 + import unittest from io import BytesIO +from email.charset import Charset from scrapy.mail import MailSender @@ -54,11 +57,58 @@ class MailSenderTest(unittest.TestCase): text, attach = payload self.assertEqual(text.get_payload(decode=True), b'body') + self.assertEqual(text.get_charset(), Charset('us-ascii')) self.assertEqual(attach.get_payload(decode=True), b'content') def _catch_mail_sent(self, **kwargs): self.catched_msg = dict(**kwargs) + def test_send_utf8(self): + subject = u'sübjèçt' + body = u'bödÿ-àéïöñß' + mailsender = MailSender(debug=True) + mailsender.send(to=['test@scrapy.org'], subject=subject, body=body, + charset='utf-8', _callback=self._catch_mail_sent) + + assert self.catched_msg + self.assertEqual(self.catched_msg['subject'], subject) + self.assertEqual(self.catched_msg['body'], body) + + msg = self.catched_msg['msg'] + self.assertEqual(msg['subject'], subject) + self.assertEqual(msg.get_payload(), body) + self.assertEqual(msg.get_charset(), Charset('utf-8')) + self.assertEqual(msg.get('Content-Type'), 'text/plain; charset="utf-8"') + + def test_send_attach_utf8(self): + subject = u'sübjèçt' + body = u'bödÿ-àéïöñß' + attach = BytesIO() + attach.write(body.encode('utf-8')) + attach.seek(0) + attachs = [('attachment', 'text/plain', attach)] + + mailsender = MailSender(debug=True) + mailsender.send(to=['test@scrapy.org'], subject=subject, body=body, + attachs=attachs, charset='utf-8', _callback=self._catch_mail_sent) + + assert self.catched_msg + self.assertEqual(self.catched_msg['subject'], subject) + self.assertEqual(self.catched_msg['body'], body) + + msg = self.catched_msg['msg'] + self.assertEqual(msg['subject'], subject) + self.assertEqual(msg.get_charset(), Charset('utf-8')) + self.assertEqual(msg.get('Content-Type'), 'multipart/mixed; charset="utf-8"') + + payload = msg.get_payload() + assert isinstance(payload, list) + self.assertEqual(len(payload), 2) + + text, attach = payload + self.assertEqual(text.get_payload(decode=True).decode('utf-8'), body) + self.assertEqual(text.get_charset(), Charset('utf-8')) + self.assertEqual(attach.get_payload(decode=True).decode('utf-8'), body) if __name__ == "__main__": unittest.main()