Search code examples
python-3.xflaskflask-sqlalchemyflask-mail

flask mail error “SMTPServerDisconnected('please run connect() first')”


I am writing a little web application based on Miguel Grinberg's Flasky. I use the exact same code for user send reset password mail using gmail.

The following as my email.py file here i can implement mail sending function

def send_password_reset_email(user):

    token = user.get_reset_password_token()
    send_email(_('[Microblog] Reset Your Password'),
               sender=current_app.config['ADMINS'][0],
               recipients=[user.email],
               text_body=render_template('email/reset_password.txt',
                                         user=user, token=token),
               html_body=render_template('email/reset_password.html',
                                         user=user, token=token))

def send_async_email(app, msg):
    with app.app_context():
        mail.send(msg)

def send_email(subject, sender, recipients, text_body, html_body):
    msg = Message(subject, sender=sender, recipients=recipients)
    msg.body = text_body
    msg.html = html_body
    Thread(target=send_async_email,
           args=(current_app._get_current_object(), msg)).start()

In routes.py file im getting email from the user and if the user email match then i well send the token to the user via mail

@bp.route('/reset_password_request', methods=['GET', 'POST'])
def reset_password_request():
    if current_user.is_authenticated:
        return redirect(url_for('main.index'))
    form = ResetPasswordRequestForm()
    if form.validate_on_submit():
        user = User.query.filter_by(email=form.email.data).first()
        if user:
            send_password_reset_email(user)
        flash(
            _('Check your email for the instructions to reset your password'))
        return redirect(url_for('auth.login'))
    return render_template('auth/reset_password_request.html',
                           title=_('Reset Password'), form=form)


@bp.route('/reset_password/<token>', methods=['GET', 'POST'])
def reset_password(token):
    if current_user.is_authenticated:
        return redirect(url_for('main.index'))
    user = User.verify_reset_password_token(token)
    if not user:
        return redirect(url_for('main.index'))
    form = ResetPasswordForm()
    if form.validate_on_submit():
        user.set_password(form.password.data)
        db.session.commit()
        flash(_('Your password has been reset.'))
        return redirect(url_for('auth.login'))
    return render_template('auth/reset_password.html', form=form)

In model.py file in the user model i generate a token for a user and also check the user token

def get_reset_password_token(self, expires_in=600):
        return jwt.encode(
            {'reset_password': self.id, 'exp': time() + expires_in},
            current_app.config['SECRET_KEY'],
            algorithm='HS256').decode('utf-8')
        
    @staticmethod
    def varify_reset_password_token(token):
        try:
            id = jwt.decode(token, current_app.config['SECRET_KEY'],
                            algorithms=['HS256'])['reset_password']
        except:
            return
        return User.query.get(id)

my flask mail setup is as follows config.py file

    MAIL_SERVER   = os.environ.get('MAIL_SERVER')
    MAIL_PORT     = int(os.environ.get('MAIL_PORT') or 25)
    MAIL_USE_TLS  = os.environ.get('MAIL_USE_TLS') is not None
    MAIL_USERNAME = os.environ.get('MAIL_USERNAME')
    MAIL_PASSWORD = os.environ.get('MAIL_PASSWORD')
    ADMINS         =['[email protected]']

The following Error i get in the terminal

Traceback (most recent call last):

  File "c:\python38\lib\threading.py", line 932, in _bootstrap_inner
    self.run()
 
  File "c:\python38\lib\threading.py", line 870, in run     
    self._target(*self._args, **self._kwargs)
  File "C:\Users\Ijaz Bacha\project\microblog1\app\email.py", line 9, in send_async_email
    mail.send(msg)
  File "c:\users\ijaz bacha\project\microblog1\venv\lib\site-packages\flask_mail.py", line 492, in send
    message.send(connection)
  File "c:\users\ijaz bacha\project\microblog1\venv\lib\site-packages\flask_mail.py", line 152, in __exit__
    self.host.quit()
  File "c:\python38\lib\smtplib.py", line 988, in quit      
    res = self.docmd("quit")
  File "c:\python38\lib\smtplib.py", line 424, in docmd
    self.putcmd(cmd, args)
  File "c:\python38\lib\smtplib.py", line 371, in putcmd
    self.send(str)
  File "c:\python38\lib\smtplib.py", line 363, in send
    raise SMTPServerDisconnected('please run connect() first')
smtplib.SMTPServerDisconnected: please run connect() first

Solution

  • In my experience, a while ago I had a very similar issue that you you were having. After troubleshooting, I found out that my code worked when I would create a mail class, and call function like $mailclass.ehlo etc.

    Based on the error its having an issue connecting or staying connected. Try calling the connect methods in the function itself and close of the connection after each email.