mirror of
https://github.com/pacnpal/django-anymail.git
synced 2025-12-20 11:51:05 -05:00
Drop Python 2 and Django 1.11 support
Minimum supported versions are now Django 2.0, Python 3.5. This touches a lot of code, to: * Remove obsolete portability code and workarounds (six, backports of email parsers, test utils, etc.) * Use Python 3 syntax (class defs, raise ... from, etc.) * Correct inheritance for mixin classes * Fix outdated docs content and links * Suppress Python 3 "unclosed SSLSocket" ResourceWarnings that are beyond our control (in integration tests due to boto3, python-sparkpost)
This commit is contained in:
@@ -26,18 +26,18 @@ The first approach is usually the simplest. The other two can be
|
||||
helpful if you are working with Python development tools that
|
||||
offer type checking or other static code analysis.
|
||||
|
||||
Availability of these features varies by ESP, and there may be additional
|
||||
limitations even when an ESP does support a particular feature. Be sure
|
||||
to check Anymail's docs for your :ref:`specific ESP <supported-esps>`.
|
||||
If you try to use a feature your ESP does not offer, Anymail will raise
|
||||
an :ref:`unsupported feature <unsupported-features>` error.
|
||||
|
||||
|
||||
.. _anymail-send-options:
|
||||
|
||||
ESP send options (AnymailMessage)
|
||||
---------------------------------
|
||||
|
||||
Availability of each of these features varies by ESP, and there may be additional
|
||||
limitations even when an ESP does support a particular feature. Be sure
|
||||
to check Anymail's docs for your :ref:`specific ESP <supported-esps>`.
|
||||
If you try to use a feature your ESP does not offer, Anymail will raise
|
||||
an :ref:`unsupported feature <unsupported-features>` error.
|
||||
|
||||
.. class:: AnymailMessage
|
||||
|
||||
A subclass of Django's :class:`~django.core.mail.EmailMultiAlternatives`
|
||||
@@ -167,7 +167,7 @@ ESP send options (AnymailMessage)
|
||||
|
||||
ESPs have differing restrictions on tags. For portability,
|
||||
it's best to stick with strings that start with an alphanumeric
|
||||
character. (Also, Postmark only allows a single tag per message.)
|
||||
character. (Also, a few ESPs allow only a single tag per message.)
|
||||
|
||||
|
||||
.. caution::
|
||||
@@ -359,7 +359,7 @@ ESP send status
|
||||
* `'queued'` the ESP has accepted the message
|
||||
and will try to send it asynchronously
|
||||
* `'invalid'` the ESP considers the sender or recipient email invalid
|
||||
* `'rejected'` the recipient is on an ESP blacklist
|
||||
* `'rejected'` the recipient is on an ESP suppression list
|
||||
(unsubscribe, previous bounces, etc.)
|
||||
* `'failed'` the attempt to send failed for some other reason
|
||||
* `'unknown'` anything else
|
||||
@@ -402,7 +402,8 @@ ESP send status
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# This will work with a requests-based backend:
|
||||
# This will work with a requests-based backend,
|
||||
# for an ESP whose send API provides a JSON response:
|
||||
message.anymail_status.esp_response.json()
|
||||
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ email using Django's default SMTP :class:`~django.core.mail.backends.smtp.EmailB
|
||||
switching to Anymail will be easy. Anymail is designed to "just work" with Django.
|
||||
|
||||
If you're not familiar with Django's email functions, please take a look at
|
||||
":mod:`sending email <django.core.mail>`" in the Django docs first.
|
||||
:doc:`django:topics/email` in the Django docs first.
|
||||
|
||||
Anymail supports most of the functionality of Django's :class:`~django.core.mail.EmailMessage`
|
||||
and :class:`~django.core.mail.EmailMultiAlternatives` classes.
|
||||
@@ -39,8 +39,8 @@ function with the ``html_message`` parameter:
|
||||
send_mail("Subject", "text body", "from@example.com",
|
||||
["to@example.com"], html_message="<html>html body</html>")
|
||||
|
||||
However, many Django email capabilities -- and additional Anymail features --
|
||||
are only available when working with an :class:`~django.core.mail.EmailMultiAlternatives`
|
||||
However, many Django email capabilities---and additional Anymail features---are only
|
||||
available when working with an :class:`~django.core.mail.EmailMultiAlternatives`
|
||||
object. Use its :meth:`~django.core.mail.EmailMultiAlternatives.attach_alternative`
|
||||
method to send HTML:
|
||||
|
||||
@@ -168,7 +168,8 @@ raise :exc:`~exceptions.AnymailUnsupportedFeature`.
|
||||
.. setting:: ANYMAIL_IGNORE_UNSUPPORTED_FEATURES
|
||||
|
||||
If you'd like to silently ignore :exc:`~exceptions.AnymailUnsupportedFeature`
|
||||
errors and send the messages anyway, set :setting:`!ANYMAIL_IGNORE_UNSUPPORTED_FEATURES`
|
||||
errors and send the messages anyway, set
|
||||
:setting:`"IGNORE_UNSUPPORTED_FEATURES" <ANYMAIL_IGNORE_UNSUPPORTED_FEATURES>`
|
||||
to `True` in your settings.py:
|
||||
|
||||
.. code-block:: python
|
||||
@@ -197,15 +198,16 @@ If a single message is sent to multiple recipients, and *any* recipient is valid
|
||||
You can still examine the message's :attr:`~message.AnymailMessage.anymail_status`
|
||||
property after the send to determine the status of each recipient.
|
||||
|
||||
You can disable this exception by setting :setting:`ANYMAIL_IGNORE_RECIPIENT_STATUS`
|
||||
to `True` in your settings.py, which will cause Anymail to treat any non-API-error response
|
||||
from your ESP as a successful send.
|
||||
You can disable this exception by setting
|
||||
:setting:`"IGNORE_RECIPIENT_STATUS" <ANYMAIL_IGNORE_RECIPIENT_STATUS>` to `True` in
|
||||
your settings.py `ANYMAIL` dict, which will cause Anymail to treat *any*
|
||||
response from your ESP (other than an API error) as a successful send.
|
||||
|
||||
.. note::
|
||||
|
||||
Many ESPs don't check recipient status during the send API call. For example,
|
||||
Most ESPs don't check recipient status during the send API call. For example,
|
||||
Mailgun always queues sent messages, so you'll never catch
|
||||
:exc:`AnymailRecipientsRefused` with the Mailgun backend.
|
||||
|
||||
For those ESPs, use Anymail's :ref:`delivery event tracking <event-tracking>`
|
||||
if you need to be notified of sends to blacklisted or invalid emails.
|
||||
You can use Anymail's :ref:`delivery event tracking <event-tracking>`
|
||||
if you need to be notified of sends to suppression-listed or invalid emails.
|
||||
|
||||
@@ -211,7 +211,7 @@ for use as merge data:
|
||||
# Do something this instead:
|
||||
message.merge_global_data = {
|
||||
'PRODUCT': product.name, # assuming name is a CharField
|
||||
'TOTAL_COST': "%.2f" % total_cost,
|
||||
'TOTAL_COST': "{cost:0.2f}".format(cost=total_cost),
|
||||
'SHIP_DATE': ship_date.strftime('%B %d, %Y') # US-style "March 15, 2015"
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ Webhook support is optional. If you haven't yet, you'll need to
|
||||
project. (You may also want to review :ref:`securing-webhooks`.)
|
||||
|
||||
Once you've enabled webhooks, Anymail will send an ``anymail.signals.tracking``
|
||||
custom Django :mod:`signal <django.dispatch>` for each ESP tracking event it receives.
|
||||
custom Django :doc:`signal <django:topics/signals>` for each ESP tracking event it receives.
|
||||
You can connect your own receiver function to this signal for further processing.
|
||||
|
||||
Be sure to read Django's `listening to signals`_ docs for information on defining
|
||||
@@ -40,7 +40,7 @@ Example:
|
||||
event.recipient, event.click_url))
|
||||
|
||||
You can define individual signal receivers, or create one big one for all
|
||||
event types, which ever you prefer. You can even handle the same event
|
||||
event types, whichever you prefer. You can even handle the same event
|
||||
in multiple receivers, if that makes your code cleaner. These
|
||||
:ref:`signal receiver functions <signal-receivers>` are documented
|
||||
in more detail below.
|
||||
@@ -189,8 +189,8 @@ Normalized tracking event
|
||||
.. attribute:: mta_response
|
||||
|
||||
If available, a `str` with a raw (intended for email administrators) response
|
||||
from the receiving MTA. Otherwise `None`. Often includes SMTP response codes,
|
||||
but the exact format varies by ESP (and sometimes receiving MTA).
|
||||
from the receiving mail transfer agent. Otherwise `None`. Often includes SMTP
|
||||
response codes, but the exact format varies by ESP (and sometimes receiving MTA).
|
||||
|
||||
.. attribute:: user_agent
|
||||
|
||||
@@ -203,7 +203,7 @@ Normalized tracking event
|
||||
|
||||
.. attribute:: esp_event
|
||||
|
||||
The "raw" event data from the ESP, deserialized into a python data structure.
|
||||
The "raw" event data from the ESP, deserialized into a Python data structure.
|
||||
For most ESPs this is either parsed JSON (as a `dict`), or HTTP POST fields
|
||||
(as a Django :class:`~django.http.QueryDict`).
|
||||
|
||||
@@ -230,7 +230,7 @@ Your Anymail signal receiver must be a function with this signature:
|
||||
:param AnymailTrackingEvent event: The normalized tracking event.
|
||||
Almost anything you'd be interested in
|
||||
will be in here.
|
||||
:param str esp_name: e.g., "SendMail" or "Postmark". If you are working
|
||||
:param str esp_name: e.g., "SendGrid" or "Postmark". If you are working
|
||||
with multiple ESPs, you can use this to distinguish
|
||||
ESP-specific handling in your shared event processing.
|
||||
:param \**kwargs: Required by Django's signal mechanism
|
||||
@@ -259,7 +259,7 @@ And will retry sending the "failed" events, which could cause duplicate
|
||||
processing in your code.
|
||||
If your signal receiver code might be slow, you should instead
|
||||
queue the event for later, asynchronous processing (e.g., using
|
||||
something like `Celery`_).
|
||||
something like :pypi:`celery`).
|
||||
|
||||
If your signal receiver function is defined within some other
|
||||
function or instance method, you *must* use the `weak=False`
|
||||
@@ -268,7 +268,6 @@ but will unpredictably stop being called at some point---typically
|
||||
on your production server, in a hard-to-debug way. See Django's
|
||||
`listening to signals`_ docs for more information.
|
||||
|
||||
.. _Celery: http://www.celeryproject.org/
|
||||
.. _listening to signals:
|
||||
https://docs.djangoproject.com/en/stable/topics/signals/#listening-to-signals
|
||||
|
||||
|
||||
Reference in New Issue
Block a user