Mail Integration
The sillo Mail contrib module provides a comprehensive email sending solution.
Mail Integration
Section titled “Mail Integration”The sillo Mail contrib module provides a comprehensive email sending solution with SMTP support, template integration, and background task processing.
Overview
Section titled “Overview”sillo Mail is a powerful and easy-to-use email sending solution for sillo applications that includes:
- SMTP Support: Full SMTP configuration with TLS/SSL support
- Template Integration: Jinja2-based HTML email templates
- Background Tasks: Async email sending with sillo-contrib tasks
- Dependency Injection: Easy integration with sillo applications
- Multiple Providers: Pre-configured settings for Gmail, Outlook, SendGrid
- Attachments: Support for file attachments and inline images
- Error Handling: Comprehensive error reporting and logging
Installation
Section titled “Installation”# Basic installationuv add "sillo-contrib[mail]"
# With template supportuv add "sillo-contrib[mail,templating]"
# With all featuresuv add "sillo-contrib[all]"Direct MailClient Usage
Section titled “Direct MailClient Usage”The most straightforward way to send emails is by using the MailClient class directly. This approach gives you full control over the email sending process without requiring dependency injection.
Creating a MailClient
Section titled “Creating a MailClient”from sillo_contrib.mail import MailClient, MailConfig
# Create with default configuration (uses environment variables)mail_client = MailClient()
# Or with explicit configurationconfig = MailConfig( smtp_host="smtp.gmail.com", smtp_port=587, smtp_username="your-email@gmail.com", smtp_password="your-app-password", use_tls=True, default_from="Your Name <your-email@gmail.com>")mail_client = MailClient(config=config)Basic Email Sending
Section titled “Basic Email Sending”import asyncio
async def send_basic_email(): # Start the client (establishes SMTP connection) await mail_client.start()
try: # Send a simple email result = await mail_client.send_email( to="recipient@example.com", subject="Hello World", body="This is a plain text email", html_body="<h1>Hello World</h1><p>This is an HTML email.</p>" )
if result.success: print(f"Email sent successfully! Message ID: {result.message_id}") else: print(f"Failed to send email: {result.error}")
finally: # Always stop the client to close connections await mail_client.stop()
# Run the async functionasyncio.run(send_basic_email())Using Templates
Section titled “Using Templates”Create templates in your templates/emails/ directory:
templates/emails/welcome.html
<!DOCTYPE html><html><head> <title>Welcome {{ name }}!</title></head><body> <h1>Welcome {{ name }}!</h1> <p>Thank you for joining {{ company_name }}.</p> <p>Your account has been created with email: {{ email }}</p> <a href="{{ activation_url }}">Activate Your Account</a></body></html>templates/emails/welcome.txt
Welcome {{ name }}!
Thank you for joining {{ company_name }}.Your account has been created with email: {{ email }}.
Activate Your Account: {{ activation_url }}Send template emails:
async def send_template_email(): config = MailConfig( smtp_host="smtp.gmail.com", smtp_port=587, smtp_username="your-email@gmail.com", smtp_password="your-app-password", use_tls=True, template_directory="templates/emails" ) mail_client = MailClient(config=config)
await mail_client.start()
try: result = await mail_client.send_template_email( to="newuser@example.com", subject="Welcome to Our Platform!", template_name="welcome", context={ "name": "John Doe", "email": "newuser@example.com", "company_name": "Acme Corp", "activation_url": "https://example.com/activate/12345" } )
print(f"Template email sent: {result.success}")
finally: await mail_client.stop()
asyncio.run(send_template_email())Advanced Message Creation
Section titled “Advanced Message Creation”For more control, create EmailMessage objects directly:
from sillo_contrib.mail import EmailMessage
async def send_advanced_email(): config = MailConfig( smtp_host="smtp.gmail.com", smtp_port=587, smtp_username="your-email@gmail.com", smtp_password="your-app-password", use_tls=True ) mail_client = MailClient(config=config)
await mail_client.start()
try: # Create a detailed email message message = EmailMessage( to="recipient@example.com", subject="Advanced Email", body="Plain text content", html_body="<h1>HTML Content</h1>", cc="manager@example.com", bcc="archive@example.com" )
# Add custom headers message.add_header("X-Campaign-ID", "summer-2024") message.add_header("X-Mailer", "sillo Mail")
# Add attachments message.add_attachment("report.pdf", b"PDF content", "application/pdf")
# Send the message result = await mail_client.send_message(message) print(f"Advanced email sent: {result.success}")
finally: await mail_client.stop()
asyncio.run(send_advanced_email())Integration with sillo Applications
Section titled “Integration with sillo Applications”While direct MailClient usage is simple, integrating with sillo applications provides additional benefits like automatic lifecycle management and dependency injection.
Application Setup
Section titled “Application Setup”For sillo applications, use the setup_mail function for automatic lifecycle management:
from sillo import silloAppfrom sillo_contrib.mail import setup_mail, MailConfig
app = silloApp()
# Setup with environment variablesmail_client = setup_mail(app)
# Or with custom configurationconfig = MailConfig( smtp_host="smtp.gmail.com", smtp_port=587, smtp_username="your-email@gmail.com", smtp_password="your-app-password", use_tls=True, default_from="Your Name <your-email@gmail.com>")mail_client = setup_mail(app, config=config)Environment Variables
Section titled “Environment Variables”# SMTP ConfigurationSMTP_HOST=smtp.gmail.comSMTP_PORT=587SMTP_USERNAME=your-email@gmail.comSMTP_PASSWORD=your-app-passwordSMTP_USE_TLS=trueSMTP_USE_SSL=false
# Email DefaultsMAIL_DEFAULT_FROM=Your Name <your-email@gmail.com>MAIL_DEFAULT_REPLY_TO=support@yourcompany.com
# Template DirectoryMAIL_TEMPLATE_DIR=templates/emails
# Debug SettingsMAIL_DEBUG=falseMAIL_SUPPRESS_SEND=falseDependency Injection
Section titled “Dependency Injection”For cleaner code in your route handlers, use dependency injection:
from sillo.http import Request, Responsefrom sillo_contrib.mail import MailDepend
@app.post("/send-email")async def send_email( request: Request, response: Response, mail_client: MailClient = MailDepend()): result = await mail_client.send_email( to="user@example.com", subject="Welcome to Our Service", body="Thank you for joining our platform!", html_body="<h1>Welcome!</h1><p>Thank you for joining our platform!</p>" )
return response.json({ "success": result.success, "message_id": result.message_id, "sent_at": result.sent_at.isoformat() })Getting MailClient from Request
Section titled “Getting MailClient from Request”Alternatively, get the mail client directly from the request:
from sillo_contrib.mail import get_mail_from_request
@app.post("/send-email-manual")async def send_email_manual(request: Request, response: Response): mail_client = get_mail_from_request(request)
result = await mail_client.send_email( to="user@example.com", subject="Welcome to Our Service", body="Thank you for joining our platform!" )
return response.json({"success": result.success, "message_id": result.message_id})Background Email Sending
Section titled “Background Email Sending”Send emails asynchronously without blocking your API responses:
from sillo_contrib.mail import send_email_async
@app.post("/send-async")async def send_async_email( request: Request, response: Response): task = await send_email_async( request=request, to="user@example.com", subject="Processing Your Request", body="We're processing your request and will notify you when complete." )
return response.json({ "message": "Email queued for sending", "task_id": task.id if task else None })Configuration
Section titled “Configuration”Provider-Specific Configurations
Section titled “Provider-Specific Configurations”config = MailConfig.for_gmail( username="your-email@gmail.com", password="your-app-password", # Use app password, not regular password default_from="Your Name <your-email@gmail.com>")Outlook/Office 365
Section titled “Outlook/Office 365”config = MailConfig.for_outlook( username="your-email@outlook.com", password="your-password", default_from="Your Name <your-email@outlook.com>")SendGrid
Section titled “SendGrid”config = MailConfig.for_sendgrid( api_key="your-sendgrid-api-key", default_from="your-email@yourdomain.com")Advanced Features
Section titled “Advanced Features”Custom Email Messages
Section titled “Custom Email Messages”from sillo_contrib.mail import EmailMessage
# Create detailed email messagemessage = EmailMessage( to="recipient@example.com", subject="Custom Email", body="Plain text content", html_body="<h1>HTML Content</h1>", cc="manager@example.com", bcc="archive@example.com", priority=1 # High priority)
# Add custom headersmessage.add_header("X-Campaign-ID", "summer-2024")message.add_header("X-Mailer", "sillo Mail")
# Add attachmentsmessage.add_attachment("report.pdf", b"PDF content", "application/pdf")
# Send the messageresult = await mail_client.send_message(message)Template Custom Filters
Section titled “Template Custom Filters”# Add custom Jinja2 filtersdef format_currency(value, currency="USD"): return f"{value:.2f} {currency}"
# In your mail client setupmail_client._template_env.filters["currency"] = format_currency
# Use in templates{{ price | currency }}API Reference
Section titled “API Reference”MailClient
Section titled “MailClient”The main mail client class for sending emails.
Methods
Section titled “Methods”send_email(to, subject, body=None, html_body=None, **kwargs)- Send an emailsend_message(message)- Send an EmailMessage objectsend_template_email(to, subject, template_name, context=None, **kwargs)- Send template emailcreate_message(to, subject, **kwargs)- Create EmailMessage object
EmailMessage
Section titled “EmailMessage”Represents an email message with all its components.
Properties
Section titled “Properties”to- Recipient email addressessubject- Email subjectbody- Plain text bodyhtml_body- HTML bodyattachments- List of attachmentsheaders- Custom headers
Methods
Section titled “Methods”add_attachment(filename, content, content_type=None, content_id=None)- Add attachmentset_template(template_name, context=None)- Set templateadd_header(name, value)- Add custom header
MailConfig
Section titled “MailConfig”Configuration for the mail client.
Class Methods
Section titled “Class Methods”for_gmail(username, password, **kwargs)- Gmail configurationfor_outlook(username, password, **kwargs)- Outlook configurationfor_sendgrid(api_key, **kwargs)- SendGrid configuration
Best Practices
Section titled “Best Practices”Security
Section titled “Security”- Never hardcode credentials - Always use environment variables
- Use app passwords - For Gmail, generate app passwords instead of using your main password
- Enable TLS - Always use TLS/SSL for secure connections
- Validate inputs - Sanitize and validate all email inputs
Performance
Section titled “Performance”- Use background tasks - Send emails asynchronously to avoid blocking responses
- Connection pooling - Reuse SMTP connections when possible
- Template caching - Templates are automatically cached for performance
Reliability
Section titled “Reliability”- Error handling - Always handle email sending errors gracefully
- Logging - Enable debug mode during development
- Testing - Use
suppress_send=Truefor testing without actually sending emails
Troubleshooting
Section titled “Troubleshooting”Common Issues
Section titled “Common Issues”-
Authentication Failed
- Check SMTP credentials
- For Gmail, use an App Password instead of your regular password
- Verify 2FA settings
-
Connection Timeout
- Check SMTP host and port
- Verify firewall settings
- Increase
smtp_timeoutvalue
-
Template Not Found
- Verify template directory path
- Check template file names and extensions
- Ensure template files exist
-
Background Tasks Not Working
- Install sillo-contrib tasks:
uv add "sillo-contrib[tasks]" - Setup tasks in your app:
setup_tasks(app)
- Install sillo-contrib tasks:
Debug Mode
Section titled “Debug Mode”Enable debug mode to see SMTP communication:
config = MailConfig( debug=True, # Enables SMTP debug logging suppress_send=True # Test mode - don't actually send)Integration with Other Modules
Section titled “Integration with Other Modules”Background Tasks
Section titled “Background Tasks”The mail module integrates seamlessly with the sillo-contrib tasks module for async email sending:
from sillo_contrib.tasks import setup_tasksfrom sillo_contrib.mail import setup_mail
app = silloApp()
# Setup tasks firsttask_manager = setup_tasks(app)
# Then setup mail (will automatically integrate with tasks)mail_client = setup_mail(app)Templates
Section titled “Templates”The mail module uses Jinja2 for template rendering, which can be integrated with your existing template setup:
# Share template environment with your web templatesfrom jinja2 import FileSystemLoader, Environment
# Create shared template environmenttemplate_env = Environment( loader=FileSystemLoader(["templates/web", "templates/emails"]))
# Use with mail clientmail_client._template_env = template_envExamples
Section titled “Examples”Complete Email Service
Section titled “Complete Email Service”from sillo import silloAppfrom sillo_contrib.mail import setup_mail, MailConfig, MailDependfrom sillo_contrib.tasks import setup_tasksimport os
app = silloApp()
# Setup background taskstask_manager = setup_tasks(app)
# Setup mailconfig = MailConfig( smtp_host=os.getenv("SMTP_HOST"), smtp_port=int(os.getenv("SMTP_PORT")), smtp_username=os.getenv("SMTP_USERNAME"), smtp_password=os.getenv("SMTP_PASSWORD"), use_tls=True, default_from=os.getenv("MAIL_DEFAULT_FROM"), template_directory="templates/emails")mail_client = setup_mail(app, config=config)
@app.post("/send-welcome")async def send_welcome_email( request: Request, response: Response, mail_client: MailClient = MailDepend()): data = await request.json
result = await mail_client.send_template_email( to=data["email"], subject="Welcome to Our Platform!", template_name="welcome", context={ "name": data["name"], "email": data["email"], "company_name": "Acme Corp" } )
return response.json({ "success": result.success, "message_id": result.message_id, "error": result.error })
@app.post("/send-async")async def send_async_email( request: Request, response: Response): data = await request.json
task = await send_email_async( request=request, to=data["email"], subject=data["subject"], body=data["body"] )
return response.json({ "message": "Email queued for sending", "task_id": task.id if task else None })This comprehensive mail integration provides everything you need to add professional email functionality to your sillo applications with ease.