Repository: mozmeao/basket

Issue:
Upgrade to Python 3.9
As part of updating all MEAO's codebases, we should move Basket from Python 3.8 to 3.9 (or even 3.10 if viable)

### Success critera
- [ ] all unit tests passing on 3.9
- [ ] manual test-drive OK on 3.9
- [ ] no new 3.9-related regressions in Sentry first 24 hours after release


Discussion:

Resolving patch:
diff --git a/basket/news/migrations/0025_auto_20220308_1803.py b/basket/news/migrations/0025_auto_20220308_1803.py
new file mode 100644
--- /dev/null
+++ b/basket/news/migrations/0025_auto_20220308_1803.py
@@ -0,0 +1,371 @@
+# Generated by Django 2.2.27 on 2022-03-08 18:03
+
+import basket.news.fields
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ("news", "0024_newsletter_is_mofo"),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name="acoustictxemailmessage",
+            name="language",
+            field=basket.news.fields.LocaleField(
+                choices=[
+                    ("ach", "ach (Acholi)"),
+                    ("af", "af (Afrikaans)"),
+                    ("ak", "ak (Akan)"),
+                    ("am-et", "am-et (Amharic)"),
+                    ("an", "an (Aragonese)"),
+                    ("ar", "ar (Arabic)"),
+                    ("as", "as (Assamese)"),
+                    ("ast", "ast (Asturian)"),
+                    ("az", "az (Azerbaijani)"),
+                    ("azz", "azz (Highland Puebla Nahuatl)"),
+                    ("be", "be (Belarusian)"),
+                    ("bg", "bg (Bulgarian)"),
+                    ("bm", "bm (Bambara)"),
+                    ("bn", "bn (Bengali)"),
+                    ("bn-BD", "bn-BD (Bengali (Bangladesh))"),
+                    ("bn-IN", "bn-IN (Bengali (India))"),
+                    ("bo", "bo (Tibetan)"),
+                    ("br", "br (Breton)"),
+                    ("brx", "brx (Bodo)"),
+                    ("bs", "bs (Bosnian)"),
+                    ("ca", "ca (Catalan)"),
+                    ("ca-valencia", "ca-valencia (Catalan (Valencian))"),
+                    ("cak", "cak (Kaqchikel)"),
+                    ("ckb", "ckb (Central Kurdish)"),
+                    ("crh", "crh (Crimean Tatar)"),
+                    ("cs", "cs (Czech)"),
+                    ("csb", "csb (Kashubian)"),
+                    ("cy", "cy (Welsh)"),
+                    ("da", "da (Danish)"),
+                    ("dbg", "dbg (Debug Robot)"),
+                    ("de", "de (German)"),
+                    ("de-AT", "de-AT (German (Austria))"),
+                    ("de-CH", "de-CH (German (Switzerland))"),
+                    ("de-DE", "de-DE (German (Germany))"),
+                    ("dsb", "dsb (Lower Sorbian)"),
+                    ("ee", "ee (Ewe)"),
+                    ("el", "el (Greek)"),
+                    ("en-AU", "en-AU (English (Australian))"),
+                    ("en-CA", "en-CA (English (Canadian))"),
+                    ("en-GB", "en-GB (English (British))"),
+                    ("en-NZ", "en-NZ (English (New Zealand))"),
+                    ("en-US", "en-US (English (US))"),
+                    ("en-ZA", "en-ZA (English (South African))"),
+                    ("eo", "eo (Esperanto)"),
+                    ("es", "es (Spanish)"),
+                    ("es-AR", "es-AR (Spanish (Argentina))"),
+                    ("es-CL", "es-CL (Spanish (Chile))"),
+                    ("es-ES", "es-ES (Spanish (Spain))"),
+                    ("es-MX", "es-MX (Spanish (Mexico))"),
+                    ("et", "et (Estonian)"),
+                    ("eu", "eu (Basque)"),
+                    ("fa", "fa (Persian)"),
+                    ("ff", "ff (Fulah)"),
+                    ("fi", "fi (Finnish)"),
+                    ("fj-FJ", "fj-FJ (Fijian)"),
+                    ("fr", "fr (French)"),
+                    ("fur-IT", "fur-IT (Friulian)"),
+                    ("fy-NL", "fy-NL (Frisian)"),
+                    ("ga", "ga (Irish)"),
+                    ("ga-IE", "ga-IE (Irish)"),
+                    ("gd", "gd (Gaelic (Scotland))"),
+                    ("gl", "gl (Galician)"),
+                    ("gn", "gn (Guarani)"),
+                    ("gu", "gu (Gujarati)"),
+                    ("gu-IN", "gu-IN (Gujarati (India))"),
+                    ("ha", "ha (Hausa)"),
+                    ("he", "he (Hebrew)"),
+                    ("hi", "hi (Hindi)"),
+                    ("hi-IN", "hi-IN (Hindi (India))"),
+                    ("hr", "hr (Croatian)"),
+                    ("hsb", "hsb (Upper Sorbian)"),
+                    ("hu", "hu (Hungarian)"),
+                    ("hy-AM", "hy-AM (Armenian)"),
+                    ("hye", "hye (Armenian Eastern Classic Orthography)"),
+                    ("ia", "ia (Interlingua)"),
+                    ("id", "id (Indonesian)"),
+                    ("ig", "ig (Igbo)"),
+                    ("is", "is (Icelandic)"),
+                    ("it", "it (Italian)"),
+                    ("ja", "ja (Japanese)"),
+                    ("ja-JP-mac", "ja-JP-mac (Japanese)"),
+                    ("ka", "ka (Georgian)"),
+                    ("kab", "kab (Kabyle)"),
+                    ("kk", "kk (Kazakh)"),
+                    ("km", "km (Khmer)"),
+                    ("kn", "kn (Kannada)"),
+                    ("ko", "ko (Korean)"),
+                    ("kok", "kok (Konkani)"),
+                    ("ks", "ks (Kashmiri)"),
+                    ("ku", "ku (Kurdish)"),
+                    ("la", "la (Latin)"),
+                    ("lg", "lg (Luganda)"),
+                    ("lij", "lij (Ligurian)"),
+                    ("ln", "ln (Lingala)"),
+                    ("lo", "lo (Lao)"),
+                    ("lt", "lt (Lithuanian)"),
+                    ("ltg", "ltg (Latgalian)"),
+                    ("lv", "lv (Latvian)"),
+                    ("mai", "mai (Maithili)"),
+                    ("meh", "meh (Mixteco Yucuhiti)"),
+                    ("mg", "mg (Malagasy)"),
+                    ("mi", "mi (Maori (Aotearoa))"),
+                    ("mk", "mk (Macedonian)"),
+                    ("ml", "ml (Malayalam)"),
+                    ("mn", "mn (Mongolian)"),
+                    ("mr", "mr (Marathi)"),
+                    ("ms", "ms (Malay)"),
+                    ("my", "my (Burmese)"),
+                    ("nb-NO", "nb-NO (Norwegian (Bokmål))"),
+                    ("ne-NP", "ne-NP (Nepali)"),
+                    ("nl", "nl (Dutch)"),
+                    ("nn-NO", "nn-NO (Norwegian (Nynorsk))"),
+                    ("nr", "nr (Ndebele, South)"),
+                    ("nso", "nso (Northern Sotho)"),
+                    ("oc", "oc (Occitan (Lengadocian))"),
+                    ("or", "or (Odia)"),
+                    ("pa", "pa (Punjabi)"),
+                    ("pa-IN", "pa-IN (Punjabi (India))"),
+                    ("pl", "pl (Polish)"),
+                    ("pt-BR", "pt-BR (Portuguese (Brazilian))"),
+                    ("pt-PT", "pt-PT (Portuguese (Portugal))"),
+                    ("rm", "rm (Romansh)"),
+                    ("ro", "ro (Romanian)"),
+                    ("ru", "ru (Russian)"),
+                    ("rw", "rw (Kinyarwanda)"),
+                    ("sa", "sa (Sanskrit)"),
+                    ("sah", "sah (Sakha)"),
+                    ("sat", "sat (Santali)"),
+                    ("sc", "sc (Sardinian)"),
+                    ("scn", "scn (Sicilian)"),
+                    ("sco", "sco (Scots)"),
+                    ("si", "si (Sinhala)"),
+                    ("sk", "sk (Slovak)"),
+                    ("sl", "sl (Slovenian)"),
+                    ("son", "son (Songhai)"),
+                    ("sq", "sq (Albanian)"),
+                    ("sr", "sr (Serbian)"),
+                    ("sr-Cyrl", "sr-Cyrl (Serbian)"),
+                    ("sr-Latn", "sr-Latn (Serbian)"),
+                    ("ss", "ss (Siswati)"),
+                    ("st", "st (Southern Sotho)"),
+                    ("sv-SE", "sv-SE (Swedish)"),
+                    ("sw", "sw (Swahili)"),
+                    ("szl", "szl (Silesian)"),
+                    ("ta", "ta (Tamil)"),
+                    ("ta-IN", "ta-IN (Tamil (India))"),
+                    ("ta-LK", "ta-LK (Tamil (Sri Lanka))"),
+                    ("te", "te (Telugu)"),
+                    ("tg", "tg (Tajik)"),
+                    ("th", "th (Thai)"),
+                    ("tl", "tl (Tagalog)"),
+                    ("tn", "tn (Tswana)"),
+                    ("tr", "tr (Turkish)"),
+                    ("trs", "trs (Triqui)"),
+                    ("ts", "ts (Tsonga)"),
+                    ("tsz", "tsz (Purépecha)"),
+                    ("tt-RU", "tt-RU (Tatar)"),
+                    ("uk", "uk (Ukrainian)"),
+                    ("ur", "ur (Urdu)"),
+                    ("uz", "uz (Uzbek)"),
+                    ("ve", "ve (Venda)"),
+                    ("vi", "vi (Vietnamese)"),
+                    ("wo", "wo (Wolof)"),
+                    ("x-testing", "x-testing (Testing)"),
+                    ("xh", "xh (Xhosa)"),
+                    ("yo", "yo (Yoruba)"),
+                    ("zh-CN", "zh-CN (Chinese (Simplified))"),
+                    ("zh-TW", "zh-TW (Chinese (Traditional))"),
+                    ("zu", "zu (Zulu)"),
+                ],
+                default="en-US",
+                max_length=32,
+            ),
+        ),
+        migrations.AlterField(
+            model_name="localestewards",
+            name="locale",
+            field=basket.news.fields.LocaleField(
+                choices=[
+                    ("ach", "ach (Acholi)"),
+                    ("af", "af (Afrikaans)"),
+                    ("ak", "ak (Akan)"),
+                    ("am-et", "am-et (Amharic)"),
+                    ("an", "an (Aragonese)"),
+                    ("ar", "ar (Arabic)"),
+                    ("as", "as (Assamese)"),
+                    ("ast", "ast (Asturian)"),
+                    ("az", "az (Azerbaijani)"),
+                    ("azz", "azz (Highland Puebla Nahuatl)"),
+                    ("be", "be (Belarusian)"),
+                    ("bg", "bg (Bulgarian)"),
+                    ("bm", "bm (Bambara)"),
+                    ("bn", "bn (Bengali)"),
+                    ("bn-BD", "bn-BD (Bengali (Bangladesh))"),
+                    ("bn-IN", "bn-IN (Bengali (India))"),
+                    ("bo", "bo (Tibetan)"),
+                    ("br", "br (Breton)"),
+                    ("brx", "brx (Bodo)"),
+                    ("bs", "bs (Bosnian)"),
+                    ("ca", "ca (Catalan)"),
+                    ("ca-valencia", "ca-valencia (Catalan (Valencian))"),
+                    ("cak", "cak (Kaqchikel)"),
+                    ("ckb", "ckb (Central Kurdish)"),
+                    ("crh", "crh (Crimean Tatar)"),
+                    ("cs", "cs (Czech)"),
+                    ("csb", "csb (Kashubian)"),
+                    ("cy", "cy (Welsh)"),
+                    ("da", "da (Danish)"),
+                    ("dbg", "dbg (Debug Robot)"),
+                    ("de", "de (German)"),
+                    ("de-AT", "de-AT (German (Austria))"),
+                    ("de-CH", "de-CH (German (Switzerland))"),
+                    ("de-DE", "de-DE (German (Germany))"),
+                    ("dsb", "dsb (Lower Sorbian)"),
+                    ("ee", "ee (Ewe)"),
+                    ("el", "el (Greek)"),
+                    ("en-AU", "en-AU (English (Australian))"),
+                    ("en-CA", "en-CA (English (Canadian))"),
+                    ("en-GB", "en-GB (English (British))"),
+                    ("en-NZ", "en-NZ (English (New Zealand))"),
+                    ("en-US", "en-US (English (US))"),
+                    ("en-ZA", "en-ZA (English (South African))"),
+                    ("eo", "eo (Esperanto)"),
+                    ("es", "es (Spanish)"),
+                    ("es-AR", "es-AR (Spanish (Argentina))"),
+                    ("es-CL", "es-CL (Spanish (Chile))"),
+                    ("es-ES", "es-ES (Spanish (Spain))"),
+                    ("es-MX", "es-MX (Spanish (Mexico))"),
+                    ("et", "et (Estonian)"),
+                    ("eu", "eu (Basque)"),
+                    ("fa", "fa (Persian)"),
+                    ("ff", "ff (Fulah)"),
+                    ("fi", "fi (Finnish)"),
+                    ("fj-FJ", "fj-FJ (Fijian)"),
+                    ("fr", "fr (French)"),
+                    ("fur-IT", "fur-IT (Friulian)"),
+                    ("fy-NL", "fy-NL (Frisian)"),
+                    ("ga", "ga (Irish)"),
+                    ("ga-IE", "ga-IE (Irish)"),
+                    ("gd", "gd (Gaelic (Scotland))"),
+                    ("gl", "gl (Galician)"),
+                    ("gn", "gn (Guarani)"),
+                    ("gu", "gu (Gujarati)"),
+                    ("gu-IN", "gu-IN (Gujarati (India))"),
+                    ("ha", "ha (Hausa)"),
+                    ("he", "he (Hebrew)"),
+                    ("hi", "hi (Hindi)"),
+                    ("hi-IN", "hi-IN (Hindi (India))"),
+                    ("hr", "hr (Croatian)"),
+                    ("hsb", "hsb (Upper Sorbian)"),
+                    ("hu", "hu (Hungarian)"),
+                    ("hy-AM", "hy-AM (Armenian)"),
+                    ("hye", "hye (Armenian Eastern Classic Orthography)"),
+                    ("ia", "ia (Interlingua)"),
+                    ("id", "id (Indonesian)"),
+                    ("ig", "ig (Igbo)"),
+                    ("is", "is (Icelandic)"),
+                    ("it", "it (Italian)"),
+                    ("ja", "ja (Japanese)"),
+                    ("ja-JP-mac", "ja-JP-mac (Japanese)"),
+                    ("ka", "ka (Georgian)"),
+                    ("kab", "kab (Kabyle)"),
+                    ("kk", "kk (Kazakh)"),
+                    ("km", "km (Khmer)"),
+                    ("kn", "kn (Kannada)"),
+                    ("ko", "ko (Korean)"),
+                    ("kok", "kok (Konkani)"),
+                    ("ks", "ks (Kashmiri)"),
+                    ("ku", "ku (Kurdish)"),
+                    ("la", "la (Latin)"),
+                    ("lg", "lg (Luganda)"),
+                    ("lij", "lij (Ligurian)"),
+                    ("ln", "ln (Lingala)"),
+                    ("lo", "lo (Lao)"),
+                    ("lt", "lt (Lithuanian)"),
+                    ("ltg", "ltg (Latgalian)"),
+                    ("lv", "lv (Latvian)"),
+                    ("mai", "mai (Maithili)"),
+                    ("meh", "meh (Mixteco Yucuhiti)"),
+                    ("mg", "mg (Malagasy)"),
+                    ("mi", "mi (Maori (Aotearoa))"),
+                    ("mk", "mk (Macedonian)"),
+                    ("ml", "ml (Malayalam)"),
+                    ("mn", "mn (Mongolian)"),
+                    ("mr", "mr (Marathi)"),
+                    ("ms", "ms (Malay)"),
+                    ("my", "my (Burmese)"),
+                    ("nb-NO", "nb-NO (Norwegian (Bokmål))"),
+                    ("ne-NP", "ne-NP (Nepali)"),
+                    ("nl", "nl (Dutch)"),
+                    ("nn-NO", "nn-NO (Norwegian (Nynorsk))"),
+                    ("nr", "nr (Ndebele, South)"),
+                    ("nso", "nso (Northern Sotho)"),
+                    ("oc", "oc (Occitan (Lengadocian))"),
+                    ("or", "or (Odia)"),
+                    ("pa", "pa (Punjabi)"),
+                    ("pa-IN", "pa-IN (Punjabi (India))"),
+                    ("pl", "pl (Polish)"),
+                    ("pt-BR", "pt-BR (Portuguese (Brazilian))"),
+                    ("pt-PT", "pt-PT (Portuguese (Portugal))"),
+                    ("rm", "rm (Romansh)"),
+                    ("ro", "ro (Romanian)"),
+                    ("ru", "ru (Russian)"),
+                    ("rw", "rw (Kinyarwanda)"),
+                    ("sa", "sa (Sanskrit)"),
+                    ("sah", "sah (Sakha)"),
+                    ("sat", "sat (Santali)"),
+                    ("sc", "sc (Sardinian)"),
+                    ("scn", "scn (Sicilian)"),
+                    ("sco", "sco (Scots)"),
+                    ("si", "si (Sinhala)"),
+                    ("sk", "sk (Slovak)"),
+                    ("sl", "sl (Slovenian)"),
+                    ("son", "son (Songhai)"),
+                    ("sq", "sq (Albanian)"),
+                    ("sr", "sr (Serbian)"),
+                    ("sr-Cyrl", "sr-Cyrl (Serbian)"),
+                    ("sr-Latn", "sr-Latn (Serbian)"),
+                    ("ss", "ss (Siswati)"),
+                    ("st", "st (Southern Sotho)"),
+                    ("sv-SE", "sv-SE (Swedish)"),
+                    ("sw", "sw (Swahili)"),
+                    ("szl", "szl (Silesian)"),
+                    ("ta", "ta (Tamil)"),
+                    ("ta-IN", "ta-IN (Tamil (India))"),
+                    ("ta-LK", "ta-LK (Tamil (Sri Lanka))"),
+                    ("te", "te (Telugu)"),
+                    ("tg", "tg (Tajik)"),
+                    ("th", "th (Thai)"),
+                    ("tl", "tl (Tagalog)"),
+                    ("tn", "tn (Tswana)"),
+                    ("tr", "tr (Turkish)"),
+                    ("trs", "trs (Triqui)"),
+                    ("ts", "ts (Tsonga)"),
+                    ("tsz", "tsz (Purépecha)"),
+                    ("tt-RU", "tt-RU (Tatar)"),
+                    ("uk", "uk (Ukrainian)"),
+                    ("ur", "ur (Urdu)"),
+                    ("uz", "uz (Uzbek)"),
+                    ("ve", "ve (Venda)"),
+                    ("vi", "vi (Vietnamese)"),
+                    ("wo", "wo (Wolof)"),
+                    ("x-testing", "x-testing (Testing)"),
+                    ("xh", "xh (Xhosa)"),
+                    ("yo", "yo (Yoruba)"),
+                    ("zh-CN", "zh-CN (Chinese (Simplified))"),
+                    ("zh-TW", "zh-TW (Chinese (Traditional))"),
+                    ("zu", "zu (Zulu)"),
+                ],
+                max_length=32,
+            ),
+        ),
+    ]
diff --git a/basket/news/views.py b/basket/news/views.py
--- a/basket/news/views.py
+++ b/basket/news/views.py
@@ -18,7 +18,7 @@
 from basket import errors
 from django_statsd.clients import statsd
 from ratelimit.exceptions import Ratelimited
-from ratelimit.utils import is_ratelimited
+from ratelimit.core import is_ratelimited
 from simple_salesforce import SalesforceError
 from synctool.routing import Route
 


Test patch:
diff --git a/basket/news/tests/test__utils.py b/basket/news/tests/test__utils.py
--- a/basket/news/tests/test__utils.py
+++ b/basket/news/tests/test__utils.py
@@ -1,10 +1,10 @@
 # -*- coding: utf8 -*-
 
-from django.test import override_settings, TestCase, RequestFactory
+from unittest.mock import Mock, patch
 
 import fxa.constants
 import fxa.errors
-from mock import Mock, patch
+from django.test import RequestFactory, TestCase, override_settings
 
 from basket.news.models import BlockedEmail
 from basket.news.utils import (
@@ -13,12 +13,12 @@
     email_is_blocked,
     get_accept_languages,
     get_best_language,
+    get_best_supported_lang,
     get_email_block_list,
     get_fxa_clients,
     has_valid_fxa_oauth,
     is_authorized,
     language_code_is_valid,
-    get_best_supported_lang,
     parse_newsletters_csv,
     parse_phone_number,
     process_email,
diff --git a/basket/news/tests/test_confirm.py b/basket/news/tests/test_confirm.py
--- a/basket/news/tests/test_confirm.py
+++ b/basket/news/tests/test_confirm.py
@@ -1,7 +1,7 @@
-from django.test import TestCase
+from unittest.mock import Mock, patch
 
 from celery.exceptions import Retry
-from mock import patch, Mock
+from django.test import TestCase
 
 from basket.news.backends.common import NewsletterException
 from basket.news.tasks import confirm_user
diff --git a/basket/news/tests/test_fields.py b/basket/news/tests/test_fields.py
--- a/basket/news/tests/test_fields.py
+++ b/basket/news/tests/test_fields.py
@@ -1,8 +1,8 @@
+from unittest.mock import Mock, call, patch
+
 from django.core.exceptions import ValidationError
 from django.test import TestCase
 
-from mock import call, Mock, patch
-
 from basket.news.fields import CommaSeparatedEmailField
 
 
diff --git a/basket/news/tests/test_forms.py b/basket/news/tests/test_forms.py
--- a/basket/news/tests/test_forms.py
+++ b/basket/news/tests/test_forms.py
@@ -1,4 +1,4 @@
-from mock import patch
+from unittest.mock import patch
 
 from basket.news import forms
 
diff --git a/basket/news/tests/test_middleware.py b/basket/news/tests/test_middleware.py
--- a/basket/news/tests/test_middleware.py
+++ b/basket/news/tests/test_middleware.py
@@ -1,6 +1,6 @@
-from django.test import RequestFactory, override_settings
+from unittest.mock import Mock
 
-from mock import Mock
+from django.test import RequestFactory, override_settings
 
 from basket.news.middleware import EnforceHostnameMiddleware, is_ip_address
 
diff --git a/basket/news/tests/test_models.py b/basket/news/tests/test_models.py
--- a/basket/news/tests/test_models.py
+++ b/basket/news/tests/test_models.py
@@ -1,8 +1,8 @@
+from unittest.mock import patch
+
 from django.core import mail
 from django.test import TestCase
 
-from mock import patch
-
 from basket.news import models
 
 
diff --git a/basket/news/tests/test_sfdc.py b/basket/news/tests/test_sfdc.py
--- a/basket/news/tests/test_sfdc.py
+++ b/basket/news/tests/test_sfdc.py
@@ -1,9 +1,9 @@
+from unittest.mock import Mock, patch
+
 from django.test import TestCase
 from django.test.utils import override_settings
 
-from mock import patch, Mock
-
-from basket.news.backends.sfdc import to_vendor, from_vendor
+from basket.news.backends.sfdc import from_vendor, to_vendor
 
 
 @patch(
diff --git a/basket/news/tests/test_tasks.py b/basket/news/tests/test_tasks.py
--- a/basket/news/tests/test_tasks.py
+++ b/basket/news/tests/test_tasks.py
@@ -1,25 +1,27 @@
 import json
 from copy import deepcopy
 from datetime import datetime, timedelta
-from uuid import uuid4
+from unittest.mock import ANY, Mock, call, patch
 from urllib.error import URLError
+from uuid import uuid4
 
+import simple_salesforce as sfapi
+from celery.exceptions import Retry
 from django.conf import settings
 from django.core.cache import cache
 from django.test import TestCase
 from django.test.utils import override_settings
 from django.utils.timezone import now
-
-import simple_salesforce as sfapi
-from celery.exceptions import Retry
-from mock import ANY, Mock, call, patch
 from requests.exceptions import ConnectionError as RequestsConnectionError
 
 from basket.news.backends.ctms import CTMSNotFoundByAltIDError
 from basket.news.backends.sfdc import SFDCDisabled
 from basket.news.celery import app as celery_app
-from basket.news.models import AcousticTxEmailMessage, FailedTask, CommonVoiceUpdate
+from basket.news.models import AcousticTxEmailMessage, CommonVoiceUpdate, FailedTask
 from basket.news.tasks import (
+    PETITION_CONTACT_FIELDS,
+    SUBSCRIBE,
+    RetryTask,
     _add_fxa_activity,
     amo_sync_addon,
     amo_sync_user,
@@ -28,22 +30,19 @@
     fxa_email_changed,
     fxa_login,
     fxa_verified,
+    get_fxa_user_data,
+    get_lock,
     gmttime,
-    PETITION_CONTACT_FIELDS,
     process_common_voice_batch,
     process_donation,
     process_donation_event,
     process_donation_receipt,
-    process_petition_signature,
     process_newsletter_subscribe,
+    process_petition_signature,
     record_common_voice_update,
     send_acoustic_tx_message,
-    SUBSCRIBE,
-    get_lock,
-    RetryTask,
     update_custom_unsub,
     update_user_meta,
-    get_fxa_user_data,
 )
 from basket.news.utils import iso_format_unix_timestamp
 
diff --git a/basket/news/tests/test_update_user_task.py b/basket/news/tests/test_update_user_task.py
--- a/basket/news/tests/test_update_user_task.py
+++ b/basket/news/tests/test_update_user_task.py
@@ -1,14 +1,13 @@
 import json
+from unittest.mock import ANY, patch
 
 from django.core.cache import cache
-from django.test import TestCase, RequestFactory
-
-from basket import errors
-from mock import patch, ANY
+from django.test import RequestFactory, TestCase
 from ratelimit.exceptions import Ratelimited
 
-from basket.news.utils import SUBSCRIBE, UNSUBSCRIBE, SET
+from basket import errors
 from basket.news import views
+from basket.news.utils import SET, SUBSCRIBE, UNSUBSCRIBE
 
 
 class UpdateUserTaskTests(TestCase):
diff --git a/basket/news/tests/test_upsert_user.py b/basket/news/tests/test_upsert_user.py
--- a/basket/news/tests/test_upsert_user.py
+++ b/basket/news/tests/test_upsert_user.py
@@ -1,9 +1,8 @@
+from unittest.mock import ANY, patch
 from uuid import uuid4
 
 from django.test import TestCase, override_settings
 
-from mock import patch, ANY
-
 from basket.news import models
 from basket.news.tasks import upsert_user
 from basket.news.utils import SET, SUBSCRIBE, UNSUBSCRIBE, generate_token
diff --git a/basket/news/tests/test_users.py b/basket/news/tests/test_users.py
--- a/basket/news/tests/test_users.py
+++ b/basket/news/tests/test_users.py
@@ -1,20 +1,17 @@
 import json
+from unittest.mock import patch
 
 from django.http import HttpResponse
 from django.test import TestCase
 from django.test.client import RequestFactory
 from django.urls import reverse
-
-from mock import patch
-
 from requests import Response
 from requests.exceptions import HTTPError
 
 from basket import errors
-
 from basket.news import views
 from basket.news.backends.common import NewsletterException
-from basket.news.backends.ctms import CTMSNotConfigured, CTMSMultipleContactsError
+from basket.news.backends.ctms import CTMSMultipleContactsError, CTMSNotConfigured
 from basket.news.backends.sfdc import SFDCDisabled
 from basket.news.models import APIUser
 from basket.news.utils import SET, generate_token
diff --git a/basket/news/tests/test_views.py b/basket/news/tests/test_views.py
--- a/basket/news/tests/test_views.py
+++ b/basket/news/tests/test_views.py
@@ -1,24 +1,21 @@
 # -*- coding: utf8 -*-
 
 import json
+from unittest.mock import ANY, Mock, patch
 
 from django.conf import settings
 from django.core.cache import cache
-from django.test import TestCase
-from django.test import override_settings
-from django.test.client import RequestFactory, Client
+from django.test import TestCase, override_settings
+from django.test.client import Client, RequestFactory
 from django.urls import reverse
-
-from basket import errors
 from email_validator import EmailSyntaxError
-from mock import Mock, patch, ANY
 
-from basket.news import models, views, utils
-from basket.news.newsletters import newsletter_languages, newsletter_fields
+from basket import errors
+from basket.news import models, utils, views
+from basket.news.newsletters import newsletter_fields, newsletter_languages
 from basket.news.tasks import SUBSCRIBE
 from basket.news.utils import email_block_list_cache
 
-
 none_mock = Mock(return_value=None)