Python's simplicity and readability make it an ideal choice for automating repetitive tasks, and email automation ranks among the most practical applications. Day to day, whether you're building a notification system for a web application, sending monthly newsletters, or automating personal reminders, mastering how to send an email python-style integrates smoothly into broader workflows. Python provides built-in tools that abstract away complex networking details, allowing developers and beginners alike to focus on the message rather than the protocol. This full breakdown walks you through the entire process using Python's standard libraries, focusing on the smtplib module for server communication and the email package for message construction. By the end, you'll possess a reusable, secure script capable of handling plain text, formatted content, and attachments across various email providers.
Prerequisites and Setup
Before writing code, a few foundational elements ensure the process runs smoothly. First, you'll need Python installed on your machine (version 3.x is recommended). No external packages are strictly required for basic operations, as smtplib and email come included with the standard library. Even so, if you prefer additional functionality or a more intuitive interface, third-party libraries like yagmail or email-validator can be installed via pip, but the methods described here rely on core Python features you already have.
Second, you must have access to an email account that allows less secure apps or, preferably, supports OAuth2 and modern authentication standards. Gmail, Outlook, Yahoo, and custom domain servers all have slightly different requirements regarding authentication, ports, and encryption. For this tutorial, we'll use Gmail's SMTP settings as a concrete example, but the logic transfers directly to other providers with minor adjustments to the server address, port, and security protocol.
Third, understanding the difference between SMTP (Simple Mail Transfer Protocol) for sending and IMAP/POP3 for receiving is helpful, but you only need to focus on SMTP details when learning how to send an email python scripts will put to use. The key details you'll configure are the SMTP server hostname, port number, and whether to use STARTTLS or SSL/TLS for encrypting the connection.
Step-by-Step Guide: Using smtplib and MIMEText
The heart of sending an email in Python lies in two standard library modules: smtplib, which handles the connection to the mail server and the actual sending process, and email.Worth adding: mime. text.MIMEText, which formats your message body so it's compatible with email standards. Together, they form a strong foundation that can be extended with attachments, HTML formatting, and header customization.
Establishing a Connection
The first technical step is creating an SMTP object and connecting to the server. gmail., smtp.com) and the port. On the flip side, for Gmail, the standard port for encrypted connections is 587 when using STARTTLS, or 465 for direct SSL. Because of that, g. This involves specifying the server address (e.We'll walk through the STARTTLS approach, as it's the most commonly recommended method for modern applications Not complicated — just consistent..
import smtplib
server
```python
import smtplib
server = smtplib.SMTP('smtp.gmail.com', 587)
server.Now, ehlo() # Identify ourselves to the server
server. starttls() # Upgrade the connection to a secure encrypted TLS connection
server.ehlo() # Re-identify after TLS upgrade
server.login('your_email@gmail.
**Breakdown of the connection sequence:**
1. **`smtplib.SMTP(host, port)`**: Initializes the connection object. Port `587` is the standard submission port requiring STARTTLS.
2. **`ehlo()`**: Sends the `EHLO` (Extended Hello) command. This identifies the client to the server and requests the list of supported extensions (like `STARTTLS`, `SIZE`, `AUTH`).
3. **`starttls()`**: Initiates the TLS handshake, encrypting all subsequent traffic (including authentication credentials). This is mandatory for modern providers.
4. **`ehlo()` (again)**: Required after `starttls()` because the connection state has changed; the client must re-negotiate capabilities over the encrypted channel.
5. **`login(user, password)`**: Authenticates using the `AUTH` command. **Critical:** For Gmail and most major providers, the "password" here must be an **App Password** (generated in your Google Account security settings), not your primary account password, especially if 2-Factor Authentication (2FA) is enabled.
> **Note for Port 465 (SSL):** If your provider requires implicit SSL on port 465 (less common now but still used), replace `smtplib.Worth adding: sMTP_SSL('smtp. gmail.com', 465)` and **remove** the `starttls()` and second `ehlo()` calls. SMTP` with `smtplib.The connection is encrypted immediately upon instantiation.
---
### Constructing the Message: From Plain Text to MIME Multipart
A raw string is insufficient for standards-compliant email. Even so, the `email. mime` package builds structured messages that clients (Gmail, Outlook, Thunderbird) render correctly.
#### 1. Simple Plain Text (`MIMEText`)
For basic notifications or logs, `MIMEText` is sufficient.
```python
from email.mime.text import MIMEText
msg = MIMEText('This is the body of the email.', 'plain', 'utf-8')
msg['Subject'] = 'Automated Report - Plain Text'
msg['From'] = 'your_email@gmail.com'
msg['To'] = 'recipient@example.
#### 2. HTML Formatting (`MIMEText` with `'html'`)
Modern emails often require formatting, links, or styling. Simply change the subtype.
```python
html_content = """
Quarterly Report
Revenue increased by 15% this quarter.
View Full Dashboard
"""
msg = MIMEText(html_content, 'html', 'utf-8')
msg['Subject'] = 'Quarterly Report - HTML'
msg['From'] = 'your_email@gmail.com'
msg['To'] = 'recipient@example.com'
3. Multipart: HTML + Plain Text Fallback + Attachments (MIMEMultipart)
Best practice dictates sending both plain text and HTML versions (multipart/alternative) so the recipient's client chooses the best render. Adding attachments requires a multipart/mixed container wrapping the alternative part Simple, but easy to overlook..
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email import encoders
import os
def build_multipart_message(sender, recipient, subject, text_body, html_body, attachment_paths=None):
"""
Constructs a MIMEMultipart message with text/HTML alternatives and optional attachments.
"""
# Root container: 'mixed' allows attachments alongside the text/HTML alternative block
msg = MIMEMultipart('mixed')
msg['Subject'] = subject
msg['From'] = sender
msg['To'] = recipient
# Child container: 'alternative' holds the text and HTML versions
alternative_part = MIMEMultipart('alternative')
part_text = MIMEText(text_body, 'plain', 'utf-8')
part_html = M