This code works and sends me an email just fine:

import smtplib #SERVER = "localhost" FROM = '' TO = [""] # must be a list SUBJECT = "Hello!" TEXT = "This message was sent with Python's smtplib." # Prepare actual message message = """\ From: %s To: %s Subject: %s %s """ % (FROM, ", ".join(TO), SUBJECT, TEXT) # Send the mail server = smtplib.SMTP('myserver') server.sendmail(FROM, TO, message) server.quit() 

However if I try to wrap it in a function like this:

def sendMail(FROM,TO,SUBJECT,TEXT,SERVER): import smtplib """this is some test documentation in the function""" message = """\ From: %s To: %s Subject: %s %s """ % (FROM, ", ".join(TO), SUBJECT, TEXT) # Send the mail server = smtplib.SMTP(SERVER) server.sendmail(FROM, TO, message) server.quit() 

and call it I get the following errors:

 Traceback (most recent call last): File "C:/Python31/mailtest1.py", line 8, in <module> sendmail.sendMail(sender,recipients,subject,body,server) File "C:/Python31\sendmail.py", line 13, in sendMail server.sendmail(FROM, TO, message) File "C:\Python31\lib\smtplib.py", line 720, in sendmail self.rset() File "C:\Python31\lib\smtplib.py", line 444, in rset return self.docmd("rset") File "C:\Python31\lib\smtplib.py", line 368, in docmd return self.getreply() File "C:\Python31\lib\smtplib.py", line 345, in getreply raise SMTPServerDisconnected("Connection unexpectedly closed") smtplib.SMTPServerDisconnected: Connection unexpectedly closed 

Can anyone help me understand why?

6

18 Answers

I recommend that you use the standard packages email and smtplib together to send email. Please look at the following example (reproduced from the Python documentation). Notice that if you follow this approach, the "simple" task is indeed simple, and the more complex tasks (like attaching binary objects or sending plain/HTML multipart messages) are accomplished very rapidly.

# Import smtplib for the actual sending function import smtplib # Import the email modules we'll need from email.mime.text import MIMEText # Open a plain text file for reading. For this example, assume that # the text file contains only ASCII characters. with open(textfile, 'rb') as fp: # Create a text/plain message msg = MIMEText(fp.read()) # me == the sender's email address # you == the recipient's email address msg['Subject'] = 'The contents of %s' % textfile msg['From'] = me msg['To'] = you # Send the message via our own SMTP server, but don't include the # envelope header. s = smtplib.SMTP('localhost') s.sendmail(me, [you], msg.as_string()) s.quit() 

For sending email to multiple destinations, you can also follow the example in the Python documentation:

# Import smtplib for the actual sending function import smtplib # Here are the email package modules we'll need from email.mime.image import MIMEImage from email.mime.multipart import MIMEMultipart # Create the container (outer) email message. msg = MIMEMultipart() msg['Subject'] = 'Our family reunion' # me == the sender's email address # family = the list of all recipients' email addresses msg['From'] = me msg['To'] = ', '.join(family) msg.preamble = 'Our family reunion' # Assume we know that the image files are all in PNG format for file in pngfiles: # Open the files in binary mode. Let the MIMEImage class automatically # guess the specific image type. with open(file, 'rb') as fp: img = MIMEImage(fp.read()) msg.attach(img) # Send the email via our own SMTP server. s = smtplib.SMTP('localhost') s.sendmail(me, family, msg.as_string()) s.quit() 

As you can see, the header To in the MIMEText object must be a string consisting of email addresses separated by commas. On the other hand, the second argument to the sendmail function must be a list of strings (each string is an email address).

So, if you have three email addresses: , , and , you can do as follows (obvious sections omitted):

to = ["", "", ""] msg['To'] = ",".join(to) s.sendmail(me, to, msg.as_string()) 

the ",".join(to) part makes a single string out of the list, separated by commas.

From your questions I gather that you have not gone through the Python tutorial - it is a MUST if you want to get anywhere in Python - the documentation is mostly excellent for the standard library.

10

When I need to mail in Python, I use the mailgun API which gets a lot of the headaches with sending mails sorted out. They have a wonderful app/api that allows you to send 5,000 free emails per month.

Sending an email would be like this:

def send_simple_message(): return requests.post( "", auth=("api", "YOUR_API_KEY"), data={"from": "Excited User <mailgun@YOUR_DOMAIN_NAME>", "to": ["", "YOU@YOUR_DOMAIN_NAME"], "subject": "Hello", "text": "Testing some Mailgun awesomness!"}) 

You can also track events and lots more, see the quickstart guide.

9

I'd like to help you with sending emails by advising the yagmail package (I'm the maintainer, sorry for the advertising, but I feel it can really help!).

The whole code for you would be:

import yagmail yag = yagmail.SMTP(FROM, 'pass') yag.send(TO, SUBJECT, TEXT) 

Note that I provide defaults for all arguments, for example if you want to send to yourself, you can omit TO, if you don't want a subject, you can omit it also.

Furthermore, the goal is also to make it really easy to attach html code or images (and other files).

Where you put contents you can do something like:

contents = ['Body text, and here is an embedded image:', ' 'You can also find an audio file attached.', '/local/path/song.mp3'] 

Wow, how easy it is to send attachments! This would take like 20 lines without yagmail ;)

Also, if you set it up once, you'll never have to enter the password again (and have it safely stored). In your case you can do something like:

import yagmail yagmail.SMTP().send(contents = contents) 

which is much more concise!

I'd invite you to have a look at the github or install it directly with pip install yagmail.

2

There is indentation problem. The code below will work:

import textwrap def sendMail(FROM,TO,SUBJECT,TEXT,SERVER): import smtplib """this is some test documentation in the function""" message = textwrap.dedent("""\ From: %s To: %s Subject: %s %s """ % (FROM, ", ".join(TO), SUBJECT, TEXT)) # Send the mail server = smtplib.SMTP(SERVER) server.sendmail(FROM, TO, message) server.quit() 

2

Here is an example on Python 3.x, much simpler than 2.x:

import smtplib from email.message import EmailMessage def send_mail(to_email, subject, message, server='smtp.example.cn', from_email=''): # import smtplib msg = EmailMessage() msg['Subject'] = subject msg['From'] = from_email msg['To'] = ', '.join(to_email) msg.set_content(message) print(msg) server = smtplib.SMTP(server) server.set_debuglevel(1) server.login(from_email, 'password') # user & password server.send_message(msg) server.quit() print('successfully sent the mail.') 

call this function:

send_mail(to_email=['', ''], subject='hello', message='Your analysis has done!') 

below may only for Chinese user:

If you use 126/163, 网易邮箱, you need to set"客户端授权密码", like below:

enter image description here

ref:

0

While indenting your code in the function (which is ok), you did also indent the lines of the raw message string. But leading white space implies folding (concatenation) of the header lines, as described in sections 2.2.3 and 3.2.3 of RFC 2822 - Internet Message Format:

Each header field is logically a single line of characters comprising the field name, the colon, and the field body. For convenience however, and to deal with the 998/78 character limitations per line, the field body portion of a header field can be split into a multiple line representation; this is called "folding".

In the function form of your sendmail call, all lines are starting with white space and so are "unfolded" (concatenated) and you are trying to send

From: To: Subject: Hello! This message was sent with Python's smtplib. 

Other than our mind suggests, smtplib will not understand the To: and Subject: headers any longer, because these names are only recognized at the beginning of a line. Instead smtplib will assume a very long sender email address:

 To: Subject: Hello! This message was sent with Python's smtplib. 

This won't work and so comes your Exception.

The solution is simple: Just preserve the message string as it was before. This can be done by a function (as Zeeshan suggested) or right away in the source code:

import smtplib def sendMail(FROM,TO,SUBJECT,TEXT,SERVER): """this is some test documentation in the function""" message = """\ From: %s To: %s Subject: %s %s """ % (FROM, ", ".join(TO), SUBJECT, TEXT) # Send the mail server = smtplib.SMTP(SERVER) server.sendmail(FROM, TO, message) server.quit() 

Now the unfolding does not occur and you send

From: To: Subject: Hello! This message was sent with Python's smtplib. 

which is what works and what was done by your old code.

Note that I was also preserving the empty line between headers and body to accommodate section 3.5 of the RFC (which is required) and put the include outside the function according to the Python style guide PEP-0008 (which is optional).

Make sure you have granted permission for both Sender and Receiver to send email and receive email from Unknown sources(External Sources) in Email Account.

import smtplib #Ports 465 and 587 are intended for email client to email server communication - sending email server = smtplib.SMTP('smtp.gmail.com', 587) #starttls() is a way to take an existing insecure connection and upgrade it to a secure connection using SSL/TLS. server.starttls() #Next, log in to the server server.login("#email", "#password") msg = "Hello! This Message was sent by the help of Python" #Send the mail server.sendmail("#Sender", "#Reciever", msg) 

enter image description here

2

It's probably putting tabs into your message. Print out message before you pass it to sendMail.

Thought I'd put in my two bits here since I have just figured out how this works.

It appears that you don't have the port specified on your SERVER connection settings, this effected me a little bit when I was trying to connect to my SMTP server that isn't using the default port: 25.

According to the smtplib.SMTP docs, your ehlo or helo request/response should automatically be taken care of, so you shouldn't have to worry about this (but might be something to confirm if all else fails).

Another thing to ask yourself is have you allowed SMTP connections on your SMTP server itself? For some sites like GMAIL and ZOHO you have to actually go in and activate the IMAP connections within the email account. Your mail server might not allow SMTP connections that don't come from 'localhost' perhaps? Something to look into.

The final thing is you might want to try and initiate the connection on TLS. Most servers now require this type of authentication.

You'll see I've jammed two TO fields into my email. The msg['TO'] and msg['FROM'] msg dictionary items allows the correct information to show up in the headers of the email itself, which one sees on the receiving end of the email in the To/From fields (you might even be able to add a Reply To field in here. The TO and FROM fields themselves are what the server requires. I know I've heard of some email servers rejecting emails if they don't have the proper email headers in place.

This is the code I've used, in a function, that works for me to email the content of a *.txt file using my local computer and a remote SMTP server (ZOHO as shown):

def emailResults(folder, filename): # body of the message doc = folder + filename + '.txt' with open(doc, 'r') as readText: msg = MIMEText(readText.read()) # headers TO = '' msg['To'] = TO FROM = '' msg['From'] = FROM msg['Subject'] = 'email subject |' + filename # SMTP send = smtplib.SMTP('smtp.zoho.com', 587) send.starttls() send.login('', 'password') send.sendmail(FROM, TO, msg.as_string()) send.quit() 

Another implementation using gmail let's say:

import smtplib def send_email(email_address: str, subject: str, body: str): """ send_email sends an email to the email address specified in the argument. Parameters ---------- email_address: email address of the recipient subject: subject of the email body: body of the email """ server = smtplib.SMTP('smtp.gmail.com', 587) server.starttls() server.login("email_address", "password") server.sendmail("email_address", email_address, "Subject: {}\n\n{}".format(subject, body)) server.quit() 

It's worth noting that the SMTP module supports the context manager so there is no need to manually call quit(), this will guarantee it is always called even if there is an exception.

 with smtplib.SMTP_SSL('smtp.gmail.com', 465) as server: server.ehlo() server.login(user, password) server.sendmail(from, to, body) 
import smtplib, ssl port = 587 # For starttls smtp_server = "smtp.office365.com" sender_email = "" receiver_email = "" password = "12345678" message = """\ Subject: Final exam Teacher when is the final exam?""" def SendMailf(): context = ssl.create_default_context() with smtplib.SMTP(smtp_server, port) as server: server.ehlo() # Can be omitted server.starttls(context=context) server.ehlo() # Can be omitted server.login(sender_email, password) server.sendmail(sender_email, receiver_email, message) print("mail send") 

I haven't been satisfied with the package options for sending emails and I decided to make and open source my own email sender. It is easy to use and capable of advanced use cases.

To install:

pip install redmail 

Usage:

from redmail import EmailSender email = EmailSender( host="<SMTP HOST ADDRESS>", port=<PORT NUMBER>, ) email.send( sender="", receivers=[""], subject="An example email", text="Hi, this is text body.", html="<h1>Hi,</h1><p>this is HTML body</p>" ) 

If your server requires a user and a password, just pass user_name and password to the EmailSender.

I have included a lot of features wrapped in the send method:

  • Include attachments
  • Include images directly to the HTML body
  • Jinja templating
  • Prettier HTML tables out of the box

Documentation:

Source code:

After a lot of fiddling with the examples e.g here this now works for me:

import smtplib from email.mime.text import MIMEText # SMTP sendmail server mail relay host = 'mail.server.com' port = 587 # starttls not SSL 465 e.g gmail, port 25 blocked by most ISPs & AWS sender_email = '' recipient_email = '' password = 'YourSMTPServerAuthenticationPass' subject = "Server - " body = "Message from server" def sendemail(host, port, sender_email, recipient_email, password, subject, body): try: p1 = f'<p><HR><BR>{recipient_email}<BR>' p2 = f'<h2><font color="green">{subject}</font></h2>' p3 = f'<p>{body}' p4 = f'<p>Kind Regards,<BR><BR>{sender_email}<BR><HR>' message = MIMEText((p1+p2+p3+p4), 'html') # servers may not accept non RFC 5321 / RFC 5322 / compliant TXT & HTML typos message['From'] = f'Sender Name <{sender_email}>' message['To'] = f'Receiver Name <{recipient_email}>' message['Cc'] = f'Receiver2 Name <>' message['Subject'] = f'{subject}' msg = message.as_string() server = smtplib.SMTP(host, port) print("Connection Status: Connected") server.set_debuglevel(1) server.ehlo() server.starttls() server.ehlo() server.login(sender_email, password) print("Connection Status: Logged in") server.sendmail(sender_email, recipient_email, msg) print("Status: Email as HTML successfully sent") except Exception as e: print(e) print("Error: unable to send email") # Run sendemail(host, port, sender_email, recipient_email, password, subject, body) print("Status: Exit") 

I wrote a simple function send_email() for email sending with smtplib and email packages (link to my article). It additionally uses dotenv package to loads the sender email and password (please don't keep secrets in the code!). I was using Gmail for email service. The password was the App Password (here is Google docs on how to generate App Password).

import os import smtplib from email.message import EmailMessage from dotenv import load_dotenv _ = load_dotenv() def send_email(to, subject, message): try: email_address = os.environ.get("EMAIL_ADDRESS") email_password = os.environ.get("EMAIL_PASSWORD") if email_address is None or email_password is None: # no email address or password # something is not configured properly print("Did you set email address and password correctly?") return False # create email msg = EmailMessage() msg['Subject'] = subject msg['From'] = email_address msg['To'] = to msg.set_content(message) # send email with smtplib.SMTP_SSL('smtp.gmail.com', 465) as smtp: smtp.login(email_address, email_password) smtp.send_message(msg) return True except Exception as e: print("Problem during send email") print(str(e)) return False 

The above approach is OK for simple email sending. If you are looking for more advanced features, such as HTML content or attachments - it, of course, can be hand-coded, but I would recommend using existing packages, for example yagmail.

Gmail has a limit of 500 emails per day. For sending many emails per day please consider transactional email service providers, like Amazon SES, MailGun, MailJet, or SendGrid.

import smtplib s = smtplib.SMTP(your smtp server, smtp port) #SMTP session message = "Hii!!!" s.sendmail("sender", "Receiver", message) # sending the mail s.quit() # terminating the session 
1

As of June 2022, it is now necessary to create an app-password for gmail:

To help keep your account secure, starting May 30, 2022 , Google will no longer support the use of third-party apps or devices that only ask for your username and password for you.

Please refers to this answer

As far your code is concerned, there doesn't seem to be anything fundamentally wrong with it except that, it is unclear how you're actually calling that function. All I can think of is that when your server is not responding then you will get this SMTPServerDisconnected error. If you lookup the getreply() function in smtplib (excerpt below), you will get an idea.

def getreply(self): """Get a reply from the server. Returns a tuple consisting of: - server response code (e.g. '250', or such, if all goes well) Note: returns -1 if it can't read response code. - server response string corresponding to response code (multiline responses are converted to a single, multiline string). Raises SMTPServerDisconnected if end-of-file is reached. """ 

check an example at that also uses a function call to send an email, if that's what you're trying to do (DRY approach).