

flask_mail.Mailを用いて、Gmailを経由するメール送信をした。

まずアプリケーションのコンフィグを設定し、アプリケーションをMailに登録する。

[code]

    from flask_mail import Mail, Message
    
    app = Flask(__name__)
    
    app.config["MAIL_SERVER"] = os.environ.get("MAIL_SERVER")
    app.config["MAIL_PORT"] = os.environ.get("MAIL_PORT")
    app.config["MAIL_USE_TLS"] = os.environ.get("MAIL_USE_TLS")
    app.config["MAIL_USE_SSL"] = os.environ.get("MAIL_USE_SSL")
    app.config["MAIL_USERNAME"] = os.environ.get("MAIL_USERNAME")
    app.config["MAIL_PASSWORD"] = os.environ.get("MAIL_PASSWORD")
    app.config["MAIL_DEFAULT_SENDER"] = os.environ.get("MAIL_DEFAULT_SENDER")
    
    mail = Mail(app)
    
[/code]

ここで環境変数は次のように設定しておく。

なお`MAIL_PASSWORD`はGmailのアプリパスワードである。

[code]

    MAIL_SERVER=smtp.gmail.com
    MAIL_PORT=587
    MAIL_USE_TLS=True
    MAIL_USE_SSL=False
    MAIL_USERNAME=[myaddress@gmail.com]
    MAIL_PASSWORD=[ewmtzfkhzgbuxov]
    MAIL_DEFAULT_SENDER=Flaskbook <myaddress@gmail.com>
    
[/code]

次にflask_mail.Messageを用いてメールの送信を行う。

まず送信用の関数を作成する。

[code]

    def send_email(to, subject, template, **kwargs):
            # to: 送信先メールアドレス
            # subject: メールのタイトル的なもの
            # template: テンプレート
    
        msg = Message(subject, recipients=[to])
        msg.body = render_template(template + ".txt", **kwargs)
        msg.html = render_template(template + ".html", **kwargs)
    
        mail.send(msg)
    
[/code]

そしてフォームを作成し、メールを送信してみる。

[code]

    # email: 送信先メールアドレス
    send_email(
        email,
        "問い合わせありがとうございます。",
        "contact_mail",
        username=username,
        description=description,
    )
    
[/code]

すると、次のようなエラーが発生する。

[code]

    File "/Users/Desktop/Flask/apps/minimalapp/app.py", line 97, in contact_complete
    send_email(
    File "/Users/Desktop/Flask/apps/minimalapp/app.py", line 116, in send_email
    mail.send(msg)
    File "/Users/Desktop/Flask/venv/lib/python3.8/site-packages/flask_mail.py", line 491, in send
    with self.connect() as connection:
    File "/Users/Desktop/Flask/venv/lib/python3.8/site-packages/flask_mail.py", line 144, in __enter__
    self.host = self.configure_host()
    File "/Users/Desktop/Flask/venv/lib/python3.8/site-packages/flask_mail.py", line 156, in configure_host
    host = smtplib.SMTP_SSL(self.mail.server, self.mail.port)
    File "/opt/anaconda3/lib/python3.8/smtplib.py", line 1034, in __init__
    SMTP.__init__(self, host, port, local_hostname, timeout,
    File "/opt/anaconda3/lib/python3.8/smtplib.py", line 253, in __init__
    (code, msg) = self.connect(host, port)
    File "/opt/anaconda3/lib/python3.8/smtplib.py", line 339, in connect
    self.sock = self._get_socket(host, port, self.timeout)
    File "/opt/anaconda3/lib/python3.8/smtplib.py", line 1040, in _get_socket
    new_socket = socket.create_connection((host, port), timeout,
    File "/opt/anaconda3/lib/python3.8/socket.py", line 808, in create_connection
    raise err
    File "/opt/anaconda3/lib/python3.8/socket.py", line 796, in create_connection
    sock.connect(sa)
    
    ConnectionRefusedError: [Errno 61] Connection refused
    
[/code]

解決策をご教授お願いします。


CC BY-SA 4.0
2023-03-17T10:00:48.130
94223
2023-03-20T09:01:39.327
2023-03-17T10:06:25.183
26370
51540
question
python flask gmail
Gmailを経由したflask-mailが送信できないエラー


## 挙動の推定原因

環境変数の内容とスタックトレースを見る限り、GmailのSMTPサーバーへ接続する際に本来の意図と違い、SSLを利用した接続をしようとしています。

### 詳細

環境変数として、`MAIL_USE_SSL=False`と質問文に記載されたもの **そのまま**
を設定している場合、`os.environ`経由で取得する値は、`False`というbool値ではなく`"False"`という文字列となっています。

`if`などの真偽値判定において、空でない文字列である`"False"`は`True`として扱われます。  
結果として、本来`False`であって欲しいSSL利用が`True`になってしまい、誤った接続方法を採用しており接続失敗している可能性が高いです。

ref:
[Pythonドキュメンテーション](https://docs.python.org/ja/3/library/stdtypes.html#truth-
value-testing)

※サンプル

[code]

    >>> def demo(val):
    ...   if val:
    ...     print("TRUE")
    ...   else:
    ...     print("FALSE")
    ...
    >>> demo(True)
    TRUE
    >>> demo(False)
    FALSE
    >>> demo("False")
    TRUE
    
[/code]

## 解消方法の例

### 環境変数には、真偽判定に適した値を設定する

例えば、下記のような設定をすることで、`MAIL_USE_SSL`に空文字列を指定することが出来ます。  
空文字列は真偽判定において`False`扱いになるため、SSL接続の処理に進まなくなります。

[code]

    export MAIL_USE_SSL=""
    
[/code]

### 環境変数を適切に処理する

環境変数を変えない場合だと、下記のコード例のように「特定条件下のみ`True`と扱い残りは`False`とする」ように値を処理することでも実現が可能ではあります。

[code]

    # "True" という文字列が来たときのみTrue
    app.config["MAIL_USE_SSL"] = os.environ.get("MAIL_USE_SSL") == "True"
    
[/code]


CC BY-SA 4.0
2023-03-20T07:41:51.953
94254
2023-03-20T09:01:39.327
2023-03-20T09:01:39.327
3060
2784
94223
answer