Flask-MailでUTF-8の日本語メールが送れない問題の回避方法(Python3)



## 先に結論を書くと

Flask-Mailが勝手にutf-8でメール送るときのグローバル設定をいじっているので、インポートしたあと、もとに戻してやると動く。
具体的には、これを送信前のどこかに書く。

```test.py
from email import charset
charset.add_charset('utf-8', charset.SHORTEST, charset.BASE64, 'utf-8')
```

それか、日本限定ならば、charsetをshift_jisに明示的に指定すれば上記みたいなことしなくても問題なく送れる。

```test.py
msg = Message(sender=(u"送信者", "from@example.com"),
                  subject = u"表題",
                  recipients = ["test@test.com"],
                  reply_to = u"返信先 <somebody@example.com>",
                  charset = 'shift_jis')  # ← これね
msg.body = u"試験だよflask_sjis"
mail.send(msg)
```

以上です。以下は捕捉です。


## Flask-Mailって何よ

Flask には Flask-Mailという超絶便利なエクステンションがある。
smtplibやemailで充分簡単ではあるけど、Webサービスを作る上でよく使うメールが異常に簡単に送れる。

これを見れば分かってもらえるかと。

http://pythonhosted.org/flask-mail/

特に、最近の要件だと、multipart/altnative なメルマガ（HTMLメールとテキストメール両対応のやつ）を送ることがよくあるけど、そんなのも異常に簡単に送れる。

```test.py
from flask import Flask
from flaskext.mail import Mail, Message

app = Flask(__name__)
mail = Mail(app)
msg = Message(subject, sender=sender, recipients=reciepients)
msg.body='わーい'
msg.html='<h1>わーい</h1>'
mail.send(msg)
```

ヤバイ。

## 起きている問題

実は、Python2だと脳停止でご機嫌に動くけど、Python3で実行すると大抵こうなる。意味分かんないし完全にやる気を無くす。

Flask良いらしいから触るね！ -> いいね！ 実業務に投入！ -> メール送信？ Flask-Mailが便利そう！ -> ゲエエ！！！！！ 詰んだ！！！ みたいな流れの人居るんじゃないかしら。俺です。こんにちは！！！


実験コードはこちら

``` flask_urf8.py
# -*- coding: utf-8 -*-
from flask import Flask
from flask_mail import Message, Mail

app = Flask(__name__)

with app.app_context():
    mail = Mail(app)
    msg = Message(sender=(u"送信者", "from@example.com"),
                  subject=u"表題",
                  recipients=["test@test.com"],
                  reply_to=u"返信先 <somebody@example.com>")
    msg.body = u"あflask_utf8"
    mail.send(msg)
```


結果

```
Traceback (most recent call last):
  File "flask_utf8.py", line 14, in <module>
    mail.send(msg)
  File "/Users/yasunori/venvs/sai/lib/python3.3/site-packages/flask_mail.py", line 416, in send
    message.send(connection)
  File "/Users/yasunori/venvs/sai/lib/python3.3/site-packages/flask_mail.py", line 351, in send
    connection.send(self)
  File "/Users/yasunori/venvs/sai/lib/python3.3/site-packages/flask_mail.py", line 168, in send
    message.as_string())
  File "/usr/local/Cellar/python3/3.3.3/Frameworks/Python.framework/Versions/3.3/lib/python3.3/smtplib.py", line 746, in sendmail
    msg = _fix_eols(msg).encode('ascii')
UnicodeEncodeError: 'ascii' codec can't encode character '\u3042' in position 364: ordinal not in range(128)
```

## 何で？


で、何だろうってFlask-Mailのコード読んでたんだけど、

``` flask_mail.py
charset.add_charset('utf-8', charset.SHORTEST, None, 'utf-8')
```

この記述がある。メールに使う、グローバルの（！）文字コード設定を勝手に（！）上書き変更してる。

で、前調べた件だけど、Python3だと、今のところ、この設定だと動かない。
http://qiita.com/yasunori/items/265d8db746742bb967c4#2-10

なので、Flask-Mailをimportしたあとで、さらに上書きして元に戻すと動きました。

``` バカー！！！.py
from email import charset
charset.add_charset('utf-8', charset.SHORTEST, charset.BASE64, 'utf-8')
```

## っていうか何勝手に設定変えてんの？

Flask-Mailのissue読んでたら、
「BODYにBASE64使うとスパムフィルタに引っかかりやすいのねん、だからNoneにすると良いと思うのねん。Djangoでもそうやってるのねん」「まじで！？ サンキュー！」とか言っていつからかしれっとこの1行が入っていた。

ちなみに、当のDjangoは、「グローバルを上書きしたのはどうかしてた」ということで、現在は上書きしないように変更されている。一瞬の隙をFlask-Mailが取り込んだっぽい。

## 最後に

もっと根本的な解決方法があったら誰か教えてください。

おしまい。