$ show anymail/django-anymail
django-anymail is a Django library that provides a unified interface for sending transactional email through multiple Email Service Providers (ESPs) such as Amazon SES, Mailgun, SendGrid, Postmark, SparkPost, Mailjet, Brevo, Resend, Mailtrap, Postal, Scaleway, Unisender Go, and MailerSend. It extends Django's built-in email framework with ESP-specific features like tags, metadata, inline images, merge data, templates, and click/open tracking. The library also handles inbound email and tracking webhooks, normalizing ESP-specific events into Django signals. Architecture follows a backend pattern: each ESP has a concrete EmailBackend class that implements `build_message_payload` and `post_to_esp`, with common logic in base classes (`AnymailRequestsBackend`, `BasePayload`). Webhook views are similarly structured with base classes for signature validation and event dispatch. The codebase is well-tested with both mock unit tests and live integration tests run weekly against real ESP APIs. It is a mature, actively maintained library with a clean separation of concerns and extensive documentation.
The LLM identified 8 files worth highlighting for reuse:
Provides reusable utility classes and functions for email handling: `EmailAddress` for safe display name and address formatting with IDNA/RFC2047 encoding, `Attachment` for normalizing various attachment formats (tuples, MIME parts, Django EmailAttachment), `parse_address_list` for robust address parsing, `merge_dicts_*` functions for merging metadata/headers with UNSET sentinel support, and `get_anymail_setting` for flexible Django settings retrieval. These are self-contained and could be extracted into a standalone email utilities library.
Implements `AnymailRequestsBackend` and `RequestsPayload` as a reusable pattern for building HTTP-based email backends. Handles session management, timeouts, debug logging, JSON response parsing, and error handling. The pattern is documented in `ADDING_ESPS.md` as the recommended way to add new ESP support. Demonstrates clean separation of payload construction and HTTP transport.
Provides `AnymailBaseWebhookView` and `AnymailCoreWebhookView` as reusable base classes for ESP webhook handling. Includes CSRF exemption, optional basic auth, request validation chain, and normalized signal dispatch for tracking and inbound events. The pattern is extended by all ESP-specific webhook views and demonstrates a robust approach to webhook security and event normalization.
Defines the normalized event system for Anymail: `EventType` and `RejectReason` enums, `AnymailTrackingEvent` and `AnymailInboundEvent` data classes, and Django signals (`tracking`, `inbound`, `pre_send`, `post_send`). This module is self-contained and provides a clean abstraction for ESP event handling that could be reused in other email-related projects.
Implements `AnymailInboundMessage`, a subclass of Python's `email.message.EmailMessage`, with convenience properties (`text`, `html`, `inlines`, `content_id_map`, `envelope_sender`) and construction methods (`construct`, `construct_attachment`) for building messages from ESP-parsed fields. Handles attachment decoding, content-ID mapping, and charset detection. Demonstrates advanced MIME handling and could be extracted as a standalone inbound email parser.
Defines a clean exception hierarchy for email sending: `AnymailError` base, with `AnymailAPIError`, `AnymailConfigurationError`, `AnymailUnsupportedFeature`, `AnymailSerializationError`, `AnymailRecipientsRefused`, `AnymailInvalidAddress`, `AnymailCancelSend`, and `AnymailWebhookValidationFailure`. Includes `describe_response` for API error formatting. This is a well-designed exception pattern that could be reused in any API client library.
Implements Django system checks for validating Anymail configuration: warns about deprecated backends (SendGrid, SendinBlue), insecure webhook settings (missing `WEBHOOK_SECRET`), and unsupported features. Demonstrates a good pattern for runtime configuration validation that could be applied to other Django apps.
Provides an abstraction layer for IDNA encoding with support for idna2008, idna2003, UTS-46, and custom encoders. Handles Python version compatibility and allows runtime configuration via `IDNA_ENCODER` setting. This is a self-contained module that solves a common internationalization problem and could be reused in any project needing flexible IDNA encoding.