Skip to content

Commit d2cc52e

Browse files
Add support for Django 6.0 (#193)
* Add Django 6.0 to CI matrix * Use `condition` kwarg on supported Django versions * Improve compatibility with using Django upstream components
1 parent 4669d19 commit d2cc52e

File tree

8 files changed

+94
-10
lines changed

8 files changed

+94
-10
lines changed

.github/workflows/ci.yml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,12 @@ jobs:
1616
matrix:
1717
os: [windows-latest, macos-latest, ubuntu-latest]
1818
python-version: ["3.10", "3.11", "3.12", "3.13"]
19-
django-version: ["4.2", "5.0", "5.1", "5.2"]
19+
django-version: ["4.2", "5.0", "5.1", "5.2", "6.0a1"]
20+
exclude:
21+
- django-version: "6.0a1"
22+
python-version: "3.10"
23+
- django-version: "6.0a1"
24+
python-version: "3.11"
2025
steps:
2126
- uses: actions/checkout@v4
2227
- name: Set up Python ${{ matrix.python-version }}

django_tasks/__init__.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import importlib.metadata
77

8+
from django.conf import global_settings
89
from django.utils.connection import BaseConnectionHandler, ConnectionProxy
910
from django.utils.module_loading import import_string
1011

@@ -39,16 +40,23 @@ class TaskBackendHandler(BaseConnectionHandler[BaseTaskBackend]):
3940

4041
def configure_settings(self, settings: dict | None) -> dict:
4142
try:
42-
return super().configure_settings(settings)
43+
task_settings = super().configure_settings(settings)
4344
except AttributeError:
4445
# HACK: Force a default task backend.
4546
# Can be replaced with `django.conf.global_settings` once vendored.
46-
return {
47+
task_settings = None
48+
49+
if task_settings is None or task_settings is getattr(
50+
global_settings, self.settings_name, None
51+
):
52+
task_settings = {
4753
DEFAULT_TASK_BACKEND_ALIAS: {
4854
"BACKEND": "django_tasks.backends.immediate.ImmediateBackend"
4955
}
5056
}
5157

58+
return task_settings
59+
5260
def create_connection(self, alias: str) -> BaseTaskBackend:
5361
params = self.settings[alias]
5462

django_tasks/backends/database/migrations/0005_alter_dbtaskresult_priority_and_more.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,17 @@
11
# Generated by Django 4.2.13 on 2024-07-10 15:48
22

3+
from django import VERSION
34
from django.db import migrations, models
45

6+
if VERSION >= (5, 1):
7+
constraint = models.CheckConstraint(
8+
condition=models.Q(("priority__range", (-100, 100))), name="priority_range"
9+
)
10+
else:
11+
constraint = models.CheckConstraint(
12+
check=models.Q(("priority__range", (-100, 100))), name="priority_range"
13+
)
14+
515

616
class Migration(migrations.Migration):
717
dependencies = [
@@ -16,8 +26,6 @@ class Migration(migrations.Migration):
1626
),
1727
migrations.AddConstraint(
1828
model_name="dbtaskresult",
19-
constraint=models.CheckConstraint(
20-
check=models.Q(("priority__range", (-100, 100))), name="priority_range"
21-
),
29+
constraint=constraint,
2230
),
2331
]

django_tasks/backends/database/models.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
TaskError,
2424
TaskResultStatus,
2525
)
26+
from django_tasks.compat import TASK_CLASSES
2627
from django_tasks.utils import get_exception_traceback, get_module_path, retry
2728

2829
from .utils import normalize_uuid
@@ -155,12 +156,12 @@ class Meta:
155156
def task(self) -> Task[P, T]:
156157
task = import_string(self.task_path)
157158

158-
if not isinstance(task, Task):
159+
if not isinstance(task, TASK_CLASSES):
159160
raise SuspiciousOperation(
160161
f"Task {self.id} does not point to a Task ({self.task_path})"
161162
)
162163

163-
return task.using(
164+
return task.using( # type: ignore[no-any-return]
164165
priority=self.priority,
165166
queue_name=self.queue_name,
166167
run_after=None if self.run_after == get_date_max() else self.run_after,

django_tasks/backends/rq.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
TaskResult,
2929
TaskResultStatus,
3030
)
31+
from django_tasks.compat import TASK_CLASSES
3132
from django_tasks.exceptions import TaskResultDoesNotExist
3233
from django_tasks.signals import task_enqueued, task_finished, task_started
3334
from django_tasks.utils import get_module_path, get_random_id
@@ -75,12 +76,12 @@ def _execute(self) -> Any:
7576
def func(self) -> Task:
7677
func = super().func
7778

78-
if not isinstance(func, Task):
79+
if not isinstance(func, TASK_CLASSES):
7980
raise SuspiciousOperation(
8081
f"Task {self.id} does not point to a Task ({self.func_name})"
8182
)
8283

83-
return func
84+
return func # type: ignore[no-any-return]
8485

8586
@cached_property
8687
def task_result(self) -> TaskResult:

django_tasks/compat.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
try:
2+
from django.tasks.base import Task as DjangoTask
3+
except ImportError:
4+
DjangoTask = None
5+
6+
from .base import Task
7+
8+
__all__ = ["TASK_CLASSES"]
9+
10+
TASK_CLASSES = (Task, DjangoTask) if DjangoTask is not None else (Task,)

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ classifiers = [
2929
"Framework :: Django :: 5.0",
3030
"Framework :: Django :: 5.1",
3131
"Framework :: Django :: 5.2",
32+
"Framework :: Django :: 6.0",
3233
"Intended Audience :: Developers",
3334
"Operating System :: OS Independent",
3435
"Natural Language :: English",

tests/tests/test_compat.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
from unittest import skipUnless
2+
3+
from django import VERSION
4+
from django.test import SimpleTestCase, override_settings
5+
6+
from django_tasks import compat, task_backends
7+
from django_tasks.backends.immediate import ImmediateBackend
8+
from django_tasks.base import Task
9+
10+
HAS_DJANGO_TASKS = VERSION >= (6, 0)
11+
12+
13+
class DjangoCompatTestCase(SimpleTestCase):
14+
def test_uses_lib_tasks_by_default(self) -> None:
15+
self.assertIsInstance(task_backends["default"], ImmediateBackend)
16+
self.assertEqual(task_backends["default"].task_class, Task)
17+
18+
@skipUnless(HAS_DJANGO_TASKS, "Requires django.tasks")
19+
def test_django_using_lib_backend(self) -> None:
20+
from django.tasks import task_backends
21+
22+
with override_settings(
23+
TASKS={
24+
"default": {
25+
"BACKEND": "django_tasks.backends.immediate.ImmediateBackend"
26+
}
27+
}
28+
):
29+
self.assertIsInstance(task_backends["default"], ImmediateBackend)
30+
31+
@skipUnless(HAS_DJANGO_TASKS, "Requires django.tasks")
32+
def test_lib_using_django_backend(self) -> None:
33+
from django.tasks.backends.immediate import ImmediateBackend
34+
35+
with override_settings(
36+
TASKS={
37+
"default": {
38+
"BACKEND": "django.tasks.backends.immediate.ImmediateBackend"
39+
}
40+
}
41+
):
42+
self.assertIsInstance(task_backends["default"], ImmediateBackend)
43+
44+
def test_compat_has_django_task(self) -> None:
45+
self.assertIn(Task, compat.TASK_CLASSES)
46+
47+
if HAS_DJANGO_TASKS:
48+
from django.tasks.base import Task as DjangoTask
49+
50+
self.assertIn(DjangoTask, compat.TASK_CLASSES)

0 commit comments

Comments
 (0)