Subject: [PATCH django-impersonate] Add support for Django 5.0 (pre)

From: Hugo Rodger-Brown <[email-masked]>
Date: Tue, 25 Apr 2023 09:44:23 +0100

# HG changeset patch
# User Hugo Rodger-Brown
# Date 1681222628 -3600
#      Tue Apr 11 15:17:08 2023 +0100
# Node ID 4a152ad24364e0778c555ec62a8dd3aaef5eebaa
# Parent  76e93d43501e5304d6b27e7f7072e4d8aab15104
Add support for Django 5.0 (pre)

diff --git a/.hgignore b/.hgignore
--- a/.hgignore
+++ b/.hgignore
@@ -1,15 +1,16 @@
 syntax:glob
+.*.swp
 .coverage
+.DS_Store
+.idea
 .tox
-settings_local.py
-.*.swp
+.venv
+*.diff
+*.komodoproject
 **.pyc
 MANIFEST
-.idea
+settings_local.py
 
 syntax:regexp
 ^htmlcov$
 ^env$
-syntax: glob
-*.komodoproject
-.DS_Store
\ No newline at end of file
diff --git a/MANIFEST.in b/MANIFEST.in
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,4 +1,4 @@
-include BSD-LICENSE
+include LICENSE
 include CHANGELOG
 include README.rst
 recursive-include impersonate/templates *
diff --git a/impersonate/admin.py b/impersonate/admin.py
--- a/impersonate/admin.py
+++ b/impersonate/admin.py
@@ -5,15 +5,11 @@
 from django.db.utils import NotSupportedError
 from django.utils.html import format_html
 
+from .compat import reverse
 from .helpers import User, check_allow_impersonate
 from .models import ImpersonationLog
 from .settings import settings
 
-try:
-    from django.urls import reverse
-except ImportError:
-    from django.core.urlresolvers import reverse
-
 logger = logging.getLogger(__name__)

diff --git a/impersonate/compat/__init__.py b/impersonate/compat/__init__.py
new file mode 100644
--- /dev/null
+++ b/impersonate/compat/__init__.py
@@ -0,0 +1,4 @@
+try:
+    from django.urls import reverse
+except ImportError:
+    from django.core.urlresolvers import reverse
diff --git a/impersonate/compat/timezone.py b/impersonate/compat/timezone.py
new file mode 100644
--- /dev/null
+++ b/impersonate/compat/timezone.py
@@ -0,0 +1,6 @@
+# timezone.utc removed in Django 5
+try:
+    from django.utils.timezone import utc
+except ImportError:
+    from zoneinfo import ZoneInfo
+    utc = ZoneInfo("UTC")
diff --git a/impersonate/middleware.py b/impersonate/middleware.py
--- a/impersonate/middleware.py
+++ b/impersonate/middleware.py
@@ -2,11 +2,11 @@
 from datetime import datetime, timedelta
 
 from django.http import HttpResponseNotAllowed
-from django.shortcuts import redirect, reverse
-from django.utils import timezone
+from django.shortcuts import redirect
 from django.utils.deprecation import MiddlewareMixin
 from django.utils.functional import SimpleLazyObject
 
+from .compat import reverse, timezone
 from .helpers import User, check_allow_for_uri, check_allow_for_user, check_read_only
 from .settings import settings
 
diff --git a/impersonate/tests.py b/impersonate/tests.py
--- a/impersonate/tests.py
+++ b/impersonate/tests.py
@@ -19,7 +19,7 @@
         is_superuser = False
         is_staff = False
 '''
-from datetime import datetime, timedelta, timezone
+from datetime import datetime, timedelta
 from distutils.version import LooseVersion
 from unittest.mock import PropertyMock, patch
 from urllib.parse import urlencode, urlsplit
@@ -35,17 +35,15 @@
 from django.utils.duration import duration_string
 
 from .admin import (
-    ImpersonationLogAdmin, ImpersonatorFilter, SessionStateFilter,
+    ImpersonationLogAdmin,
+    ImpersonatorFilter,
+    SessionStateFilter,
 )
+from .compat import reverse, timezone
 from .helpers import users_impersonable
 from .models import ImpersonationLog
 from .signals import session_begin, session_end
 
-try:
-    from django.urls import reverse
-except ImportError:
-    from django.core.urlresolvers import reverse
-
 User = get_user_model()
 django_version_loose = LooseVersion(django.get_version())
 
diff --git a/impersonate/views.py b/impersonate/views.py
--- a/impersonate/views.py
+++ b/impersonate/views.py
@@ -5,12 +5,16 @@
 from django.db.models import Q
 from django.http import Http404
 from django.shortcuts import get_object_or_404, redirect, render
-from django.utils import timezone
 
+from .compat import timezone
 from .decorators import allowed_user_required
 from .helpers import (
-    check_allow_for_user, get_paginator, get_redir_arg, get_redir_field,
-    get_redir_path, users_impersonable,
+    check_allow_for_user,
+    get_paginator,
+    get_redir_arg,
+    get_redir_field,
+    get_redir_path,
+    users_impersonable,
 )
 from .settings import User, settings
 from .signals import session_begin, session_end
diff --git a/pyproject.toml b/pyproject.toml
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,7 +1,7 @@
 [tool.black]
 line-length = 79
 skip-string-normalization = true
-target-version = ['py37', 'py38']
+target-version = ['py37', 'py38', 'py39', 'py310', 'py311']
 include = '\.pyi?$'
 exclude = '''
 
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -48,11 +48,14 @@
         'Framework :: Django :: 2.2',
         'Framework :: Django :: 3.2',
         'Framework :: Django :: 4.0',
+        'Framework :: Django :: 4.1',
+        'Framework :: Django :: 4.2',
         'Programming Language :: Python',
         'Programming Language :: Python :: 3.7',
         'Programming Language :: Python :: 3.8',
         'Programming Language :: Python :: 3.9',
         'Programming Language :: Python :: 3.10',
+        'Programming Language :: Python :: 3.11',
         'Environment :: Web Environment',
     ],
 )
diff --git a/tox.ini b/tox.ini
--- a/tox.ini
+++ b/tox.ini
@@ -1,10 +1,19 @@
 [tox]
 downloadcache = {toxworkdir}/cache/
-envlist = py{37,38,39}-django{2.2,3.2},py{38,39}-django{4.0},py310-django{3.2,4.0}
+envlist =
+	py{37,38,39}-django{2.2,3.0,3.1,3.2}
+	py{38,39,310}-django{4.0}
+	py{38,39,310,311}-django{4.1}
+	py{310,311}-django{main}
 
 [testenv]
 commands = {envpython} runtests.py
 deps =
 	django2.2: django>=2.2,<3.0
+	django3.0: django>=3.0,<3.1
+	django3.1: django>=3.1,<3.2
 	django3.2: django>=3.2,<4.0
 	django4.0: django>=4.0,<4.1
+	django4.1: django>=4.1,<4.2
+	django4.2: django>=4.2,<4.3
+	djangomain: https://github.com/django/django/archive/main.tar.g