Skip to Content
Course content

278: Sending Emails with smtplib

Click on the "Edit" button in the top corner of the screen to edit your slide content.

You’ve probably used email clients your whole life, but when you need a Python script to send one, you'll find that the process is surprisingly low-level. We're going to use smtplib, which is Python's built-in library for the Simple Mail Transfer Protocol. It’s not the prettiest API, but it's the standard.

For this lesson, let's build a "Server Health Alert." Imagine you have a script monitoring your server's disk space; you don't want to check a log file every hour, you just want an email when things get critical.

Connecting to the Mail Server

First, we need to tell Python where to send the mail. Most of us use Gmail, Outlook, or a corporate SMTP relay. I usually prefer using an App Password for Gmail because it's more secure than using your primary account password in a script (which is a huge security risk).

import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart

# These would ideally be in an environment variable, not hardcoded
SMTP_SERVER = "smtp.gmail.com"
SMTP_PORT = 587
SENDER_EMAIL = "your-bot@gmail.com"
SENDER_PASSWORD = "your-app-password"
RECIPIENT_EMAIL = "admin@example.com"

Packaging the Email with MIME

Here is where people often get tripped up. You can't just send a raw string as an email. Emails are actually structured documents with headers (To, From, Subject) and a body. We use the MIME (Multipurpose Internet Mail Extensions) modules to build this structure.

def create_alert_message(disk_usage):
    message = MIMEMultipart()
    message["From"] = SENDER_EMAIL
    message["To"] = RECIPIENT_EMAIL
    message["Subject"] = f"CRITICAL: Server Disk Usage at {disk_usage}%"

    body = f"Warning: The server disk space has reached {disk_usage}%. Please clear logs immediately."
    message.attach(MIMEText(body, "plain"))
    
    return message

The "Connection Refused" Mistake

Now, let's try to send it. I'll show you a mistake I made early in my career. I used to try to log in immediately after creating the SMTP object. It looks like this:

# THE WRONG WAY
server = smtplib.SMTP(SMTP_SERVER, SMTP_PORT)
server.login(SENDER_EMAIL, SENDER_PASSWORD) # This will likely fail or be rejected
server.send_message(msg)
server.quit()

When I first did this, I kept getting authentication errors or "connection dropped" messages. Why? Because modern mail servers require an encrypted connection before they'll let you send credentials over the wire. You can't just shout your password into the void; you have to establish a secure tunnel first using starttls().

Here is the corrected flow. We initialize the connection, secure it, and then log in.

def send_health_alert(disk_usage):
    msg = create_alert_message(disk_usage)
    
    try:
        # 1. Create the connection
        server = smtplib.SMTP(SMTP_SERVER, SMTP_PORT)
        
        # 2. Upgrade the connection to secure TLS
        server.starttls() 
        
        # 3. Now it's safe to login
        server.login(SENDER_EMAIL, SENDER_PASSWORD)
        
        # 4. Send the packaged MIME message
        server.send_message(msg)
        print("Alert sent successfully!")
        
    except Exception as e:
        print(f"Error sending email: {e}")
    finally:
        server.quit()

# Triggering the alert
send_health_alert(92)

Refining the Process

One thing to keep in mind: smtplib is synchronous. If the mail server is lagging, your entire program stops and waits. In a real production environment, I'd suggest putting this in a background queue (like Celery), but for a simple health check script, this direct approach is perfectly fine. Just remember to always use finally: server.quit() to ensure you don't leave hanging connections on the mail server, which could lead to your IP being temporarily throttled.




📋 Practical Task

Build a Weekly Project Status Reporter

Write a Python script that sends a formatted status report email. Your script should:

  • Define a list of strings representing "Completed Tasks" for the week.
  • Use MIMEMultipart to construct an email with the subject "Weekly Project Update".
  • Convert the list of tasks into a bulleted string for the email body.
  • Implement the smtplib connection flow (including starttls()) to send this report to a recipient of your choice.
  • Include a try/except/finally block to handle potential connection errors and ensure the server connection is closed.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.