Send email with attachment (SMTP)
Send an email with a PDF attachment using Gmail/SMTP and an app password.
import os, smtplib
from email.message import EmailMessage
msg = EmailMessage()
msg["Subject"] = "Daily report"
msg["From"] = os.environ["SMTP_USER"]
msg["To"] = "boss@example.com"
msg.set_content("Please find today's report attached.")
with open("report.pdf", "rb") as f:
msg.add_attachment(f.read(), maintype="application", subtype="pdf", filename="report.pdf")
with smtplib.SMTP_SSL("smtp.gmail.com", 465) as s:
s.login(os.environ["SMTP_USER"], os.environ["SMTP_APP_PASSWORD"])
s.send_message(msg)
When you need this
Sending a daily report, an invoice or an alert without anyone opening Gmail. Combine it with Windows Task Scheduler or cron and the report just shows up in the inbox every morning.
Set up Gmail first
Gmail won't accept your normal password from a script. You need an App Password:
- Turn on 2-Step Verification on the Google account.
- Go to Google Account → Security → App passwords, and create one.
- You'll get a 16-character code. That's what goes in
SMTP_APP_PASSWORD.
Keep both the email and the app password in environment variables, as the snippet does, not typed into the code. Scripts get shared, pasted into chats and pushed to GitHub, and a password in the code goes wherever the code goes.
How it works
EmailMessage builds the email: subject, from, to and body. add_attachment reads the PDF as bytes and attaches it with the right type, so it opens as a PDF on the other end. SMTP_SSL connects to Gmail on port 465 over an encrypted connection, logs in and sends.
Small changes you'll probably want
- Several recipients:
msg["To"] = "a@x.com, b@x.com". - An Excel attachment: use
maintype="application", subtype="vnd.openxmlformats-officedocument.spreadsheetml.sheet". - An HTML body: after
set_content(...), addmsg.add_alternative("<p>Hi,<br>Report attached.</p>", subtype="html").
Gmail limits how much you can send in a day, roughly 500 emails for a normal account. For newsletters or bulk mail, use a proper email service instead.