Skip to content

Commit

Permalink
Confirm support for Django 4.1
Browse files Browse the repository at this point in the history
Replaces deprecated `django.utils.timezone.utc` with
`datetime.timezone.utc` (available since Python 3.2).
  • Loading branch information
tim-schilling committed Aug 24, 2022
1 parent 6b3775a commit a4f50c4
Show file tree
Hide file tree
Showing 25 changed files with 80 additions and 89 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ Features
* Support customizing the requests.Session for requests-based backends,
and document how this can be used to mount an adapter that simplifies
automatic retry logic. (Thanks to `@dgilmanAIDENTIFIED`_.)
* Confirm support for Django 4.1 and resolve deprecation warning regarding
``django.utils.timezone.utc``.

Fixes
~~~~~
Expand Down
6 changes: 3 additions & 3 deletions anymail/backends/base.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import json
from datetime import date, datetime
from datetime import date, datetime, timezone

from django.conf import settings
from django.core.mail.backends.base import BaseEmailBackend
from django.utils.timezone import is_naive, get_current_timezone, make_aware, utc
from django.utils.timezone import is_naive, get_current_timezone, make_aware
from requests.structures import CaseInsensitiveDict

from ..exceptions import (
Expand Down Expand Up @@ -394,7 +394,7 @@ def aware_datetime(self, value):
dt = datetime(value.year, value.month, value.day) # naive, midnight
else:
try:
dt = datetime.utcfromtimestamp(value).replace(tzinfo=utc)
dt = datetime.utcfromtimestamp(value).replace(tzinfo=timezone.utc)
except (TypeError, ValueError):
return value
if is_naive(dt):
Expand Down
10 changes: 5 additions & 5 deletions anymail/webhooks/mailgun.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import json
from datetime import datetime
from datetime import datetime, timezone

import hashlib
import hmac
from django.utils.crypto import constant_time_compare
from django.utils.timezone import utc

from .base import AnymailBaseWebhookView
from ..exceptions import AnymailConfigurationError, AnymailWebhookValidationFailure, AnymailInvalidAddress
Expand Down Expand Up @@ -115,7 +114,7 @@ def esp_to_anymail_event(self, esp_event):
recipient = event_data.get('recipient')

try:
timestamp = datetime.fromtimestamp(float(event_data['timestamp']), tz=utc)
timestamp = datetime.fromtimestamp(float(event_data['timestamp']), tz=timezone.utc)
except KeyError:
timestamp = None

Expand Down Expand Up @@ -213,7 +212,8 @@ def mailgun_legacy_to_anymail_event(self, esp_event):
"to Anymail's Mailgun *tracking* webhook URL.")

event_type = self.legacy_event_types.get(esp_event.getfirst('event'), EventType.UNKNOWN)
timestamp = datetime.fromtimestamp(int(esp_event['timestamp']), tz=utc) # use *last* value of timestamp
timestamp = datetime.fromtimestamp(
int(esp_event['timestamp']), tz=timezone.utc) # use *last* value of timestamp
# Message-Id is not documented for every event, but seems to always be included.
# (It's sometimes spelled as 'message-id', lowercase, and missing the <angle-brackets>.)
message_id = esp_event.getfirst('Message-Id', None) or esp_event.getfirst('message-id', None)
Expand Down Expand Up @@ -381,7 +381,7 @@ def esp_to_anymail_event(self, request):

return AnymailInboundEvent(
event_type=EventType.INBOUND,
timestamp=datetime.fromtimestamp(int(request.POST['timestamp']), tz=utc),
timestamp=datetime.fromtimestamp(int(request.POST['timestamp']), tz=timezone.utc),
event_id=request.POST.get('token', None),
esp_event=esp_event,
message=message,
Expand Down
5 changes: 2 additions & 3 deletions anymail/webhooks/mailjet.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import json
from datetime import datetime
from datetime import datetime, timezone

from django.utils.timezone import utc

from .base import AnymailBaseWebhookView
from ..inbound import AnymailInboundMessage
Expand Down Expand Up @@ -68,7 +67,7 @@ def esp_to_anymail_event(self, esp_event):
event_type = EventType.DEFERRED

try:
timestamp = datetime.fromtimestamp(esp_event['time'], tz=utc)
timestamp = datetime.fromtimestamp(esp_event['time'], tz=timezone.utc)
except (KeyError, ValueError):
timestamp = None

Expand Down
7 changes: 3 additions & 4 deletions anymail/webhooks/mandrill.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import json
from datetime import datetime
from datetime import datetime, timezone

import hashlib
import hmac
from base64 import b64encode
from django.utils.crypto import constant_time_compare
from django.utils.timezone import utc

from .base import AnymailBaseWebhookView, AnymailCoreWebhookView
from ..exceptions import AnymailWebhookValidationFailure
Expand Down Expand Up @@ -109,7 +108,7 @@ def mandrill_tracking_to_anymail_event(self, esp_event):
event_type = self.event_types.get(esp_type, EventType.UNKNOWN)

try:
timestamp = datetime.fromtimestamp(esp_event['ts'], tz=utc)
timestamp = datetime.fromtimestamp(esp_event['ts'], tz=timezone.utc)
except (KeyError, ValueError):
timestamp = None

Expand Down Expand Up @@ -170,7 +169,7 @@ def mandrill_inbound_to_anymail_event(self, esp_event):
message.spam_score = esp_event['msg'].get('spam_report', {}).get('score', None)

try:
timestamp = datetime.fromtimestamp(esp_event['ts'], tz=utc)
timestamp = datetime.fromtimestamp(esp_event['ts'], tz=timezone.utc)
except (KeyError, ValueError):
timestamp = None

Expand Down
6 changes: 2 additions & 4 deletions anymail/webhooks/postal.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import binascii
import json
from base64 import b64decode
from datetime import datetime
from datetime import datetime, timezone


from django.utils.timezone import utc

from .base import AnymailBaseWebhookView
from ..exceptions import (
AnymailInvalidAddress,
Expand Down Expand Up @@ -94,7 +92,7 @@ def parse_events(self, request):

raw_timestamp = esp_event.get("timestamp")
timestamp = (
datetime.fromtimestamp(int(raw_timestamp), tz=utc)
datetime.fromtimestamp(int(raw_timestamp), tz=timezone.utc)
if raw_timestamp
else None
)
Expand Down
5 changes: 2 additions & 3 deletions anymail/webhooks/sendgrid.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import json
from datetime import datetime
from datetime import datetime, timezone
from email.parser import BytesParser
from email.policy import default as default_policy

from django.utils.timezone import utc

from .base import AnymailBaseWebhookView
from ..inbound import AnymailInboundMessage
Expand Down Expand Up @@ -47,7 +46,7 @@ def parse_events(self, request):
def esp_to_anymail_event(self, esp_event):
event_type = self.event_types.get(esp_event['event'], EventType.UNKNOWN)
try:
timestamp = datetime.fromtimestamp(esp_event['timestamp'], tz=utc)
timestamp = datetime.fromtimestamp(esp_event['timestamp'], tz=timezone.utc)
except (KeyError, ValueError):
timestamp = None

Expand Down
6 changes: 2 additions & 4 deletions anymail/webhooks/sendinblue.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import json
from datetime import datetime

from django.utils.timezone import utc
from datetime import datetime, timezone

from .base import AnymailBaseWebhookView
from ..signals import AnymailTrackingEvent, EventType, RejectReason, tracking
Expand Down Expand Up @@ -45,7 +43,7 @@ def esp_to_anymail_event(self, esp_event):
# SendinBlue supplies "ts", "ts_event" and "date" fields, which seem to be based on the
# timezone set in the account preferences (and possibly with inconsistent DST adjustment).
# "ts_epoch" is the only field that seems to be consistently UTC; it's in milliseconds
timestamp = datetime.fromtimestamp(esp_event["ts_epoch"] / 1000.0, tz=utc)
timestamp = datetime.fromtimestamp(esp_event["ts_epoch"] / 1000.0, tz=timezone.utc)
except (KeyError, ValueError):
timestamp = None

Expand Down
6 changes: 2 additions & 4 deletions anymail/webhooks/sparkpost.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import json
from base64 import b64decode
from datetime import datetime

from django.utils.timezone import utc
from datetime import datetime, timezone

from .base import AnymailBaseWebhookView
from ..exceptions import AnymailConfigurationError
Expand Down Expand Up @@ -128,7 +126,7 @@ def esp_to_anymail_event(self, event_class, event, raw_event):

event_type = self.event_types.get(event['type'], EventType.UNKNOWN)
try:
timestamp = datetime.fromtimestamp(int(event['timestamp']), tz=utc)
timestamp = datetime.fromtimestamp(int(event['timestamp']), tz=timezone.utc)
except (KeyError, TypeError, ValueError):
timestamp = None

Expand Down
5 changes: 2 additions & 3 deletions docs/sending/anymail_additions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -214,10 +214,9 @@ an :ref:`unsupported feature <unsupported-features>` error.

.. code-block:: python
from datetime import datetime, timedelta
from django.utils.timezone import utc
from datetime import datetime, timedelta, timezone
message.send_at = datetime.now(utc) + timedelta(hours=1)
message.send_at = datetime.now(timezone.utc) + timedelta(hours=1)
To avoid confusion, it's best to provide either an *aware*
`~datetime.datetime` (one that has its tzinfo set), or an
Expand Down
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ def long_description_from_readme(rst):
"Framework :: Django :: 3.1",
"Framework :: Django :: 3.2",
"Framework :: Django :: 4.0",
"Framework :: Django :: 4.1",
"Environment :: Web Environment",
],
long_description=long_description,
Expand Down
9 changes: 4 additions & 5 deletions tests/test_amazon_ses_inbound.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import json
from base64 import b64encode
from datetime import datetime
from datetime import datetime, timezone
from textwrap import dedent
from unittest.mock import ANY, patch

from django.test import tag
from django.utils.timezone import utc

from anymail.exceptions import AnymailAPIError, AnymailConfigurationError
from anymail.inbound import AnymailInboundMessage
Expand Down Expand Up @@ -131,7 +130,7 @@ def test_inbound_sns_utf8(self):
event = kwargs['event']
self.assertIsInstance(event, AnymailInboundEvent)
self.assertEqual(event.event_type, 'inbound')
self.assertEqual(event.timestamp, datetime(2018, 3, 30, 17, 21, 51, microsecond=636000, tzinfo=utc))
self.assertEqual(event.timestamp, datetime(2018, 3, 30, 17, 21, 51, microsecond=636000, tzinfo=timezone.utc))
self.assertEqual(event.event_id, "jili9m351il3gkburn7o2f0u6788stij94c8ld01")
self.assertIsInstance(event.message, AnymailInboundMessage)
self.assertEqual(event.esp_event, raw_ses_event)
Expand Down Expand Up @@ -185,7 +184,7 @@ def test_inbound_sns_base64(self):
event = kwargs['event']
self.assertIsInstance(event, AnymailInboundEvent)
self.assertEqual(event.event_type, 'inbound')
self.assertEqual(event.timestamp, datetime(2018, 3, 30, 17, 21, 51, microsecond=636000, tzinfo=utc))
self.assertEqual(event.timestamp, datetime(2018, 3, 30, 17, 21, 51, microsecond=636000, tzinfo=timezone.utc))
self.assertEqual(event.event_id, "jili9m351il3gkburn7o2f0u6788stij94c8ld01")
self.assertIsInstance(event.message, AnymailInboundMessage)
self.assertEqual(event.esp_event, raw_ses_event)
Expand Down Expand Up @@ -248,7 +247,7 @@ def test_inbound_s3(self):
event = kwargs['event']
self.assertIsInstance(event, AnymailInboundEvent)
self.assertEqual(event.event_type, 'inbound')
self.assertEqual(event.timestamp, datetime(2018, 3, 30, 17, 21, 51, microsecond=636000, tzinfo=utc))
self.assertEqual(event.timestamp, datetime(2018, 3, 30, 17, 21, 51, microsecond=636000, tzinfo=timezone.utc))
self.assertEqual(event.event_id, "fqef5sop459utgdf4o9lqbsv7jeo73pejig34301")
self.assertIsInstance(event.message, AnymailInboundMessage)
self.assertEqual(event.esp_event, raw_ses_event)
Expand Down
13 changes: 9 additions & 4 deletions tests/test_amazon_ses_webhooks.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import json
import warnings
from datetime import datetime
from datetime import datetime, timezone
from unittest.mock import ANY, patch

from django.test import SimpleTestCase, override_settings, tag
from django.utils.timezone import utc

from anymail.exceptions import AnymailConfigurationError, AnymailInsecureWebhookWarning
from anymail.signals import AnymailTrackingEvent
Expand Down Expand Up @@ -116,7 +115,10 @@ def test_bounce_event(self):
self.assertIsInstance(event, AnymailTrackingEvent)
self.assertEqual(event.event_type, "bounced")
self.assertEqual(event.esp_event, raw_ses_event)
self.assertEqual(event.timestamp, datetime(2018, 3, 26, 17, 58, 59, microsecond=675000, tzinfo=utc)) # SNS
self.assertEqual(
event.timestamp,
datetime(2018, 3, 26, 17, 58, 59, microsecond=675000, tzinfo=timezone.utc)
) # SNS
self.assertEqual(event.message_id, "00000138111222aa-33322211-cccc-cccc-cccc-ddddaaaa0680-000000")
self.assertEqual(event.event_id, "19ba9823-d7f2-53c1-860e-cb10e0d13dfc")
self.assertEqual(event.recipient, "jane@example.com")
Expand Down Expand Up @@ -264,7 +266,10 @@ def test_send_event(self):
self.assertIsInstance(event, AnymailTrackingEvent)
self.assertEqual(event.event_type, "sent")
self.assertEqual(event.esp_event, raw_ses_event)
self.assertEqual(event.timestamp, datetime(2018, 3, 26, 17, 58, 59, microsecond=675000, tzinfo=utc)) # SNS
self.assertEqual(
event.timestamp,
datetime(2018, 3, 26, 17, 58, 59, microsecond=675000, tzinfo=timezone.utc)
) # SNS
self.assertEqual(event.message_id, "7c191be45-e9aedb9a-02f9-4d12-a87d-dd0099a07f8a-000000")
self.assertEqual(event.event_id, "19ba9823-d7f2-53c1-860e-cb10e0d13dfc")
self.assertEqual(event.recipient, "recipient@example.com")
Expand Down
7 changes: 3 additions & 4 deletions tests/test_general_backend.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from datetime import datetime
from datetime import datetime, timezone
from email.mime.text import MIMEText

from django.core import mail
Expand All @@ -7,7 +7,6 @@
from django.test import SimpleTestCase
from django.test.utils import override_settings
from django.utils.functional import Promise
from django.utils.timezone import utc
from django.utils.translation import gettext_lazy

from anymail.backends.test import EmailBackend as TestBackend, TestPayload
Expand Down Expand Up @@ -134,7 +133,7 @@ class SendDefaultsTests(TestBackendTestCase):
'SEND_DEFAULTS': {
# This isn't an exhaustive list of Anymail message attrs; just one of each type
'metadata': {'global': 'globalvalue'},
'send_at': datetime(2016, 5, 12, 4, 17, 0, tzinfo=utc),
'send_at': datetime(2016, 5, 12, 4, 17, 0, tzinfo=timezone.utc),
'tags': ['globaltag'],
'template_id': 'my-template',
'track_clicks': True,
Expand All @@ -147,7 +146,7 @@ def test_send_defaults(self):
params = self.get_send_params()
# All these values came from ANYMAIL_SEND_DEFAULTS:
self.assertEqual(params['metadata'], {'global': 'globalvalue'})
self.assertEqual(params['send_at'], datetime(2016, 5, 12, 4, 17, 0, tzinfo=utc))
self.assertEqual(params['send_at'], datetime(2016, 5, 12, 4, 17, 0, tzinfo=timezone.utc))
self.assertEqual(params['tags'], ['globaltag'])
self.assertEqual(params['template_id'], 'my-template')
self.assertEqual(params['track_clicks'], True)
Expand Down
5 changes: 2 additions & 3 deletions tests/test_mailgun_inbound.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import json
from datetime import datetime
from datetime import datetime, timezone
from io import BytesIO
from textwrap import dedent
from unittest.mock import ANY

from django.test import override_settings, tag
from django.utils.timezone import utc

from anymail.exceptions import AnymailConfigurationError
from anymail.inbound import AnymailInboundMessage
Expand Down Expand Up @@ -62,7 +61,7 @@ def test_inbound_basics(self):
event = kwargs['event']
self.assertIsInstance(event, AnymailInboundEvent)
self.assertEqual(event.event_type, 'inbound')
self.assertEqual(event.timestamp, datetime(2016, 4, 21, 17, 55, 30, tzinfo=utc))
self.assertEqual(event.timestamp, datetime(2016, 4, 21, 17, 55, 30, tzinfo=timezone.utc))
self.assertEqual(event.event_id, "06c96bafc3f42a66b9edd546347a2fe18dc23461fe80dc52f0")
self.assertIsInstance(event.message, AnymailInboundMessage)
self.assertEqual(querydict_to_postdict(event.esp_event.POST), raw_event)
Expand Down
9 changes: 4 additions & 5 deletions tests/test_mailgun_webhooks.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import json
from datetime import datetime
from datetime import datetime, timezone
from unittest.mock import ANY

import hashlib
import hmac
from django.core.exceptions import ImproperlyConfigured
from django.test import override_settings, tag
from django.utils.timezone import utc

from anymail.exceptions import AnymailConfigurationError
from anymail.signals import AnymailTrackingEvent
Expand Down Expand Up @@ -197,7 +196,7 @@ def test_delivered_event(self):
event = kwargs['event']
self.assertIsInstance(event, AnymailTrackingEvent)
self.assertEqual(event.event_type, "delivered")
self.assertEqual(event.timestamp, datetime(2018, 8, 12, 21, 17, 17, microsecond=153125, tzinfo=utc))
self.assertEqual(event.timestamp, datetime(2018, 8, 12, 21, 17, 17, microsecond=153125, tzinfo=timezone.utc))
self.assertEqual(event.message_id, "<20180812211713.1.DF5966851B4BAA99@example.org>")
# Note that Anymail uses the "token" as its normalized event_id:
self.assertEqual(event.event_id, "651869375b9df3c98fc15c4889b102119add1235c38fc92824")
Expand Down Expand Up @@ -513,7 +512,7 @@ def test_delivered_event(self):
event = kwargs['event']
self.assertIsInstance(event, AnymailTrackingEvent)
self.assertEqual(event.event_type, "delivered")
self.assertEqual(event.timestamp, datetime(2016, 4, 21, 17, 55, 30, tzinfo=utc))
self.assertEqual(event.timestamp, datetime(2016, 4, 21, 17, 55, 30, tzinfo=timezone.utc))
self.assertEqual(event.message_id, "<20160421175529.19495.89030.B3AE3728@example.com>")
self.assertEqual(event.event_id, "06c96bafc3f42a66b9edd546347a2fe18dc23461fe80dc52f0")
self.assertEqual(event.recipient, "recipient@example.com")
Expand Down Expand Up @@ -552,7 +551,7 @@ def test_dropped_bounce(self):
event = kwargs['event']
self.assertIsInstance(event, AnymailTrackingEvent)
self.assertEqual(event.event_type, "rejected")
self.assertEqual(event.timestamp, datetime(2016, 4, 21, 17, 55, 30, tzinfo=utc))
self.assertEqual(event.timestamp, datetime(2016, 4, 21, 17, 55, 30, tzinfo=timezone.utc))
self.assertEqual(event.message_id, "<20160421180324.70521.79375.96884DDB@example.com>")
self.assertEqual(event.event_id, "a3fe1fa1640349ac552b84ddde373014b4c41645830c8dd3fc")
self.assertEqual(event.recipient, "bounce@example.com")
Expand Down

0 comments on commit a4f50c4

Please sign in to comment.