[AIRFLOW-1914] Add other charset support to email utils
The built-in email utils does not support multibyte string content, for example, Japanese or emojis. The fix is to add mime_charset parameter to allow for other values such as `utf-8`. Closes #3308 from wolfier/AIRFLOW-1914
This commit is contained in:
Родитель
29ae02a070
Коммит
c0cf73d27b
|
@ -7,9 +7,9 @@
|
|||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
|
@ -39,6 +39,11 @@ class EmailOperator(BaseOperator):
|
|||
:type cc: list or string (comma or semicolon delimited)
|
||||
:param bcc: list of recipients to be added in BCC field
|
||||
:type bcc: list or string (comma or semicolon delimited)
|
||||
:param mime_subtype: MIME sub content type
|
||||
:type mime_subtype: string
|
||||
:param mime_charset: character set parameter added to the Content-Type
|
||||
header.
|
||||
:type mime_charset: string
|
||||
"""
|
||||
|
||||
template_fields = ('to', 'subject', 'html_content')
|
||||
|
@ -55,6 +60,7 @@ class EmailOperator(BaseOperator):
|
|||
cc=None,
|
||||
bcc=None,
|
||||
mime_subtype='mixed',
|
||||
mime_charset='us_ascii',
|
||||
*args, **kwargs):
|
||||
super(EmailOperator, self).__init__(*args, **kwargs)
|
||||
self.to = to
|
||||
|
@ -64,6 +70,9 @@ class EmailOperator(BaseOperator):
|
|||
self.cc = cc
|
||||
self.bcc = bcc
|
||||
self.mime_subtype = mime_subtype
|
||||
self.mime_charset = mime_charset
|
||||
|
||||
def execute(self, context):
|
||||
send_email(self.to, self.subject, self.html_content, files=self.files, cc=self.cc, bcc=self.bcc, mime_subtype=self.mime_subtype)
|
||||
send_email(self.to, self.subject, self.html_content,
|
||||
files=self.files, cc=self.cc, bcc=self.bcc,
|
||||
mime_subtype=self.mime_subtype, mine_charset=self.mime_charset)
|
||||
|
|
|
@ -7,9 +7,9 @@
|
|||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
|
@ -39,9 +39,9 @@ from airflow.exceptions import AirflowConfigException
|
|||
from airflow.utils.log.logging_mixin import LoggingMixin
|
||||
|
||||
|
||||
def send_email(to, subject, html_content, files=None,
|
||||
dryrun=False, cc=None, bcc=None,
|
||||
mime_subtype='mixed', **kwargs):
|
||||
def send_email(to, subject, html_content,
|
||||
files=None, dryrun=False, cc=None, bcc=None,
|
||||
mime_subtype='mixed', mime_charset='us-ascii', **kwargs):
|
||||
"""
|
||||
Send email using backend specified in EMAIL_BACKEND.
|
||||
"""
|
||||
|
@ -50,12 +50,13 @@ def send_email(to, subject, html_content, files=None,
|
|||
backend = getattr(module, attr)
|
||||
return backend(to, subject, html_content, files=files,
|
||||
dryrun=dryrun, cc=cc, bcc=bcc,
|
||||
mime_subtype=mime_subtype, **kwargs)
|
||||
mime_subtype=mime_subtype, mime_charset=mime_charset, **kwargs)
|
||||
|
||||
|
||||
def send_email_smtp(to, subject, html_content, files=None,
|
||||
dryrun=False, cc=None, bcc=None,
|
||||
mime_subtype='mixed', **kwargs):
|
||||
mime_subtype='mixed', mime_charset='us-ascii',
|
||||
**kwargs):
|
||||
"""
|
||||
Send an email with html content
|
||||
|
||||
|
@ -81,7 +82,7 @@ def send_email_smtp(to, subject, html_content, files=None,
|
|||
recipients = recipients + bcc
|
||||
|
||||
msg['Date'] = formatdate(localtime=True)
|
||||
mime_text = MIMEText(html_content, 'html')
|
||||
mime_text = MIMEText(html_content, 'html', mime_charset)
|
||||
msg.attach(mime_text)
|
||||
|
||||
for fname in files or []:
|
||||
|
|
|
@ -37,6 +37,7 @@ from datetime import timedelta
|
|||
from dateutil.relativedelta import relativedelta
|
||||
from email.mime.application import MIMEApplication
|
||||
from email.mime.multipart import MIMEMultipart
|
||||
from email.mime.text import MIMEText
|
||||
from freezegun import freeze_time
|
||||
from numpy.testing import assert_array_almost_equal
|
||||
from six.moves.urllib.parse import urlencode
|
||||
|
@ -2409,8 +2410,7 @@ class EmailTest(unittest.TestCase):
|
|||
utils.email.send_email('to', 'subject', 'content')
|
||||
send_email_test.assert_called_with(
|
||||
'to', 'subject', 'content', files=None, dryrun=False,
|
||||
cc=None, bcc=None, mime_subtype='mixed'
|
||||
)
|
||||
cc=None, bcc=None, mime_charset='us-ascii', mime_subtype='mixed')
|
||||
self.assertFalse(mock_send_email.called)
|
||||
|
||||
|
||||
|
@ -2432,11 +2432,20 @@ class EmailSmtpTest(unittest.TestCase):
|
|||
self.assertEqual('subject', msg['Subject'])
|
||||
self.assertEqual(configuration.conf.get('smtp', 'SMTP_MAIL_FROM'), msg['From'])
|
||||
self.assertEqual(2, len(msg.get_payload()))
|
||||
self.assertEqual(u'attachment; filename="' + os.path.basename(attachment.name) + '"',
|
||||
msg.get_payload()[-1].get(u'Content-Disposition'))
|
||||
filename = u'attachment; filename="' + os.path.basename(attachment.name) + '"'
|
||||
self.assertEqual(filename, msg.get_payload()[-1].get(u'Content-Disposition'))
|
||||
mimeapp = MIMEApplication('attachment')
|
||||
self.assertEqual(mimeapp.get_payload(), msg.get_payload()[-1].get_payload())
|
||||
|
||||
@mock.patch('airflow.utils.email.send_MIME_email')
|
||||
def test_send_smtp_with_multibyte_content(self, mock_send_mime):
|
||||
utils.email.send_email_smtp('to', 'subject', '🔥', mime_charset='utf-8')
|
||||
self.assertTrue(mock_send_mime.called)
|
||||
call_args = mock_send_mime.call_args[0]
|
||||
msg = call_args[2]
|
||||
mimetext = MIMEText('🔥', 'mixed', 'utf-8')
|
||||
self.assertEqual(mimetext.get_payload(), msg.get_payload()[0].get_payload())
|
||||
|
||||
@mock.patch('airflow.utils.email.send_MIME_email')
|
||||
def test_send_bcc_smtp(self, mock_send_mime):
|
||||
attachment = tempfile.NamedTemporaryFile()
|
||||
|
|
Загрузка…
Ссылка в новой задаче