Python Email Tutorial

Let’s look at email tutorial

  1. Notice

    An email message has headers and a body. Constructing a message is separate from sending it through an SMTP service.

  2. Try

    Follow the example, change its sample, and compare what happens.

  3. Check

    Explain the result and test your understanding.

Watch how it works

Focus on how the names, objects, and calculations change. We’ll inspect the printed output in the next section.

Explore the idea

Follow the code and watch its state change. Guided simulation.

Code
Python
from email.message import EmailMessage
message = EmailMessage()
message["Subject"] = 'Welcome'
message.set_content("Hello, Ada!")
print(message["Subject"])
print(message.get_content().strip())
Live state

Top-level scope

EmailMessage→Object 1
message→Object 2
Object 1 · typeclass EmailMessageObject 2 · EmailMessage<email.message.EmailMessage object>
This step

An email message has headers and a body. Constructing a message is separate from sending it through an SMTP service.

Picture email tutorial

An email message has headers and a body. Constructing a message is separate from sending it through an SMTP service.

Image brief

Alt text
A diagram for Python Email Tutorial. Instructions: from email.message import EmailMessage message = EmailMessage() message["Subject"] = 'Welcome' message.set_content("Hello, Ada!") print(message["Subject"]) print(message.get_content().strip()). Displayed result: Welcome Hello, Ada!. The final example bindings are EmailMessage --> object 1 (type); message --> object 2 (EmailMessage). object 1: class EmailMessage; object 2: <email.message.EmailMessage object>
Image generation prompt
Create a clean educational illustration for a Python programming lesson titled "Python Email Tutorial". Teaching focus: An email message has headers and a body. Constructing a message is separate from sending it through an SMTP service. Depict exactly the relationships and labels in this reference diagram: EXAMPLE INSTRUCTIONS from email.message import EmailMessage message = EmailMessage() message["Subject"] = 'Welcome' message.set_content("Hello, Ada!") print(message["Subject"]) print(message.get_content().strip()) | v DISPLAYED RESULT Welcome Hello, Ada! FINAL BINDINGS EmailMessage --> object 1 (type) message --> object 2 (EmailMessage) object 1: class EmailMessage object 2: <email.message.EmailMessage object> Use a flat vector style, generous spacing, clear arrows and legible labels, with a restrained blue/teal palette on a plain background. The diagram must explain the programming idea accurately. Do not depict variables as boxes containing copied objects when they share a reference. Avoid mascots, decorative screenshots, logos, gradients and extra text. Landscape composition, approximately 3:2, suitable for a course card. Accessible description: A diagram for Python Email Tutorial. Instructions: from email.message import EmailMessage message = EmailMessage() message["Subject"] = 'Welcome' message.set_content("Hello, Ada!") print(message["Subject"]) print(message.get_content().strip()). Displayed result: Welcome Hello, Ada!. The final example bindings are EmailMessage --> object 1 (type); message --> object 2 (EmailMessage). object 1: class EmailMessage; object 2: <email.message.EmailMessage object>

Follow the execution

Choose a different sample input, then follow every step through to the output. Before revealing it, predict what the program will display.

Execution workspace

Choose a sample and follow its execution. Guided simulation.

Code
Python
from email.message import EmailMessage
message = EmailMessage()
message["Subject"] = 'Welcome'
message.set_content("Hello, Ada!")
print(message["Subject"])
print(message.get_content().strip())
Live state

Top-level scope

EmailMessage→Object 1
message→Object 2
Object 1 · typeclass EmailMessageObject 2 · EmailMessage<email.message.EmailMessage object>
This step

The example has finished. Compare the final output with the values and references above.

Terminal
Output
Welcome
Hello, Ada!

What do you expect this program to print?

What does this example display?

from email.message import EmailMessage
message = EmailMessage()
message["Subject"] = 'Welcome'
message.set_content("Hello, Ada!")
print(message["Subject"])
print(message.get_content().strip())
What does this example display?
Choose one answer. You can try again.

Answer and explanation

Welcome Hello, Ada!

Follow the printed values in order. The output is: Welcome Hello, Ada!

Which explanation matches what you learned?

Which explanation matches what you learned?
Choose one answer. You can try again.

Answer and explanation

An email message has headers and a body. Constructing a message is separate from sending it through an SMTP service.

An email message has headers and a body. Constructing a message is separate from sending it through an SMTP service.

Introduction to Sending Emails with Python

Have you ever wanted your Python application to send an email—whether it’s a newsletter, a password reset link, or an alert from a monitoring system? This tutorial walks you through how to send emails using Python’s built-in smtplib and email libraries. No third-party packages required.

Understanding the Basics: SMTP and Email Protocols

Before diving into code, it’s helpful to know how email sending works. Email clients (like Gmail or Outlook) use SMTP (Simple Mail Transfer Protocol) to send messages. Python’s smtplib allows you to interact with SMTP servers, and the email library helps you structure messages in a proper format.

1. Sending a Basic Plain Text Email

Let’s start with the simplest example: sending a plain text email from a Gmail account.

import smtplib
from email.message import EmailMessage

# Setup email content
msg = EmailMessage()
msg['Subject'] = 'Test Email from Python'
msg['From'] = 'your_email@gmail.com'
msg['To'] = 'recipient@example.com'
msg.set_content('This is a test email sent from Python.')

# Gmail SMTP setup
smtp_server = 'smtp.gmail.com'
smtp_port = 587

# Send the email
with smtplib.SMTP(smtp_server, smtp_port) as server:
    server.starttls()  # Secure the connection
    server.login('your_email@gmail.com', 'your_app_password')
    server.send_message(msg)
    print("Email sent successfully!")
Email sent successfully!

Explaining the Code

  • EmailMessage() – a modern way to create well-formatted email messages.
  • starttls() – upgrades the connection to a secure encrypted one.
  • Use App Passwords if 2FA is enabled for Gmail. Regular passwords won’t work.

2. Sending HTML Emails

You can send beautiful, formatted HTML emails as well. Let’s modify the previous example.

msg = EmailMessage()
msg['Subject'] = 'Welcome!'
msg['From'] = 'your_email@gmail.com'
msg['To'] = 'recipient@example.com'

html_content = """
<html>
  <body>
    <h1>Welcome to our service!</h1>
    <p>We are thrilled to have you onboard.</p>
  </body>
</html>
"""
msg.set_content('This is a fallback for non-HTML clients.')
msg.add_alternative(html_content, subtype='html')
HTML content sent as an email.

3. Attaching Files to Emails

To include an attachment like a PDF or image, you can use the add_attachment() method. Here’s how to attach a PDF file:

from pathlib import Path

pdf_path = Path('sample.pdf')
pdf_data = pdf_path.read_bytes()

msg.add_attachment(pdf_data, maintype='application', subtype='pdf', filename='sample.pdf')

Verification Tips Before Sending

  • ✔️ Check that msg['From'] and login credentials match.
  • ✔️ Validate recipient addresses to avoid SMTP errors.
  • ✔️ If sending many emails, respect SMTP limits to avoid being blocked.
  • ✔️ Use environment variables or secure config for passwords.

4. Error Handling and Common Pitfalls

It’s essential to wrap your email logic in try-except blocks to handle network errors or login failures gracefully.

try:
    with smtplib.SMTP(smtp_server, smtp_port) as server:
        server.starttls()
        server.login('your_email@gmail.com', 'your_app_password')
        server.send_message(msg)
except smtplib.SMTPAuthenticationError:
    print("Login failed. Check your credentials.")
except Exception as e:
    print(f"Something went wrong: {e}")
Login failed. Check your credentials.

5. How to Test Without Sending Real Emails

For testing, Python offers a local debugging SMTP server that just prints emails instead of sending:

Run this in your terminal

python -m smtpd -c DebuggingServer -n localhost:1025

with smtplib.SMTP('localhost', 1025) as server:
    server.send_message(msg)
Email content will be printed in the terminal running the dummy server.

Summary – Things to Remember

  • smtplib handles the email sending, while email.message handles formatting.
  • Always use starttls() or SMTP_SSL to keep your credentials safe.
  • Use try-except blocks to catch and debug common errors.
  • For Gmail, generate an App Password if 2-Step Verification is enabled.

Next Steps

You can now build on this to send reports, alerts, or even newsletter campaigns using Python. Try integrating it with schedulers or form submissions for real-world automation!

What to remember

Connect what you noticed in the example with the idea below.

Remember

An email message has headers and a body. Constructing a message is separate from sending it through an SMTP service.

Watch out for this idea

Creating an EmailMessage automatically sends email.

This is the misconception to avoid.

Reaching the recap and completing the quizzes are separate milestones.

    Next lesson: Python JSON Tutorial →