The email module handles creating and parsing email messages. It's the foundation for working with email in Python—building messages, parsing received mail, and handling attachments.
Create Simple Email
from email.message import EmailMessage
msg = EmailMessage()
msg['Subject'] = 'Hello from Python'
msg['From'] = 'sender@example.com'
msg['To'] = 'recipient@example.com'
msg.set_content('This is the email body.')
print(msg)HTML Email
from email.message import EmailMessage
msg = EmailMessage()
msg['Subject'] = 'HTML Email'
msg['From'] = 'sender@example.com'
msg['To'] = 'recipient@example.com'
# Set HTML content with plain text alternative
msg.set_content('Plain text version')
msg.add_alternative('''
<html>
<body>
<h1>Hello!</h1>
<p>This is <b>HTML</b> content.</p>
</body>
</html>
''', subtype='html')
print(msg)Add Attachments
from email.message import EmailMessage
import mimetypes
msg = EmailMessage()
msg['Subject'] = 'Email with Attachment'
msg['From'] = 'sender@example.com'
msg['To'] = 'recipient@example.com'
msg.set_content('Please see attached file.')
# Add file attachment
filename = 'document.pdf'
with open(filename, 'rb') as f:
file_data = f.read()
file_type, _ = mimetypes.guess_type(filename)
maintype, subtype = file_type.split('/')
msg.add_attachment(
file_data,
maintype=maintype,
subtype=subtype,
filename=filename
)Add Image Attachment
from email.message import EmailMessage
msg = EmailMessage()
msg['Subject'] = 'Photo'
msg['From'] = 'sender@example.com'
msg['To'] = 'recipient@example.com'
msg.set_content('Here is the photo.')
with open('photo.jpg', 'rb') as f:
msg.add_attachment(
f.read(),
maintype='image',
subtype='jpeg',
filename='photo.jpg'
)Inline Images in HTML
from email.message import EmailMessage
import uuid
msg = EmailMessage()
msg['Subject'] = 'Inline Image'
msg['From'] = 'sender@example.com'
msg['To'] = 'recipient@example.com'
# Generate content ID
cid = str(uuid.uuid4())
# HTML with inline image reference
html = f'''
<html>
<body>
<h1>Look at this!</h1>
<img src="cid:{cid}">
</body>
</html>
'''
msg.set_content('Plain text fallback')
msg.add_alternative(html, subtype='html')
# Add image with content ID
with open('image.png', 'rb') as f:
msg.get_payload()[1].add_related(
f.read(),
maintype='image',
subtype='png',
cid=cid
)Parse Email
Read and extract email parts:
from email import message_from_string, message_from_bytes
# From string
email_text = '''From: sender@example.com
To: recipient@example.com
Subject: Test Email
This is the body.'''
msg = message_from_string(email_text)
print(msg['From']) # sender@example.com
print(msg['Subject']) # Test Email
print(msg.get_payload()) # This is the body.Parse Multipart Email
from email import message_from_bytes
def parse_email(raw_email):
"""Extract parts from multipart email."""
msg = message_from_bytes(raw_email)
result = {
'from': msg['From'],
'to': msg['To'],
'subject': msg['Subject'],
'body': None,
'html': None,
'attachments': []
}
if msg.is_multipart():
for part in msg.walk():
content_type = part.get_content_type()
disposition = str(part.get('Content-Disposition', ''))
if content_type == 'text/plain' and 'attachment' not in disposition:
result['body'] = part.get_payload(decode=True).decode()
elif content_type == 'text/html' and 'attachment' not in disposition:
result['html'] = part.get_payload(decode=True).decode()
elif 'attachment' in disposition:
result['attachments'].append({
'filename': part.get_filename(),
'content_type': content_type,
'data': part.get_payload(decode=True)
})
else:
result['body'] = msg.get_payload(decode=True).decode()
return resultSave Attachments
from email import message_from_bytes
import os
def save_attachments(raw_email, output_dir):
"""Save all attachments from email."""
msg = message_from_bytes(raw_email)
saved = []
for part in msg.walk():
if part.get_content_disposition() == 'attachment':
filename = part.get_filename()
if filename:
filepath = os.path.join(output_dir, filename)
with open(filepath, 'wb') as f:
f.write(part.get_payload(decode=True))
saved.append(filepath)
return savedHandle Encodings
from email.header import decode_header
def decode_subject(msg):
"""Decode email subject handling various encodings."""
subject = msg['Subject']
if subject:
decoded_parts = decode_header(subject)
decoded_subject = ''
for part, encoding in decoded_parts:
if isinstance(part, bytes):
decoded_subject += part.decode(encoding or 'utf-8')
else:
decoded_subject += part
return decoded_subject
return ''Build with MIMEMultipart (Legacy)
Older API still works:
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email import encoders
msg = MIMEMultipart()
msg['Subject'] = 'Legacy Style'
msg['From'] = 'sender@example.com'
msg['To'] = 'recipient@example.com'
# Attach text
msg.attach(MIMEText('Body text', 'plain'))
# Attach file
with open('file.pdf', 'rb') as f:
part = MIMEBase('application', 'octet-stream')
part.set_payload(f.read())
encoders.encode_base64(part)
part.add_header('Content-Disposition', 'attachment', filename='file.pdf')
msg.attach(part)Send via SMTP
from email.message import EmailMessage
import smtplib
msg = EmailMessage()
msg['Subject'] = 'Test'
msg['From'] = 'sender@example.com'
msg['To'] = 'recipient@example.com'
msg.set_content('Hello!')
# Send
with smtplib.SMTP('smtp.example.com', 587) as server:
server.starttls()
server.login('user', 'password')
server.send_message(msg)Email Validation
Basic email format check:
import re
def is_valid_email(email):
"""Basic email format validation."""
pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
return bool(re.match(pattern, email))
print(is_valid_email('user@example.com')) # True
print(is_valid_email('invalid')) # FalseMultiple Recipients
from email.message import EmailMessage
msg = EmailMessage()
msg['Subject'] = 'Team Update'
msg['From'] = 'sender@example.com'
msg['To'] = 'alice@example.com, bob@example.com'
msg['Cc'] = 'manager@example.com'
msg['Bcc'] = 'archive@example.com'
msg.set_content('Update for the team.')When to Use email Module
Use email module when:
- Building email messages with attachments
- Parsing received emails
- Processing email files (.eml)
- Custom email handling
Use higher-level libraries when:
- Simple sending only →
smtplibalone - Full email client →
imaplib+email - Production apps → consider
yagmail,emails
The email module handles the complexity of MIME—use it whenever you need more than plain text.
React to this post: