Repository: C0D1UM/django-secured-fields (language: python)

Issue:
Add support for Django 4.0



Discussion / hints:

Fix patch:
diff --git a/README.md b/README.md
index cf3db9b..255a38c 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,7 @@
 [![codecov](https://codecov.io/gh/C0D1UM/django-secured-fields/branch/main/graph/badge.svg?token=PN19DJ3SDF)](https://codecov.io/gh/C0D1UM/django-secured-fields)
 [![PyPI](https://img.shields.io/pypi/v/django-secured-fields)](https://pypi.org/project/django-secured-fields/)  
 [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/django-secured-fields)](https://github.com/C0D1UM/django-secured-fields)
-[![Django Version](https://img.shields.io/badge/django-3.1%20%7C%203.2-blue)](https://github.com/C0D1UM/django-secured-fields)
+[![Django Version](https://img.shields.io/badge/django-3.1%20%7C%203.2%20%7C%204.0-blue)](https://github.com/C0D1UM/django-secured-fields)
 [![PostgreSQL Version](https://img.shields.io/badge/postgres-12.9%20%7C%2013.5%20%7C%2014.1-blue)](https://github.com/C0D1UM/django-secured-fields)
 [![MySQL Version](https://img.shields.io/badge/mysql-5.7%20%7C%208.0-blue)](https://github.com/C0D1UM/django-secured-fields)
 
diff --git a/poetry.lock b/poetry.lock
index fc5dcdc..b365c9f 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -38,6 +38,17 @@ category = "dev"
 optional = false
 python-versions = "*"
 
+[[package]]
+name = "backports.zoneinfo"
+version = "0.2.1"
+description = "Backport of the standard library zoneinfo module"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.extras]
+tzdata = ["tzdata"]
+
 [[package]]
 name = "cffi"
 version = "1.15.0"
@@ -97,16 +108,17 @@ python-versions = ">=3.5"
 
 [[package]]
 name = "django"
-version = "3.2.11"
-description = "A high-level Python Web framework that encourages rapid development and clean, pragmatic design."
+version = "4.0.1"
+description = "A high-level Python web framework that encourages rapid development and clean, pragmatic design."
 category = "main"
 optional = false
-python-versions = ">=3.6"
+python-versions = ">=3.8"
 
 [package.dependencies]
-asgiref = ">=3.3.2,<4"
-pytz = "*"
+asgiref = ">=3.4.1,<4"
+"backports.zoneinfo" = {version = "*", markers = "python_version < \"3.9\""}
 sqlparse = ">=0.2.2"
+tzdata = {version = "*", markers = "sys_platform == \"win32\""}
 
 [package.extras]
 argon2 = ["argon2-cffi (>=19.1.0)"]
@@ -378,14 +390,6 @@ python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
 [package.dependencies]
 six = ">=1.5"
 
-[[package]]
-name = "pytz"
-version = "2021.3"
-description = "World timezone definitions, modern and historical"
-category = "main"
-optional = false
-python-versions = "*"
-
 [[package]]
 name = "six"
 version = "1.16.0"
@@ -402,14 +406,6 @@ category = "main"
 optional = false
 python-versions = ">=3.5"
 
-[[package]]
-name = "tblib"
-version = "1.7.0"
-description = "Traceback serialization library."
-category = "dev"
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
-
 [[package]]
 name = "toml"
 version = "0.10.2"
@@ -437,6 +433,14 @@ category = "dev"
 optional = false
 python-versions = ">=3.6"
 
+[[package]]
+name = "tzdata"
+version = "2021.5"
+description = "Provider of IANA time zone data"
+category = "main"
+optional = false
+python-versions = ">=2"
+
 [[package]]
 name = "wcwidth"
 version = "0.2.5"
@@ -464,7 +468,7 @@ python-versions = "*"
 [metadata]
 lock-version = "1.1"
 python-versions = "^3.8"
-content-hash = "3d72457a3ea30bda2c6c060b7afa51fb40d9504586407db2ed71cbdbb5029a15"
+content-hash = "e754fa626d86f2d0132d7d3d7db5d23436b89077e7ac7cf42e9f11b01aa90bcf"
 
 [metadata.files]
 appnope = [
@@ -483,6 +487,24 @@ backcall = [
     {file = "backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"},
     {file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"},
 ]
+"backports.zoneinfo" = [
+    {file = "backports.zoneinfo-0.2.1-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:da6013fd84a690242c310d77ddb8441a559e9cb3d3d59ebac9aca1a57b2e18bc"},
+    {file = "backports.zoneinfo-0.2.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:89a48c0d158a3cc3f654da4c2de1ceba85263fafb861b98b59040a5086259722"},
+    {file = "backports.zoneinfo-0.2.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:1c5742112073a563c81f786e77514969acb58649bcdf6cdf0b4ed31a348d4546"},
+    {file = "backports.zoneinfo-0.2.1-cp36-cp36m-win32.whl", hash = "sha256:e8236383a20872c0cdf5a62b554b27538db7fa1bbec52429d8d106effbaeca08"},
+    {file = "backports.zoneinfo-0.2.1-cp36-cp36m-win_amd64.whl", hash = "sha256:8439c030a11780786a2002261569bdf362264f605dfa4d65090b64b05c9f79a7"},
+    {file = "backports.zoneinfo-0.2.1-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:f04e857b59d9d1ccc39ce2da1021d196e47234873820cbeaad210724b1ee28ac"},
+    {file = "backports.zoneinfo-0.2.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:17746bd546106fa389c51dbea67c8b7c8f0d14b5526a579ca6ccf5ed72c526cf"},
+    {file = "backports.zoneinfo-0.2.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5c144945a7752ca544b4b78c8c41544cdfaf9786f25fe5ffb10e838e19a27570"},
+    {file = "backports.zoneinfo-0.2.1-cp37-cp37m-win32.whl", hash = "sha256:e55b384612d93be96506932a786bbcde5a2db7a9e6a4bb4bffe8b733f5b9036b"},
+    {file = "backports.zoneinfo-0.2.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a76b38c52400b762e48131494ba26be363491ac4f9a04c1b7e92483d169f6582"},
+    {file = "backports.zoneinfo-0.2.1-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:8961c0f32cd0336fb8e8ead11a1f8cd99ec07145ec2931122faaac1c8f7fd987"},
+    {file = "backports.zoneinfo-0.2.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e81b76cace8eda1fca50e345242ba977f9be6ae3945af8d46326d776b4cf78d1"},
+    {file = "backports.zoneinfo-0.2.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7b0a64cda4145548fed9efc10322770f929b944ce5cee6c0dfe0c87bf4c0c8c9"},
+    {file = "backports.zoneinfo-0.2.1-cp38-cp38-win32.whl", hash = "sha256:1b13e654a55cd45672cb54ed12148cd33628f672548f373963b0bff67b217328"},
+    {file = "backports.zoneinfo-0.2.1-cp38-cp38-win_amd64.whl", hash = "sha256:4a0f800587060bf8880f954dbef70de6c11bbe59c673c3d818921f042f9954a6"},
+    {file = "backports.zoneinfo-0.2.1.tar.gz", hash = "sha256:fadbfe37f74051d024037f223b8e001611eac868b5c5b06144ef4d8b799862f2"},
+]
 cffi = [
     {file = "cffi-1.15.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:c2502a1a03b6312837279c8c1bd3ebedf6c12c4228ddbad40912d671ccc8a962"},
     {file = "cffi-1.15.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:23cfe892bd5dd8941608f93348c0737e369e51c100d03718f108bf1add7bd6d0"},
@@ -615,8 +637,8 @@ decorator = [
     {file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"},
 ]
 django = [
-    {file = "Django-3.2.11-py3-none-any.whl", hash = "sha256:0a0a37f0b93aef30c4bf3a839c187e1175bcdeb7e177341da0cb7b8194416891"},
-    {file = "Django-3.2.11.tar.gz", hash = "sha256:69c94abe5d6b1b088bf475e09b7b74403f943e34da107e798465d2045da27e75"},
+    {file = "Django-4.0.1-py3-none-any.whl", hash = "sha256:7cd8e8a3ed2bc0dfda05ce1e53a9c81b30eefd7aa350e538a18884475e4d4ce2"},
+    {file = "Django-4.0.1.tar.gz", hash = "sha256:2485eea3cc4c3bae13080dee866ebf90ba9f98d1afe8fda89bfb0eb2e218ef86"},
 ]
 django-environ = [
     {file = "django-environ-0.8.1.tar.gz", hash = "sha256:6f0bc902b43891656b20486938cba0861dc62892784a44919170719572a534cb"},
@@ -831,10 +853,6 @@ python-dateutil = [
     {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"},
     {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"},
 ]
-pytz = [
-    {file = "pytz-2021.3-py2.py3-none-any.whl", hash = "sha256:3672058bc3453457b622aab7a1c3bfd5ab0bdae451512f6cf25f64ed37f5b87c"},
-    {file = "pytz-2021.3.tar.gz", hash = "sha256:acad2d8b20a1af07d4e4c9d2e9285c5ed9104354062f275f3fcd88dcef4f1326"},
-]
 six = [
     {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
     {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
@@ -843,10 +861,6 @@ sqlparse = [
     {file = "sqlparse-0.4.2-py3-none-any.whl", hash = "sha256:48719e356bb8b42991bdbb1e8b83223757b93789c00910a616a071910ca4a64d"},
     {file = "sqlparse-0.4.2.tar.gz", hash = "sha256:0c00730c74263a94e5a9919ade150dfc3b19c574389985446148402998287dae"},
 ]
-tblib = [
-    {file = "tblib-1.7.0-py2.py3-none-any.whl", hash = "sha256:289fa7359e580950e7d9743eab36b0691f0310fce64dee7d9c31065b8f723e23"},
-    {file = "tblib-1.7.0.tar.gz", hash = "sha256:059bd77306ea7b419d4f76016aef6d7027cc8a0785579b5aad198803435f882c"},
-]
 toml = [
     {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
     {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
@@ -859,6 +873,10 @@ typing-extensions = [
     {file = "typing_extensions-4.0.1-py3-none-any.whl", hash = "sha256:7f001e5ac290a0c0401508864c7ec868be4e701886d5b573a9528ed3973d9d3b"},
     {file = "typing_extensions-4.0.1.tar.gz", hash = "sha256:4ca091dea149f945ec56afb48dae714f21e8692ef22a395223bcd328961b6a0e"},
 ]
+tzdata = [
+    {file = "tzdata-2021.5-py2.py3-none-any.whl", hash = "sha256:3eee491e22ebfe1e5cfcc97a4137cd70f092ce59144d81f8924a844de05ba8f5"},
+    {file = "tzdata-2021.5.tar.gz", hash = "sha256:68dbe41afd01b867894bbdfd54fa03f468cfa4f0086bfb4adcd8de8f24f3ee21"},
+]
 wcwidth = [
     {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"},
     {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"},
diff --git a/pyproject.toml b/pyproject.toml
index 6dcc081..f491e95 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -13,7 +13,7 @@ packages = [
 
 [tool.poetry.dependencies]
 python = "^3.8"
-django = "^3.1"
+django = ">=3.1 || <4.1"
 cryptography = ">=35"
 
 [tool.poetry.dev-dependencies]
@@ -28,7 +28,7 @@ django-environ = "^0.8"
 Pillow = "^9.0"
 django-extensions = "^3.0"
 coverage = "^6"
-tblib = "^1.7.0"
+"backports.zoneinfo" = { version = "^0.2", python = "~3.8" }
 
 [tool.poetry-dynamic-versioning]
 enable = true


Test patch:
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 413cbf6..b773956 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -32,7 +32,7 @@ jobs:
       fail-fast: false
       matrix:
         python: [ "3.8", "3.9", "3.10" ]
-        django: [ "3.1", "3.2" ]
+        django: [ "3.1", "3.2", "4.0" ]
         db: ${{ fromJSON(inputs.database-versions) }}
         # ref: https://github.com/jefftriplett/python-github-actions-matrix-demo
         exclude:
@@ -42,7 +42,7 @@ jobs:
         # newest matrix set which will upload to codecov
         include:
           - python: "3.10"
-            django: "3.2"
+            django: "4.0"
             db: ${{ inputs.database-version-codecov }}
             codecov: "true"
     services:
diff --git a/test_secured_fields/main/tests/test_fields.py b/test_secured_fields/main/tests/test_fields.py
index abb2ae4..b8aabdb 100644
--- a/test_secured_fields/main/tests/test_fields.py
+++ b/test_secured_fields/main/tests/test_fields.py
@@ -5,7 +5,6 @@
 import typing
 import warnings
 
-import pytz
 from django import test
 from django.core import exceptions
 from django.core.files.storage import FileSystemStorage
@@ -210,7 +209,7 @@ def test_naive_use_tz(self):
         create_value = datetime.datetime(2021, 12, 31, 23, 59, 3)
 
         if connection.vendor == DatabaseVendor.POSTGRESQL:
-            self.create_and_assert(create_value, datetime.datetime(2021, 12, 31, 23, 59, 3, tzinfo=pytz.UTC))
+            self.create_and_assert(create_value, datetime.datetime(2021, 12, 31, 23, 59, 3, tzinfo=test_utils.TZ_UTC))
             self.assert_encrypted_str_field('2021-12-31 23:59:03+00:00')
         elif connection.vendor == DatabaseVendor.MYSQL:
             # mysql is timezone naive
@@ -221,7 +220,7 @@ def test_naive_use_tz(self):
 
     @test.override_settings(USE_TZ=True)
     def test_utc_use_tz(self):
-        create_value = timezone.make_aware(datetime.datetime(2021, 12, 31, 23, 59, 3), pytz.UTC)
+        create_value = timezone.make_aware(datetime.datetime(2021, 12, 31, 23, 59, 3), test_utils.TZ_UTC)
 
         if connection.vendor == DatabaseVendor.POSTGRESQL:
             self.create_and_assert(create_value)
@@ -235,7 +234,7 @@ def test_utc_use_tz(self):
 
     @test.override_settings(USE_TZ=True)
     def test_bangkok_use_tz(self):
-        create_value = timezone.make_aware(datetime.datetime(2021, 12, 31, 23, 59, 3), pytz.timezone('Asia/Bangkok'))
+        create_value = timezone.make_aware(datetime.datetime(2021, 12, 31, 23, 59, 3), test_utils.TZ_BANGKOK)
 
         if connection.vendor == DatabaseVendor.POSTGRESQL:
             self.create_and_assert(create_value)
@@ -248,13 +247,13 @@ def test_bangkok_use_tz(self):
             raise DatabaseBackendNotSupported
 
 
-@freeze_time(datetime.datetime(2021, 12, 31, 23, 59, 3, tzinfo=pytz.UTC))
+@freeze_time(datetime.datetime(2021, 12, 31, 23, 59, 3, tzinfo=test_utils.TZ_UTC))
 @test.override_settings(USE_TZ=True)
 class DateTimeFieldWithAutoNowTestCase(BaseTestCases.BaseFieldTestCase):
     model_class = models.DateTimeFieldAutoNowModel
 
     def test_simple(self):
-        create_value = datetime.datetime(2021, 12, 31, 23, 59, 3, tzinfo=pytz.UTC)
+        create_value = datetime.datetime(2021, 12, 31, 23, 59, 3, tzinfo=test_utils.TZ_UTC)
 
         if connection.vendor == DatabaseVendor.POSTGRESQL:
             self.create_and_assert(test_utils.NoValue, assert_value=create_value)
@@ -273,7 +272,7 @@ class SearchableDateTimeFieldTestCase(DateTimeFieldTestCase):
 
     @test.override_settings(SECURED_FIELDS_HASH_SALT='test')
     def test_with_salt(self):
-        create_value = datetime.datetime(2021, 12, 31, 23, 59, 3, tzinfo=pytz.UTC)
+        create_value = datetime.datetime(2021, 12, 31, 23, 59, 3, tzinfo=test_utils.TZ_UTC)
 
         if connection.vendor == DatabaseVendor.POSTGRESQL:
             self.create_and_assert(create_value)
diff --git a/test_secured_fields/main/tests/test_lookups.py b/test_secured_fields/main/tests/test_lookups.py
index e6e7986..2916f3a 100644
--- a/test_secured_fields/main/tests/test_lookups.py
+++ b/test_secured_fields/main/tests/test_lookups.py
@@ -2,7 +2,6 @@
 import decimal
 import typing
 
-import pytz
 from django import test
 from django.db import connection
 
@@ -33,7 +32,7 @@ def test_date_field(self):
         self.create_and_assert(models.SearchableDateFieldModel, datetime.date(2021, 12, 31))
 
     def test_datetime_field(self):
-        create_value = datetime.datetime(2021, 12, 31, 23, 59, 3, tzinfo=pytz.UTC)
+        create_value = datetime.datetime(2021, 12, 31, 23, 59, 3, tzinfo=test_utils.TZ_UTC)
         assert_value = create_value
 
         if connection.vendor == DatabaseVendor.MYSQL:
@@ -81,10 +80,10 @@ def test_date_field(self):
         )
 
     def test_datetime_field(self):
-        create_value = datetime.datetime(2021, 12, 31, 23, 59, 3, tzinfo=pytz.UTC)
+        create_value = datetime.datetime(2021, 12, 31, 23, 59, 3, tzinfo=test_utils.TZ_UTC)
         assert_value = [
-            datetime.datetime(2021, 12, 31, 23, 59, 3, tzinfo=pytz.UTC),
-            datetime.datetime(2021, 12, 31, 23, 50, 3, tzinfo=pytz.UTC),
+            datetime.datetime(2021, 12, 31, 23, 59, 3, tzinfo=test_utils.TZ_UTC),
+            datetime.datetime(2021, 12, 31, 23, 50, 3, tzinfo=test_utils.TZ_UTC),
         ]
 
         if connection.vendor == DatabaseVendor.MYSQL:
diff --git a/test_secured_fields/main/tests/utils.py b/test_secured_fields/main/tests/utils.py
index 5568a31..5dbc8d5 100644
--- a/test_secured_fields/main/tests/utils.py
+++ b/test_secured_fields/main/tests/utils.py
@@ -1,8 +1,16 @@
 import shutil
+try:
+    from zoneinfo import ZoneInfo
+except ImportError:
+    from backports.zoneinfo import ZoneInfo
 
 from django.conf import settings
 
 
+TZ_UTC = ZoneInfo('UTC')
+TZ_BANGKOK = ZoneInfo('Asia/Bangkok')
+
+
 class NoValue:
     pass