From a4f50c434063f03e6c516f7fe8c84f095c7b5d8f Mon Sep 17 00:00:00 2001 From: Tim Schilling Date: Wed, 24 Aug 2022 15:24:27 -0500 Subject: [PATCH] Confirm support for Django 4.1 Replaces deprecated `django.utils.timezone.utc` with `datetime.timezone.utc` (available since Python 3.2). --- CHANGELOG.rst | 2 ++ anymail/backends/base.py | 6 +++--- anymail/webhooks/mailgun.py | 10 +++++----- anymail/webhooks/mailjet.py | 5 ++--- anymail/webhooks/mandrill.py | 7 +++---- anymail/webhooks/postal.py | 6 ++---- anymail/webhooks/sendgrid.py | 5 ++--- anymail/webhooks/sendinblue.py | 6 ++---- anymail/webhooks/sparkpost.py | 6 ++---- docs/sending/anymail_additions.rst | 5 ++--- setup.py | 1 + tests/test_amazon_ses_inbound.py | 9 ++++----- tests/test_amazon_ses_webhooks.py | 13 +++++++++---- tests/test_general_backend.py | 7 +++---- tests/test_mailgun_inbound.py | 5 ++--- tests/test_mailgun_webhooks.py | 9 ++++----- tests/test_mailjet_webhooks.py | 7 +++---- tests/test_mandrill_webhooks.py | 5 ++--- tests/test_postal_webhooks.py | 13 ++++++------- tests/test_postmark_webhooks.py | 12 ++++++------ tests/test_sendgrid_webhooks.py | 7 +++---- tests/test_sendinblue_webhooks.py | 5 ++--- tests/test_sparkpost_backend.py | 6 +++--- tests/test_sparkpost_webhooks.py | 5 ++--- tox.ini | 7 +++++-- 25 files changed, 80 insertions(+), 89 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 7f092c3..816ef32 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -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 ~~~~~ diff --git a/anymail/backends/base.py b/anymail/backends/base.py index d8c639b..a9a860c 100644 --- a/anymail/backends/base.py +++ b/anymail/backends/base.py @@ -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 ( @@ -394,7 +394,7 @@ class BasePayload: 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): diff --git a/anymail/webhooks/mailgun.py b/anymail/webhooks/mailgun.py index 0eb50e1..6c9c1eb 100644 --- a/anymail/webhooks/mailgun.py +++ b/anymail/webhooks/mailgun.py @@ -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 @@ -115,7 +114,7 @@ class MailgunTrackingWebhookView(MailgunBaseWebhookView): 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 @@ -213,7 +212,8 @@ class MailgunTrackingWebhookView(MailgunBaseWebhookView): "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 .) message_id = esp_event.getfirst('Message-Id', None) or esp_event.getfirst('message-id', None) @@ -381,7 +381,7 @@ class MailgunInboundWebhookView(MailgunBaseWebhookView): 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, diff --git a/anymail/webhooks/mailjet.py b/anymail/webhooks/mailjet.py index c6cf0f8..678a68b 100644 --- a/anymail/webhooks/mailjet.py +++ b/anymail/webhooks/mailjet.py @@ -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 @@ -68,7 +67,7 @@ class MailjetTrackingWebhookView(AnymailBaseWebhookView): 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 diff --git a/anymail/webhooks/mandrill.py b/anymail/webhooks/mandrill.py index 25f2e0b..2c78f07 100644 --- a/anymail/webhooks/mandrill.py +++ b/anymail/webhooks/mandrill.py @@ -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 @@ -109,7 +108,7 @@ class MandrillCombinedWebhookView(MandrillSignatureMixin, AnymailBaseWebhookView 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 @@ -170,7 +169,7 @@ class MandrillCombinedWebhookView(MandrillSignatureMixin, AnymailBaseWebhookView 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 diff --git a/anymail/webhooks/postal.py b/anymail/webhooks/postal.py index e11d36e..b6d774d 100644 --- a/anymail/webhooks/postal.py +++ b/anymail/webhooks/postal.py @@ -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, @@ -94,7 +92,7 @@ class PostalTrackingWebhookView(PostalBaseWebhookView): 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 ) diff --git a/anymail/webhooks/sendgrid.py b/anymail/webhooks/sendgrid.py index f48a52b..999c230 100644 --- a/anymail/webhooks/sendgrid.py +++ b/anymail/webhooks/sendgrid.py @@ -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 @@ -47,7 +46,7 @@ class SendGridTrackingWebhookView(AnymailBaseWebhookView): 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 diff --git a/anymail/webhooks/sendinblue.py b/anymail/webhooks/sendinblue.py index aec3ddb..9e78f35 100644 --- a/anymail/webhooks/sendinblue.py +++ b/anymail/webhooks/sendinblue.py @@ -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 @@ -45,7 +43,7 @@ class SendinBlueTrackingWebhookView(AnymailBaseWebhookView): # 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 diff --git a/anymail/webhooks/sparkpost.py b/anymail/webhooks/sparkpost.py index 3501188..96ab99e 100644 --- a/anymail/webhooks/sparkpost.py +++ b/anymail/webhooks/sparkpost.py @@ -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 @@ -128,7 +126,7 @@ class SparkPostTrackingWebhookView(SparkPostBaseWebhookView): 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 diff --git a/docs/sending/anymail_additions.rst b/docs/sending/anymail_additions.rst index 7d39793..a754f81 100644 --- a/docs/sending/anymail_additions.rst +++ b/docs/sending/anymail_additions.rst @@ -214,10 +214,9 @@ an :ref:`unsupported feature ` 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 diff --git a/setup.py b/setup.py index 3f765b4..7c1f055 100644 --- a/setup.py +++ b/setup.py @@ -102,6 +102,7 @@ setup( "Framework :: Django :: 3.1", "Framework :: Django :: 3.2", "Framework :: Django :: 4.0", + "Framework :: Django :: 4.1", "Environment :: Web Environment", ], long_description=long_description, diff --git a/tests/test_amazon_ses_inbound.py b/tests/test_amazon_ses_inbound.py index 64d37c9..57f5184 100644 --- a/tests/test_amazon_ses_inbound.py +++ b/tests/test_amazon_ses_inbound.py @@ -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 @@ -131,7 +130,7 @@ class AmazonSESInboundTests(WebhookTestCase, AmazonSESWebhookTestsMixin): 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) @@ -185,7 +184,7 @@ class AmazonSESInboundTests(WebhookTestCase, AmazonSESWebhookTestsMixin): 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) @@ -248,7 +247,7 @@ class AmazonSESInboundTests(WebhookTestCase, AmazonSESWebhookTestsMixin): 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) diff --git a/tests/test_amazon_ses_webhooks.py b/tests/test_amazon_ses_webhooks.py index 467c434..f8772c7 100644 --- a/tests/test_amazon_ses_webhooks.py +++ b/tests/test_amazon_ses_webhooks.py @@ -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 @@ -116,7 +115,10 @@ class AmazonSESNotificationsTests(WebhookTestCase, AmazonSESWebhookTestsMixin): 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") @@ -264,7 +266,10 @@ class AmazonSESNotificationsTests(WebhookTestCase, AmazonSESWebhookTestsMixin): 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") diff --git a/tests/test_general_backend.py b/tests/test_general_backend.py index c1e3303..1112c39 100644 --- a/tests/test_general_backend.py +++ b/tests/test_general_backend.py @@ -1,4 +1,4 @@ -from datetime import datetime +from datetime import datetime, timezone from email.mime.text import MIMEText from django.core import mail @@ -7,7 +7,6 @@ from django.core.mail import get_connection, send_mail 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 @@ -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, @@ -147,7 +146,7 @@ class SendDefaultsTests(TestBackendTestCase): 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) diff --git a/tests/test_mailgun_inbound.py b/tests/test_mailgun_inbound.py index 3ba7dce..a52d48e 100644 --- a/tests/test_mailgun_inbound.py +++ b/tests/test_mailgun_inbound.py @@ -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 @@ -62,7 +61,7 @@ class MailgunInboundTestCase(WebhookTestCase): 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) diff --git a/tests/test_mailgun_webhooks.py b/tests/test_mailgun_webhooks.py index 663a788..cd076cc 100644 --- a/tests/test_mailgun_webhooks.py +++ b/tests/test_mailgun_webhooks.py @@ -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 @@ -197,7 +196,7 @@ class MailgunTestCase(WebhookTestCase): 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") @@ -513,7 +512,7 @@ class MailgunLegacyTestCase(WebhookTestCase): 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") @@ -552,7 +551,7 @@ class MailgunLegacyTestCase(WebhookTestCase): 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") diff --git a/tests/test_mailjet_webhooks.py b/tests/test_mailjet_webhooks.py index 4594e5e..ecf4ba2 100644 --- a/tests/test_mailjet_webhooks.py +++ b/tests/test_mailjet_webhooks.py @@ -1,9 +1,8 @@ import json -from datetime import datetime +from datetime import datetime, timezone from unittest.mock import ANY from django.test import tag -from django.utils.timezone import utc from anymail.signals import AnymailTrackingEvent from anymail.webhooks.mailjet import MailjetTrackingWebhookView @@ -44,7 +43,7 @@ class MailjetDeliveryTestCase(WebhookTestCase): event = kwargs['event'] self.assertIsInstance(event, AnymailTrackingEvent) self.assertEqual(event.event_type, "delivered") - self.assertEqual(event.timestamp, datetime(2017, 6, 22, 1, 5, 27, tzinfo=utc)) + self.assertEqual(event.timestamp, datetime(2017, 6, 22, 1, 5, 27, tzinfo=timezone.utc)) self.assertEqual(event.esp_event, raw_events[0]) self.assertEqual(event.mta_response, "sent (250 2.0.0 OK 1498093527 a67bc12345def.22 - gsmtp)") self.assertEqual(event.message_id, "12345678901234567") # converted to str (matching backend status) @@ -251,7 +250,7 @@ class MailjetDeliveryTestCase(WebhookTestCase): event = kwargs['event'] self.assertIsInstance(event, AnymailTrackingEvent) self.assertEqual(event.event_type, "delivered") - self.assertEqual(event.timestamp, datetime(2017, 6, 22, 1, 5, 27, tzinfo=utc)) + self.assertEqual(event.timestamp, datetime(2017, 6, 22, 1, 5, 27, tzinfo=timezone.utc)) self.assertEqual(event.esp_event, raw_event) self.assertEqual(event.mta_response, "sent (250 2.0.0 OK 1498093527 a67bc12345def.22 - gsmtp)") self.assertEqual(event.message_id, "12345678901234567") # converted to str (matching backend status) diff --git a/tests/test_mandrill_webhooks.py b/tests/test_mandrill_webhooks.py index a8677ba..1890e79 100644 --- a/tests/test_mandrill_webhooks.py +++ b/tests/test_mandrill_webhooks.py @@ -2,13 +2,12 @@ import hashlib import hmac import json from base64 import b64encode -from datetime import datetime +from datetime import datetime, timezone from unittest.mock import ANY from urllib.parse import urljoin from django.core.exceptions import ImproperlyConfigured from django.test import override_settings, tag -from django.utils.timezone import utc from anymail.signals import AnymailTrackingEvent from anymail.webhooks.mandrill import MandrillCombinedWebhookView, MandrillTrackingWebhookView @@ -166,7 +165,7 @@ class MandrillTrackingTestCase(WebhookTestCase): event = kwargs['event'] self.assertIsInstance(event, AnymailTrackingEvent) self.assertEqual(event.event_type, "sent") - self.assertEqual(event.timestamp, datetime(2016, 4, 19, 19, 47, 26, tzinfo=utc)) + self.assertEqual(event.timestamp, datetime(2016, 4, 19, 19, 47, 26, tzinfo=timezone.utc)) self.assertEqual(event.esp_event, raw_events[0]) self.assertEqual(event.message_id, "abcdef012345789abcdef012345789") self.assertEqual(event.recipient, "recipient@example.com") diff --git a/tests/test_postal_webhooks.py b/tests/test_postal_webhooks.py index 2eef98f..58a51f9 100644 --- a/tests/test_postal_webhooks.py +++ b/tests/test_postal_webhooks.py @@ -1,11 +1,10 @@ import json import unittest from base64 import b64encode -from datetime import datetime +from datetime import datetime, timezone from unittest.mock import ANY from django.test import tag -from django.utils.timezone import utc from anymail.exceptions import AnymailConfigurationError from anymail.signals import AnymailTrackingEvent @@ -100,7 +99,7 @@ class PostalDeliveryTestCase(WebhookTestCase): self.assertIsInstance(event, AnymailTrackingEvent) self.assertEqual(event.event_type, "bounced") self.assertEqual(event.esp_event, raw_event) - self.assertEqual(event.timestamp, datetime.fromtimestamp(1606753101, tz=utc)) + self.assertEqual(event.timestamp, datetime.fromtimestamp(1606753101, tz=timezone.utc)) self.assertEqual(event.message_id, 233843) self.assertEqual(event.event_id, "0fcc831f-92b9-4e2b-97f2-d873abc77fab") self.assertEqual(event.recipient, "bounce@example.com") @@ -145,7 +144,7 @@ class PostalDeliveryTestCase(WebhookTestCase): self.assertIsInstance(event, AnymailTrackingEvent) self.assertEqual(event.event_type, "deferred") self.assertEqual(event.esp_event, raw_event) - self.assertEqual(event.timestamp, datetime.fromtimestamp(1606753101, tz=utc)) + self.assertEqual(event.timestamp, datetime.fromtimestamp(1606753101, tz=timezone.utc)) self.assertEqual(event.message_id, 1564) self.assertEqual(event.event_id, "0fcc831f-92b9-4e2b-97f2-d873abc77fab") self.assertEqual(event.recipient, "deferred@example.com") @@ -190,7 +189,7 @@ class PostalDeliveryTestCase(WebhookTestCase): self.assertIsInstance(event, AnymailTrackingEvent) self.assertEqual(event.event_type, "queued") self.assertEqual(event.esp_event, raw_event) - self.assertEqual(event.timestamp, datetime.fromtimestamp(1606753101, tz=utc)) + self.assertEqual(event.timestamp, datetime.fromtimestamp(1606753101, tz=timezone.utc)) self.assertEqual(event.message_id, 1568) self.assertEqual(event.event_id, "9be13015-2e54-456c-bf66-eacbe33da824") self.assertEqual(event.recipient, "suppressed@example.com") @@ -235,7 +234,7 @@ class PostalDeliveryTestCase(WebhookTestCase): self.assertIsInstance(event, AnymailTrackingEvent) self.assertEqual(event.event_type, "failed") self.assertEqual(event.esp_event, raw_event) - self.assertEqual(event.timestamp, datetime.fromtimestamp(1606753101, tz=utc)) + self.assertEqual(event.timestamp, datetime.fromtimestamp(1606753101, tz=timezone.utc)) self.assertEqual(event.message_id, 1571) self.assertEqual(event.event_id, "5fec5077-dae7-4989-94d5-e1963f3e9181") self.assertEqual(event.recipient, "failed@example.com") @@ -280,7 +279,7 @@ class PostalDeliveryTestCase(WebhookTestCase): self.assertIsInstance(event, AnymailTrackingEvent) self.assertEqual(event.event_type, "delivered") self.assertEqual(event.esp_event, raw_event) - self.assertEqual(event.timestamp, datetime.fromtimestamp(1606753101, tz=utc)) + self.assertEqual(event.timestamp, datetime.fromtimestamp(1606753101, tz=timezone.utc)) self.assertEqual(event.message_id, 1563) self.assertEqual(event.recipient, "recipient@example.com") self.assertEqual(event.tags, ["welcome-email"]) diff --git a/tests/test_postmark_webhooks.py b/tests/test_postmark_webhooks.py index 22e935c..8128e3b 100644 --- a/tests/test_postmark_webhooks.py +++ b/tests/test_postmark_webhooks.py @@ -1,9 +1,9 @@ import json -from datetime import datetime +from datetime import datetime, timezone from unittest.mock import ANY from django.test import tag -from django.utils.timezone import get_fixed_timezone, utc +from django.utils.timezone import get_fixed_timezone from anymail.exceptions import AnymailConfigurationError from anymail.signals import AnymailTrackingEvent @@ -165,7 +165,7 @@ class PostmarkDeliveryTestCase(WebhookTestCase): self.assertEqual(event.event_type, "clicked") self.assertEqual(event.esp_event, raw_event) self.assertEqual(event.timestamp, datetime(2017, 10, 25, 15, 21, 11, microsecond=906561, - tzinfo=utc)) + tzinfo=timezone.utc)) self.assertEqual(event.message_id, "f4830d10-9c35-4f0c-bca3-3d9b459821f8") self.assertEqual(event.recipient, "recipient@example.com") self.assertEqual(event.user_agent, "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_5) etc.") @@ -247,7 +247,7 @@ class PostmarkDeliveryTestCase(WebhookTestCase): self.assertIsInstance(event, AnymailTrackingEvent) self.assertEqual(event.event_type, "unsubscribed") self.assertEqual(event.esp_event, raw_event) - self.assertEqual(event.timestamp, datetime(2022, 6, 5, 17, 17, 32, tzinfo=utc)) + self.assertEqual(event.timestamp, datetime(2022, 6, 5, 17, 17, 32, tzinfo=timezone.utc)) self.assertEqual(event.message_id, "a4909a96-73d7-4c49-b148-a54522d3f7ac") self.assertEqual(event.recipient, "john@example.com",) self.assertEqual(event.reject_reason, "unsubscribed") @@ -278,7 +278,7 @@ class PostmarkDeliveryTestCase(WebhookTestCase): self.assertIsInstance(event, AnymailTrackingEvent) self.assertEqual(event.event_type, "subscribed") self.assertEqual(event.esp_event, raw_event) - self.assertEqual(event.timestamp, datetime(2022, 6, 5, 17, 17, 32, tzinfo=utc)) + self.assertEqual(event.timestamp, datetime(2022, 6, 5, 17, 17, 32, tzinfo=timezone.utc)) self.assertEqual(event.message_id, "a4909a96-73d7-4c49-b148-a54522d3f7ac") self.assertEqual(event.recipient, "john@example.com",) self.assertEqual(event.reject_reason, None) @@ -309,7 +309,7 @@ class PostmarkDeliveryTestCase(WebhookTestCase): self.assertIsInstance(event, AnymailTrackingEvent) self.assertEqual(event.event_type, "bounced") self.assertEqual(event.esp_event, raw_event) - self.assertEqual(event.timestamp, datetime(2022, 6, 5, 17, 17, 32, tzinfo=utc)) + self.assertEqual(event.timestamp, datetime(2022, 6, 5, 17, 17, 32, tzinfo=timezone.utc)) self.assertEqual(event.message_id, "b4cb783d-78ed-43f2-983b-63f55c712dc8") self.assertEqual(event.recipient, "john@example.com",) self.assertEqual(event.reject_reason, "bounced") diff --git a/tests/test_sendgrid_webhooks.py b/tests/test_sendgrid_webhooks.py index e1987ea..f710fb6 100644 --- a/tests/test_sendgrid_webhooks.py +++ b/tests/test_sendgrid_webhooks.py @@ -1,9 +1,8 @@ import json -from datetime import datetime +from datetime import datetime, timezone from unittest.mock import ANY from django.test import tag -from django.utils.timezone import utc from anymail.signals import AnymailTrackingEvent from anymail.webhooks.sendgrid import SendGridTrackingWebhookView @@ -43,7 +42,7 @@ class SendGridDeliveryTestCase(WebhookTestCase): event = kwargs['event'] self.assertIsInstance(event, AnymailTrackingEvent) self.assertEqual(event.event_type, "queued") - self.assertEqual(event.timestamp, datetime(2016, 4, 19, 19, 47, 26, tzinfo=utc)) + self.assertEqual(event.timestamp, datetime(2016, 4, 19, 19, 47, 26, tzinfo=timezone.utc)) self.assertEqual(event.esp_event, raw_events[0]) self.assertEqual(event.message_id, "3c2f4df8-c6dd-4cd2-9b91-6582b81a0349") self.assertEqual(event.event_id, "ZyjAM5rnQmuI1KFInHQ3Nw") @@ -72,7 +71,7 @@ class SendGridDeliveryTestCase(WebhookTestCase): event = kwargs['event'] self.assertIsInstance(event, AnymailTrackingEvent) self.assertEqual(event.event_type, "delivered") - self.assertEqual(event.timestamp, datetime(2016, 4, 19, 19, 47, 30, tzinfo=utc)) + self.assertEqual(event.timestamp, datetime(2016, 4, 19, 19, 47, 30, tzinfo=timezone.utc)) self.assertEqual(event.esp_event, raw_events[0]) self.assertEqual(event.message_id, "4ab185c2-0171-492f-9ce0-27de258efc99") self.assertEqual(event.event_id, "nOSv8m0eTQ-vxvwNwt3fZQ") diff --git a/tests/test_sendinblue_webhooks.py b/tests/test_sendinblue_webhooks.py index dc902df..02a0f89 100644 --- a/tests/test_sendinblue_webhooks.py +++ b/tests/test_sendinblue_webhooks.py @@ -1,9 +1,8 @@ import json -from datetime import datetime +from datetime import datetime, timezone from unittest.mock import ANY from django.test import tag -from django.utils.timezone import utc from anymail.signals import AnymailTrackingEvent from anymail.webhooks.sendinblue import SendinBlueTrackingWebhookView @@ -60,7 +59,7 @@ class SendinBlueDeliveryTestCase(WebhookTestCase): self.assertIsInstance(event, AnymailTrackingEvent) self.assertEqual(event.event_type, "queued") self.assertEqual(event.esp_event, raw_event) - self.assertEqual(event.timestamp, datetime(2018, 3, 6, 19, 10, 23, microsecond=0, tzinfo=utc)) + self.assertEqual(event.timestamp, datetime(2018, 3, 6, 19, 10, 23, microsecond=0, tzinfo=timezone.utc)) self.assertEqual(event.message_id, "<201803062010.27287306012@smtp-relay.mailin.fr>") self.assertIsNone(event.event_id) # SendinBlue does not provide a unique event id self.assertEqual(event.recipient, "recipient@example.com") diff --git a/tests/test_sparkpost_backend.py b/tests/test_sparkpost_backend.py index 7474bc9..4ccca55 100644 --- a/tests/test_sparkpost_backend.py +++ b/tests/test_sparkpost_backend.py @@ -1,5 +1,5 @@ import json -from datetime import date, datetime +from datetime import date, datetime, timezone from decimal import Decimal from email.mime.base import MIMEBase from email.mime.image import MIMEImage @@ -7,7 +7,7 @@ from email.mime.text import MIMEText from django.core import mail from django.test import override_settings, tag -from django.utils.timezone import get_fixed_timezone, override as override_current_timezone, utc +from django.utils.timezone import get_fixed_timezone, override as override_current_timezone from anymail.exceptions import ( AnymailAPIError, AnymailConfigurationError, AnymailRecipientsRefused, @@ -370,7 +370,7 @@ class SparkPostBackendAnymailFeatureTests(SparkPostBackendMockAPITestCase): self.assertEqual(data["options"]["start_time"], "2016-03-04T05:06:07-08:00") # Explicit UTC: - self.message.send_at = datetime(2016, 3, 4, 5, 6, 7, tzinfo=utc) + self.message.send_at = datetime(2016, 3, 4, 5, 6, 7, tzinfo=timezone.utc) self.message.send() data = self.get_api_call_json() self.assertEqual(data["options"]["start_time"], "2016-03-04T05:06:07+00:00") diff --git a/tests/test_sparkpost_webhooks.py b/tests/test_sparkpost_webhooks.py index 9e8a174..454990a 100644 --- a/tests/test_sparkpost_webhooks.py +++ b/tests/test_sparkpost_webhooks.py @@ -1,9 +1,8 @@ import json -from datetime import datetime +from datetime import datetime, timezone from unittest.mock import ANY from django.test import override_settings, tag -from django.utils.timezone import utc from anymail.signals import AnymailTrackingEvent from anymail.webhooks.sparkpost import SparkPostTrackingWebhookView @@ -73,7 +72,7 @@ class SparkPostDeliveryTestCase(WebhookTestCase): event = kwargs['event'] self.assertIsInstance(event, AnymailTrackingEvent) self.assertEqual(event.event_type, "queued") - self.assertEqual(event.timestamp, datetime(2016, 2, 2, 19, 50, 00, tzinfo=utc)) + self.assertEqual(event.timestamp, datetime(2016, 2, 2, 19, 50, 00, tzinfo=timezone.utc)) self.assertEqual(event.esp_event, raw_events[0]) self.assertEqual(event.message_id, "65832150921904138") # actually transmission_id self.assertEqual(event.event_id, "92356927693813856") diff --git a/tox.ini b/tox.ini index 76d30d5..6e53e72 100644 --- a/tox.ini +++ b/tox.ini @@ -5,12 +5,14 @@ envlist = # Factors: django-python-extras # Test lint, docs, earliest/latest Django first, to catch most errors early... lint - django40-py310-all + django41-py310-all django20-py35-all docs # ... then test all the other supported combinations: + # Django 4.1: Python 3.8, 3.9, 3.10 + django41-py{38,39,py38}-all # Django 4.0: Python 3.8, 3.9, 3.10 - django40-py{38,39,py38}-all + django40-py{38,39,310,py38}-all # Django 3.2: Python 3.6, 3.7, 3.8, 3.9 django32-py{36,37,38,39,py38}-all # Django 3.1: Python 3.6, 3.7, 3.8, 3.9 (added in 3.1.3) @@ -39,6 +41,7 @@ deps = django31: django~=3.1.0 django32: django~=3.2.0 django40: django~=4.0.0 + django41: django~=4.1.0 djangoDev: https://github.com/django/django/tarball/main old_urllib3: urllib3<1.25 extras =