Python
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax and Data Types
-
Section 3: Collections
-
39: Set Operations: Union, Intersection, Difference
-
Section 4: Control Flow
-
Section 5: Functions
-
Section 6: Turtle Graphics and Early Practice Projects
-
Section 7: Working with Files and I/O
-
Section 8: Regular Expressions
-
Section 9: Object-Oriented Python
-
Section 10: Error Handling
-
Section 11: Modules and Packages
-
Section 12: Iterators, Generators, and Functional Tools
-
Section 13: Decorators and Metaprogramming
-
Section 14: Concurrency and Parallelism
-
Section 15: Working with Dates, Times, and Numbers
-
Section 16: Standard Library Deep Dive I: Data Structures
-
Section 17: Standard Library Deep Dive II: System and Introspection
-
Section 18: Standard Library Deep Dive III: Security and Encoding
-
Section 19: Standard Library Deep Dive IV: Text and Data Utilities
-
Section 20: Networking and Web Basics
-
Section 21: Working with Databases
-
Section 22: Testing and Quality
-
Section 23: Advanced Typing
-
Section 24: Context Managers and Resource Handling
-
Section 25: Text, Unicode, and Binary Data
-
Section 26: More Functional and Iteration Tools
-
Section 27: Data Validation and Configuration
-
Section 28: Working with Images and Media
-
Section 29: Property-Based and Documentation Testing
-
Section 30: Packaging and Deployment
-
Section 31: Performance and Internals
-
Section 32: Design Patterns in Python
-
Section 33: GUI Programming
-
Section 34: Security Basics
-
Section 35: Data Structures and Algorithms
-
Section 36: Practical Projects
-
Section 37: Capstone Projects
-
Section 38: Interview and Algorithm Practice
-
Section 39: Writing Idiomatic Python
278: Sending Emails with smtplib
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
MIMEMultipartto construct an email with the subject "Weekly Project Update". - Convert the list of tasks into a bulleted string for the email body.
- Implement the
smtplibconnection flow (includingstarttls()) to send this report to a recipient of your choice. - Include a
try/except/finallyblock to handle potential connection errors and ensure the server connection is closed.
There are no comments for now.