HomeCourseModule 12 › Sending emails from Python

Sending emails from Python

Module 12 · Automating Boring Excel Tasks8 min readIntermediate

What you'll learn

  • Send a plain-text email with smtplib
  • Attach a file
  • Use environment variables for credentials

The standard library way

import smtplib
import os
from email.message import EmailMessage

msg = EmailMessage()
msg["Subject"] = "Monthly Report"
msg["From"] = "you@yourdomain.com"
msg["To"] = "boss@yourdomain.com"
msg.set_content("Hi — attached is the monthly report.\n\nThanks,\nYou")

# Attach a file
with open("report.xlsx", "rb") as f:
    msg.add_attachment(
        f.read(),
        maintype="application",
        subtype="vnd.openxmlformats-officedocument.spreadsheetml.sheet",
        filename="report.xlsx",
    )

with smtplib.SMTP_SSL("smtp.gmail.com", 465) as smtp:
    smtp.login(os.environ["EMAIL_USER"], os.environ["EMAIL_PASS"])
    smtp.send_message(msg)
print("Sent.")
⚠️ Never paste passwords in code
Use environment variables (os.environ["EMAIL_PASS"]), a .env file loaded with python-dotenv, or a system secret manager. Never check credentials into version control.

Gmail and Outlook today

Big providers now require app-specific passwords or OAuth. The cleanest libraries:

Personalised mass email

for _, row in customers.iterrows():
    msg = EmailMessage()
    msg["Subject"] = f"Your {row['month']} statement"
    msg["From"]    = "you@yourdomain.com"
    msg["To"]      = row["email"]
    msg.set_content(f"""Hi {row['first_name']},

Your statement for {row['month']} is attached.

Best,
You""")

    with open(row["attachment"], "rb") as f:
        msg.add_attachment(f.read(),
                           maintype="application",
                           subtype="pdf",
                           filename=Path(row["attachment"]).name)

    with smtplib.SMTP_SSL("smtp.gmail.com", 465) as smtp:
        smtp.login(os.environ["EMAIL_USER"], os.environ["EMAIL_PASS"])
        smtp.send_message(msg)

Key takeaways

  • smtplib sends mail; EmailMessage formats it.
  • Use app passwords or OAuth — never your real password.
  • Personalised mass-mail = loop over a DataFrame, one message per row.

Test send

Send yourself a test email with an attached CSV. Confirm it lands. Then do the same for three rows of a small DataFrame.

📹 Video walkthrough
A video walkthrough of this lesson will be embedded here. Until then, the written walkthrough above mirrors what the video will cover step-for-step.