Search code examples
pythonemailsmtpsmtplib

Using smtplib to send an email to mailtrap only works when code is not within function or class


this question is somehow similar to python: sending a mail, fails when inside a "with" block .

I'm using Python (3.6) to send emails to mailtrap smtp. Mailtrap actually provides you with the integration code for smtplib which is the one below:

import smtplib

sender = "Private Person <from@smtp.mailtrap.io>"
receiver = "A Test User <to@smtp.mailtrap.io>"

message = f"""\
Subject: Hi Mailtrap
To: {receiver}
From: {sender}

This is a test e-mail message."""

with smtplib.SMTP("smtp.mailtrap.io", 2525) as server:
    server.login("<MYUSER>", "<MYPASSWORD>")
    server.sendmail(sender, receiver, message)

The code above works just fine if I place it in a module and run it.I go to mailtrap inbox and verify that the email is there. However I want to encapsulate this in a function like this:


import smtplib
from socket import gaierror


def test():
    sender = "Test Dev <from@smtp.mailtrap.io>"
    receiver = "Test User <to@smtp.mailtrap.io>"
    message = f"""\
    Subject: Hi there
    To: {receiver}
    From: {sender}

    TESTING"""

    try:
        with smtplib.SMTP("smtp.mailtrap.io", 2525) as server:
            server.login("<MYUSER>", "<MYPASSWORD")
            print("Sending email")
            server.sendmail(sender, receiver, message)
        print('Sent')


    except (gaierror, ConnectionRefusedError):
        print('Failed to connect to the server. Bad connection settings?')
    except smtplib.SMTPServerDisconnected:
        print('Failed to connect to the server. Wrong user/password?')
    except smtplib.SMTPException as e:
        print('SMTP error occurred: ' + str(e))

if __name__ == "__main__":
    test()

This doesn't work. WHY? Here is the output: output image There's no connection error or any other exception. However I go to mailtrap and don't find the email there.

Is this a mailtrap issue or is it related to smtplib ? I'm cracking my head around this one


Solution

  • I was having this same issue and couldn't wrap my head around it. I did notice that when I made my message an empty string, it worked.

    After an embarrassingly long time of searching; I found this post which pointed me to the solution.

    You must set the MIME type of the email message. So rather than just passing a string you pass a message object:

    message = MIMEText("TEST!")
        message["Subject"] = "Alert!"
        message["From"] = sender
        message["To"] = receiver
    

    ... then eventually

    server.sendmail(sender, receiver, message.as_string())
    

    my full send email function looks like this:

        def send_mail(self):
        message = MIMEText("TEST!")
        message["Subject"] = "Alert!"
        message["From"] = sender
        message["To"] = receiver
        try:
            context = ssl.create_default_context()
    
            with smtplib.SMTP(smtp_server, port) as server:
                server.set_debuglevel(1)
                server.ehlo()  # Can be omitted
                server.starttls(context=context)
                server.ehlo()  # Can be omitted
                server.login(login, password)
                server.sendmail(sender, receiver, message.as_string())
            print('Sent')
        except (gaierror, ConnectionRefusedError):
            print('Failed to connect to the server. Bad connection settings?')
        except smtplib.SMTPServerDisconnected:
            print('Failed to connect to the server. Wrong user/password?')
        except smtplib.SMTPException as e:
            print('SMTP error occurred: ' + str(e))
        except Exception as e:
            print('everything else')
    

    It's unfortunate that you must specify the sender and receiver in both the message object and sendemail fuction.