* Un-hardcode status message_id in test backend
For the test EmailBackend, get message ID's based on array position in
`mail.outbox`, so that tests can predict the message ID.
* Add a console backend for use in development
Adds an EmailBackend derived from both Anymail's test backend and
Django's console backend, to provide anymail statuses and signal
handling while printing messages to the console. For use during
development on localhost.
Closes#87
Use a default timeout of 30 seconds for all requests, and add a
REQUESTS_TIMEOUT Anymail setting to override.
(I'm making a judgement call that this is not a breaking change in the
real world, and not bumping the major version. Theoretically, it could
affect you if your network somehow takes >30s to connect to your ESP,
but eventually succeeds. If so, set REQUESTS_TIMEOUT to None to restore
the earlier behavior.)
Fixes#80.
Within an EmailAddress (previously ParsedEmail object), properties
now match Python 3.6 email.headerregistry.Address naming:
* .email --> .addr_spec
* .name --> .display_name
* .localpart --> .username
(Completes work started in 386668908423d1d4eade90cf7a21a546a1e96514;
this updates remaining uses of old names and removes them.)
Update internal-use ParsedEmail to be more like Python 3.6+
email.headerregistry.Address, and remove "internal use only"
recommendation.
(Prep for exposing inbound email headers in a convenient form.
Old names remain temporarily available for internal use;
should clean up at some point.)
SendGrid requires extra headers and metadata values be strings.
Anymail has always coerced int and float; this treats Python 2's
`long` integer type the same.
Fixes#74
* Change Anymail's test EmailBackend to collect sent messages in
django.core.mail.outbox, same as Django's own locmem EmailBackend.
(So Django's test runner will automatically clear accumulated mail
between test cases.)
* Rename EmailMessage `test_response` attr to `anymail_test_response`
to avoid conflicts, and record merged ESP send params in
new `anymail_send_params` attr.
* Add docs
Closes#36.
[RFC-5322 allows](https://tools.ietf.org/html/rfc5322#section-3.6.2)
multiple addresses in the From header.
Django's SMTP backend supports this, as a single comma-separated
string (*not* a list of strings like the recipient params):
from_email='one@example.com, two@example.com'
to=['one@example.com', 'two@example.com']
Both Mailgun and SparkPost support multiple From addresses
(and Postmark accepts them, though truncates to the first one
on their end). For compatibility with Django -- and because
Anymail attempts to support all ESP features -- Anymail now
allows multiple From addresses, too, for ESPs that support it.
Note: as a practical matter, deliverability with multiple
From addresses is pretty bad. (Google outright rejects them.)
This change also reworks Anymail's internal ParsedEmail object,
and approach to parsing addresses, for better consistency with
Django's SMTP backend and improved error messaging.
In particular, Django (and now Anymail) allows multiple email
addresses in a single recipient string:
to=['one@example.com', 'two@example.com, three@example.com']
len(to) == 2 # but there will be three recipients
Fixes#60
Issue a better error message if message.reply_to
is set to a single string.
(Would also like to do this for to, cc, and bcc,
but Django core EmailMessage.recipients is called
and stumbles over thoses cases before Anymail's
backend gets involved.)
Fixes#57
* **Future breaking change:**
Rename all Anymail backends to just `EmailBackend`,
matching Django's naming convention.
(E.g., switch to "anymail.backends.mailgun.EmailBackend"
rather than "anymail.backends.mailgun.MailgunBackend".)
The old names still work, but will issue a DeprecationWarning
and will be removed in some future release.
(Apologies for this change; the old naming convention was
a holdover from Djrill, and I wanted consistency with
other Django EmailBackends before hitting 1.0.)
Fixes#49.
SendGrid: update to v3 send API
**SendGrid:** **[possibly-breaking]** Update SendGrid backend to newer Web API v3. This should be a transparent change for most projects. Exceptions: if you use SendGrid username/password auth, esp_extra with "x-smtpapi", or multiple Reply-To addresses, please review the [porting notes](http://anymail.readthedocs.io/en/latest/esps/sendgrid/#sendgrid-v3-upgrade).
Closes#28
In BasePayload, ensure any Django ugettext_lazy
(or similar) are converted to real strings before
handing off to ESP code. This resolves problems where
calling code expects it can use lazy strings "anywhere",
but non-Django code (requests, ESP packages) don't
always handle them correctly.
* Add utils helpers for lazy objects (is_lazy, force_non_lazy*)
* Add lazy object handling to utils.Attachment
* Add lazy object handling converters to BasePayload attr
processing where appropriate. (This ends up varying by
the expected attribute type.)
Fixes#34.
Allow custom MAILGUN_SENDER_DOMAIN in Anymail
settings. (Replaces need to use global esp_extra.)
Improve docs to cover cases where this is needed.
(esp_extra sender_domain is still supported for
overriding individual messages.)
Fixes#26.
When using a stored template, SparkPost disallows
subject, text, and html. Django's EmailMessage default
empty strings are enough to provoke "Both content
object and template_id are specified" from SparkPost,
so remove them (if empty) when using stored templates.
Update docs and tests; add integration test for template_id.
Fixes#24
python-sparkpost generates a transmissions.send
payload which is now considered invalid by the API,
if you try to use both `cc` (or `bcc`) and the
`recipients` dict structure required for merge_data.
[Anymail had been generating that recipients dict
structure in all cases, for simplicity. Sometime
between 2016-06-07 and 2016-06-22, SparkPost
began rejecting that if it appeared in the `header_to`
constructed by python-sparkpost.]
Catch and re-raise requests.RequestException in
AnymailRequestsBackend.post_to_esp.
* AnymailRequestsAPIError is needed for proper
fail_silently handling.
* Retain original requests exception type, to avoid
breaking existing code that might look for specific
requests exceptions.
Closes#16
* Set to field when using merge_data
The `to` field is required even if providing recipient addresses in x-smtpapi. See https://sendgrid.com/docs/API_Reference/Web_API/mail.html#-send.
* Check data['to'] contains expected emails
* Add space for toname check
* Make `to` expected data contain email only
* Create generic TestBackend that simply collects
send parameters
* Change BackendSettingsTests to TestBackend,
and add some missing cases
* Add UnsupportedFeatureTests
* Replace repetitive per-backend SEND_DEFAULTS
test cases with single (and more comprehensive)
SendDefaultsTests
* Merge esp_extra with Mandrill send payload
* Handle pythonic forms of `recipient_metadata`
and `template_content` in esp_extra
* DeprecationWarning for Mandrill EmailMessage
attributes inherited from Djrill
* message.template_id to use ESP stored templates
* message.merge_data and merge_global_data
to supply per-recipient/global merge variables
(with or without an ESP stored template)
* When using per-recipient merge_data, tell ESP to use
batch send: individual message per "to" address.
(Mailgun does this automatically; SendGrid requires
using a different "to" field; Mandrill requires
`preserve_recipients=False`; Postmark doesn't
support *this type* of batch sending with merge data.)
* Allow message.from_email=None (must be set after
init) and message.subject=None to suppress those
fields in API calls (for ESPs that allow "From" and
"Subject" in their template definitions).
Mailgun:
* Emulate merge_global_data by copying to
recipient-variables for each recipient.
SendGrid:
* Add delimiters to merge field names via
esp_extra['merge_field_format'] or
ANYMAIL_SENDGRID_MERGE_FIELD_FORMAT setting.
Mandrill:
* Remove Djrill versions of these features;
update migration notes.
Closes#5.
Previously, setting esp_extra['x-smtpapi']['filters']
would override the entire filters setting, potentially
undoing other Anymail options that use SendGrid
filters (like track_opens).
Now, 'filters' is special-cased, and merged with
any other Anymail filter options.
(We don't do a fully deep merge, because otherwise
there would be no way to use esp_extra to *clear*
Anymail settings.)
* Update utils.get_anymail_setting to support
kwargs override of django.conf.settings values
* Use the updated version everywhere
* Switch from ImproperlyConfigured to
AnymailConfigurationError exception
(anticipates feature_wehooks change)
Closes#12
Treat Mailgun metadata like all other ESPs: simple
key-value dict, where values are strings. If you want
to store JSON in metadata, you should serialize and
deserialize it yourself.
* Add smtp-id in unique_args (metadata), to ensure
it shows up in click and open events.
* Add SENDGRID_GENERATE_MESSAGE_ID setting,
default True, to control auto-Message-ID behavior.
* Document it.
Simplify install to just `pip install django-anymail`.
(Rather than `... django-anymail[mailgun]`
All of the ESPs so far require requests, so just move
that into the base requirements. (Chances are your
Django app already needs requests for some other
reason, anyway.)
Truly unique ESP dependencies (e.g., boto for
AWS-SES) could still use the setup extra features
mechanism.
Interpret dates and naive datetimes as Django's
current_timezone (rather than UTC like Djrill did).
This should be more likely to behave as expected
when running with a non-UTC TIME_ZONE setting.
(Prep for installing backends as package extras)
* Extract AnymailRequestsBackend and RequestsPayload
to base_requests.py
* Don't define/require requests exceptions when requests
not available
For MANDRILL_API_KEY (e.g.,), look for these settings:
* ANYMAIL = { 'MANDRILL_API_KEY': '...' }
* ANYMAIL_MANDRILL_API_KEY = "..."
* MANDRILL_API_KEY = "..."
(the "bare" third version is used only for settings that
might be reasonably shared with other apps, like api keys)