Overview
This article explains how to send emails using Python’s standard libraries: smtplib and email.message. We will use the modern EmailMessage class, which is recommended for Python 3.6 and later, instead of the older and more complex MIMEText. We cover both plain text and HTML formats.
Specifications (Input/Output)
- Input:
- Sender address (From)
- Recipient address (To)
- SMTP server settings (Host, Port, Credentials)
- Email subject and body (Text or HTML)
- Output:
- Execution of email sending via SMTP server
- Success or failure logs
- Requirement: Access to an SMTP server (such as Gmail or a local test server).
Basic Usage
This is a simple example of sending a plain text email. Using the with statement ensures that the SMTP connection is closed automatically.
import smtplib
from email.message import EmailMessage
# Create the message
msg = EmailMessage()
msg["Subject"] = "Hello Python"
msg["From"] = "sender@example.com"
msg["To"] = "receiver@example.com"
msg.set_content("This is a test email sent from Python.")
# Sending the email (Change settings to match your environment)
# This example assumes a local test server at localhost:1025
with smtplib.SMTP("localhost", 1025) as smtp:
smtp.send_message(msg)
Full Code
This is a practical function that sends an HTML email. It also includes a plain text version for email clients that do not support HTML. This example uses Gmail’s SMTP server.
import smtplib
from email.message import EmailMessage
import ssl
def send_html_email():
"""
Demo function to send an HTML email.
Sets plain text as a fallback for non-HTML environments.
"""
# 1. Configuration (Recommended to use environment variables in production)
smtp_host = "smtp.gmail.com"
smtp_port = 587
my_email = "your_email@gmail.com"
my_password = "your_app_password" # Use an App Password for Gmail
to_email = "target_address@example.com"
# 2. Create EmailMessage object and set headers
msg = EmailMessage()
msg["Subject"] = "[Notification] HTML Email from Python"
msg["From"] = my_email
msg["To"] = to_email
# 3. Set the body (Multipart structure)
# First, set the plain text content for non-HTML environments
text_content = "Hello.\nThis is an HTML email.\nPlease view it in a compatible environment."
msg.set_content(text_content)
# Next, add the HTML body using add_alternative
# This ensures the HTML version is prioritized by the recipient's client
html_content = """
<html>
<body>
<h1 style="color: #2E86C1;">HTML Email Test</h1>
<p>This email uses Python's <b>EmailMessage</b> class.</p>
<p>Link: <a href="https://www.python.org">Python.org</a></p>
</body>
</html>
"""
msg.add_alternative(html_content, subtype="html")
# 4. Connect to SMTP server and send
try:
print("Connecting to SMTP server...")
with smtplib.SMTP(smtp_host, smtp_port) as smtp:
# Enable debug levels if you want to see the server communication
# smtp.set_debuglevel(1)
# Start encryption
context = ssl.create_default_context()
smtp.starttls(context=context)
# Login
smtp.login(my_email, my_password)
# Send
smtp.send_message(msg)
print("Email sent successfully.")
except smtplib.SMTPAuthenticationError:
print("Authentication Error: Incorrect email address or password.")
except Exception as e:
print(f"An error occurred: {e}")
if __name__ == "__main__":
send_html_email()
Customization Points
Creating and Operating EmailMessage Objects
These are the main methods of the email.message module.
| Syntax | Meaning / Process |
msg = EmailMessage() | Creates a new empty email message object. You can set headers (Subject, From, To) like a dictionary. |
msg.set_content(body) | Sets the main body of the email. By default, it is treated as plain text (text/plain). |
msg.add_alternative(body, subtype="html") | Adds an alternative version (like HTML) as a multipart message. If called after set_content, HTML-compatible clients will display this version. |
Creating and Operating smtplib.SMTP Objects
These are the main methods of the smtplib module.
| Syntax | Meaning / Process |
smtplib.SMTP(HOST, PORT) | Creates a connection object to the SMTP server. Usually, port 587 (STARTTLS) or 25 (non-encrypted/local) is used. |
smtp.starttls() | Switches the connection to TLS (Transport Layer Security) mode to encrypt the communication path. Must be executed before sending credentials. |
smtp.login(EMAIL, PASS) | Sends authentication credentials to the SMTP server. For Gmail, you may need an “App Password” instead of your regular password. |
smtp.send_message(msg) | Analyzes the EmailMessage object and sends the email in the correct format. |
Important Notes
Security and Password Management
It is dangerous to write passwords directly in your script. You should read them from environment variables (os.environ) or external configuration files. When using Gmail, you must enable two-factor authentication and generate an “App Password.” Regular login passwords will be rejected.
Port Numbers
- 587: Used with
smtplib.SMTP("host", 587)followed bystarttls(). This is the recommended method. - 465: Used with
smtplib.SMTP_SSL("host", 465)to start an SSL connection immediately.
Sending Limits
Free SMTP servers often have limits on the number of emails you can send per day and use spam filters. These are not suitable for sending large volumes of emails, such as newsletters.
Advanced Usage
Adding attachments is easy with EmailMessage.
import mimetypes
# (Code for creating msg is omitted)
# File you want to attach
file_path = "report.pdf"
with open(file_path, "rb") as f:
file_data = f.read()
file_name = f.name
# Automatically determine the MIME type
guessed_type = mimetypes.guess_type(file_name)[0]
if guessed_type:
maintype, subtype = guessed_type.split("/")
else:
maintype, subtype = "application", "octet-stream"
# Add attachment
msg.add_attachment(
file_data,
maintype=maintype,
subtype=subtype,
filename=file_name
)
Summary
Sending emails in Python has become very intuitive with the EmailMessage class. You can use set_content for text only and combine it with add_alternative for HTML emails. Always use the with statement and try-except blocks for proper connection management and error handling.
