Search code examples
pythonemailsendmailsmtplib

Python server.sendmail() returns {}. Email not sent


I'm having a file.txt file which has many lines. I want that any one line is chosen at random and emailed. This is looped thrice.

import smtplib,datetime,random

def mail(em):
    #print "yo"
    fromAddress = "[email protected]"
    toAddress = "[email protected]"
    subject = "WOW " + str(datetime.datetime.now())
    #print subject
    msg = em
    server=smtplib.SMTP("smtp.gmail.com", 587)
    server.starttls()
    password = "mypassword"
    server.login(fromAddress, password)
    server.sendmail(fromAddress, toAddress, msg)
    #print server.sendmail(fromAddress, toAddress, msg)


for i in range(0,3):
    em=str(random.choice(list(open('file.txt'))))
    mail(em)

This code does not work. The login and everything is fine. The only problem I notice is that server.sendmail(fromAddress, toAddress, msg) returns an empty {} What could be the possible fix to this? Whats the problem in first place? I have run the mail() function in the terminal and that seems to work fine. All the files are hosted here


Solution

  • First, as the docs say:

    If this method does not raise an exception, it returns a dictionary, with one entry for each recipient that was refused.

    So, the fact that you're getting back {} isn't a problem; it means that you had no errors.


    Second, what you're sending is not a valid message. A message must contain headers, then a blank line, then the body. And the headers should contain at least From: and To:.

    The smtplib library doesn't do this for you; as explained in the example:

    Note that the headers to be included with the message must be included in the message as entered; this example doesn’t do any processing of the RFC 822 headers. In particular, the ‘To’ and ‘From’ addresses must be included in the message headers explicitly.

    ...

    Note: In general, you will want to use the email package’s features to construct an email message, which you can then convert to a string and send via sendmail(); see email: Examples.

    Or, in the sendmail docs:

    Note: The from_addr and to_addrs parameters are used to construct the message envelope used by the transport agents. The SMTP does not modify the message headers in any way.

    In this case (as in the example), it's simple enough to just craft the message manually:

    msg = "From: {}\r\nTo: {}\r\n\r\n{}\r\n".format(fromAddress, toAddress, em)
    

    Finally, you really should call server.quit() at the end of the mail function. While (at least in CPython) the server object will be destroyed as soon as the function ends, unlike many other objects in Python, SMTP objects do not call close when destroyed, much less quit. So, from the server's point of view, you're suddenly dropping connection. While the RFCs suggest that a server should work just fine without a QUIT, and most of them do, they also recommend that the client should always send it and wait for a response, so… why not do it?