From 9bd8b53de50971666c17550fb66ea9dda35e928a Mon Sep 17 00:00:00 2001 From: Javier Buzzi Date: Mon, 1 Dec 2025 04:16:57 -0500 Subject: [PATCH 01/11] Remove py39-dj42-postgres from envlist --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 5ffeeead..255f016e 100644 --- a/tox.ini +++ b/tox.ini @@ -1,10 +1,10 @@ [tox] envlist = + py314-dj{main,52,51}-postgres py313-dj{main,52,51}-postgres py312-dj{main,52,51,42}-postgres py311-dj{main,52,51,42}-postgres py310-dj{main,52,51,42}-postgres - py39-dj42-postgres linting [testenv] From fd22ac8882afe6a666fe65e23114d47fd07f28ac Mon Sep 17 00:00:00 2001 From: Javier Buzzi Date: Mon, 1 Dec 2025 04:33:58 -0500 Subject: [PATCH 02/11] Add python 3.14 --- .github/workflows/main.yml | 20 ++++++++++++-------- README.rst | 2 +- docs/changelog.rst | 5 +++++ pyproject.toml | 4 ++-- 4 files changed, 20 insertions(+), 11 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 6fd5edc1..2fe22683 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -78,12 +78,20 @@ jobs: matrix: include: - name: linting,docs - python: '3.13' + python: '3.14' allow_failure: false # Explicitly test min pytest. - - name: py313-dj52-sqlite-pytestmin-coverage - python: '3.13' + - name: py314-dj52-sqlite-pytestmin-coverage + python: '3.14' + allow_failure: false + + - name: py314-dj52-postgres-xdist-coverage + python: '3.14' + allow_failure: false + + - name: py314-dj51-postgres-xdist-coverage + python: '3.14' allow_failure: false - name: py313-dj52-postgres-xdist-coverage @@ -126,10 +134,6 @@ jobs: python: '3.10' allow_failure: false - - name: py39-dj42-mysql-xdist-coverage - python: '3.9' - allow_failure: false - - name: py313-djmain-sqlite-coverage python: '3.13' allow_failure: true @@ -148,7 +152,7 @@ jobs: # pypy3: not included with coverage reports (much slower then). - name: pypy3-dj42-postgres - python: 'pypy3.9' + python: 'pypy3.10' allow_failure: false check: # This job does nothing and is only used for the branch protection diff --git a/README.rst b/README.rst index 87291333..6b31e079 100644 --- a/README.rst +++ b/README.rst @@ -34,7 +34,7 @@ pytest-django allows you to test your Django project/applications with the * Django: 4.2, 5.1, 5.2 and latest main branch (compatible at the time of each release) - * Python: CPython>=3.9 or PyPy 3 + * Python: CPython>=3.10 or PyPy 3 * pytest: >=7.0 For compatibility with older versions, use previous pytest-django releases. diff --git a/docs/changelog.rst b/docs/changelog.rst index d5cd706c..59fcce51 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -4,6 +4,11 @@ Changelog v4.12.0 (Not released yet) -------------------------- +Compatibility +^^^^^^^^^^^^^ + +* Official Python 3.14 support. + Improvements ^^^^^^^^^^^^ diff --git a/pyproject.toml b/pyproject.toml index 75915cc8..34843643 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,7 +9,7 @@ build-backend = "setuptools.build_meta" name = "pytest-django" description = "A Django plugin for pytest." readme = "README.rst" -requires-python = ">=3.9" +requires-python = ">=3.10" dynamic = ["version"] authors = [ { name = "Andreas Pelme", email = "andreas@pelme.se" }, @@ -28,11 +28,11 @@ classifiers = [ "License :: OSI Approved :: BSD License", "Operating System :: OS Independent", "Programming Language :: Python", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", "Topic :: Software Development :: Testing", From 69d8f445c933bdb810e094f581b806ad8371414e Mon Sep 17 00:00:00 2001 From: Javier Buzzi Date: Mon, 1 Dec 2025 04:35:21 -0500 Subject: [PATCH 03/11] Update main.yml --- .github/workflows/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 2fe22683..32893b4c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -82,8 +82,8 @@ jobs: allow_failure: false # Explicitly test min pytest. - - name: py314-dj52-sqlite-pytestmin-coverage - python: '3.14' + - name: py313-dj52-sqlite-pytestmin-coverage + python: '3.13' allow_failure: false - name: py314-dj52-postgres-xdist-coverage From 23da98de1bb6e5a39e029e34a39c36673489aa0a Mon Sep 17 00:00:00 2001 From: Javier Buzzi Date: Mon, 1 Dec 2025 04:36:29 -0500 Subject: [PATCH 04/11] Update Python version in CI workflow --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 32893b4c..8829e4ed 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -78,7 +78,7 @@ jobs: matrix: include: - name: linting,docs - python: '3.14' + python: '3.13' allow_failure: false # Explicitly test min pytest. From 2217b64e9546c55976f7306aab700abfaff10446 Mon Sep 17 00:00:00 2001 From: Javier Buzzi Date: Mon, 1 Dec 2025 04:44:03 -0500 Subject: [PATCH 05/11] Update job names and allow_failure settings --- .github/workflows/main.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8829e4ed..9e1deb81 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -86,11 +86,11 @@ jobs: python: '3.13' allow_failure: false - - name: py314-dj52-postgres-xdist-coverage + - name: py314-djmain-postgres-xdist-coverage python: '3.14' - allow_failure: false + allow_failure: true - - name: py314-dj51-postgres-xdist-coverage + - name: py314-dj52-postgres-xdist-coverage python: '3.14' allow_failure: false @@ -140,7 +140,7 @@ jobs: - name: py313-dj52-sqlite-coverage python: '3.13' - allow_failure: true + allow_failure: false - name: py312-dj51-sqlite-xdist-coverage python: '3.12' From cdfa454a352f08debc70837fd94cb52fc0c8279f Mon Sep 17 00:00:00 2001 From: Javier Buzzi Date: Mon, 1 Dec 2025 04:55:25 -0500 Subject: [PATCH 06/11] min python 3.10 changes --- .github/workflows/main.yml | 170 ------------------------------------- pytest_django/asserts.py | 19 +++-- pytest_django/fixtures.py | 8 +- 3 files changed, 14 insertions(+), 183 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 9e1deb81..e69de29b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,170 +0,0 @@ -name: main - -on: - push: - branches: - - main - tags: - - "*" - pull_request: - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} - -env: - PYTEST_ADDOPTS: "--color=yes" - -# Set permissions at the job level. -permissions: {} - -jobs: - test: - runs-on: ubuntu-24.04 - continue-on-error: ${{ matrix.allow_failure }} - timeout-minutes: 15 - permissions: - contents: read - security-events: write - env: - TOXENV: ${{ matrix.name }} - steps: - - uses: actions/checkout@v5 - with: - persist-credentials: false - - - uses: actions/setup-python@v6 - with: - python-version: ${{ matrix.python }} - - - name: Setup mysql - if: contains(matrix.name, 'mysql') - run: | - sudo systemctl start mysql.service - echo "TEST_DB_USER=root" >> $GITHUB_ENV - echo "TEST_DB_PASSWORD=root" >> $GITHUB_ENV - - - name: Setup postgresql - if: contains(matrix.name, 'postgres') - run: | - sudo systemctl start postgresql.service - sudo -u postgres createuser --createdb $USER - - - name: Install dependencies - run: | - python -m pip install uv - uv tool install tox==4.28.4 --with tox-uv - - - name: Run tox - run: tox - - - name: Upload zizmor SARIF report into the GitHub repo code scanning - if: contains(matrix.name, 'linting') - uses: github/codeql-action/upload-sarif@v4 - with: - sarif_file: zizmor.sarif - category: zizmor - - - name: Report coverage - if: contains(matrix.name, 'coverage') - uses: codecov/codecov-action@v5 - with: - fail_ci_if_error: true - files: ./coverage.xml - token: ${{ secrets.CODECOV_TOKEN }} - - strategy: - fail-fast: false - matrix: - include: - - name: linting,docs - python: '3.13' - allow_failure: false - - # Explicitly test min pytest. - - name: py313-dj52-sqlite-pytestmin-coverage - python: '3.13' - allow_failure: false - - - name: py314-djmain-postgres-xdist-coverage - python: '3.14' - allow_failure: true - - - name: py314-dj52-postgres-xdist-coverage - python: '3.14' - allow_failure: false - - - name: py313-dj52-postgres-xdist-coverage - python: '3.13' - allow_failure: false - - - name: py313-dj51-postgres-xdist-coverage - python: '3.13' - allow_failure: false - - - name: py312-dj42-postgres-xdist-coverage - python: '3.12' - allow_failure: false - - - name: py311-dj50-postgres-xdist-coverage - python: '3.11' - allow_failure: false - - - name: py311-dj42-postgres-xdist-coverage - python: '3.11' - allow_failure: false - - - name: py310-dj52-postgres-xdist-coverage - python: '3.10' - allow_failure: false - - - name: py310-dj51-postgres-xdist-coverage - python: '3.10' - allow_failure: false - - - name: py310-dj42-postgres-xdist-coverage - python: '3.10' - allow_failure: false - - - name: py311-dj51-mysql-coverage - python: '3.11' - allow_failure: false - - - name: py310-dj42-mysql-coverage - python: '3.10' - allow_failure: false - - - name: py313-djmain-sqlite-coverage - python: '3.13' - allow_failure: true - - - name: py313-dj52-sqlite-coverage - python: '3.13' - allow_failure: false - - - name: py312-dj51-sqlite-xdist-coverage - python: '3.12' - allow_failure: false - - - name: py311-dj42-sqlite-xdist-coverage - python: '3.11' - allow_failure: false - - # pypy3: not included with coverage reports (much slower then). - - name: pypy3-dj42-postgres - python: 'pypy3.10' - allow_failure: false - - check: # This job does nothing and is only used for the branch protection - if: always() - - needs: - - test - - runs-on: ubuntu-24.04 - - steps: - - name: Decide whether the needed jobs succeeded or failed - uses: re-actors/alls-green@2765efec08f0fd63e83ad900f5fd75646be69ff6 - with: - jobs: ${{ toJSON(needs) }} diff --git a/pytest_django/asserts.py b/pytest_django/asserts.py index 76a45809..1066516d 100644 --- a/pytest_django/asserts.py +++ b/pytest_django/asserts.py @@ -5,7 +5,7 @@ from __future__ import annotations from functools import wraps -from typing import TYPE_CHECKING, Any, Callable +from typing import TYPE_CHECKING from django import VERSION from django.test import LiveServerTestCase, SimpleTestCase, TestCase, TransactionTestCase @@ -24,6 +24,15 @@ class MessagesTestCase(MessagesTestMixin, TestCase): else: test_case = TestCase("run") +if TYPE_CHECKING: + from collections.abc import Callable, Collection, Iterator, Sequence + from contextlib import AbstractContextManager + from typing import overload, Any + + from django import forms + from django.db.models import Model, QuerySet, RawQuerySet + from django.http.response import HttpResponseBase + def _wrapper(name: str) -> Callable[..., Any]: func = getattr(test_case, name) @@ -55,14 +64,6 @@ def assertion_func(*args: Any, **kwargs: Any) -> Any: if TYPE_CHECKING: - from collections.abc import Collection, Iterator, Sequence - from contextlib import AbstractContextManager - from typing import overload - - from django import forms - from django.db.models import Model, QuerySet, RawQuerySet - from django.http.response import HttpResponseBase - def assertRedirects( response: HttpResponseBase, expected_url: str, diff --git a/pytest_django/fixtures.py b/pytest_django/fixtures.py index 6f7929be..9a4f8eaf 100644 --- a/pytest_django/fixtures.py +++ b/pytest_django/fixtures.py @@ -3,7 +3,7 @@ from __future__ import annotations import os -from collections.abc import Generator, Iterable, Sequence +from collections.abc import Callable, Generator, Iterable, Sequence from contextlib import AbstractContextManager, contextmanager from functools import partial from typing import TYPE_CHECKING, Protocol @@ -16,7 +16,7 @@ if TYPE_CHECKING: - from typing import Any, Callable, Literal, Optional, Union + from typing import Any, Literal, Union import django import django.test @@ -24,8 +24,8 @@ from . import DjangoDbBlocker from .django_compat import _User, _UserModel - _DjangoDbDatabases = Optional[Union[Literal["__all__"], Iterable[str]]] - _DjangoDbAvailableApps = Optional[list[str]] + _DjangoDbDatabases = Union[Literal["__all__"] | Iterable[str]] + _DjangoDbAvailableApps = list[str] | None # transaction, reset_sequences, databases, serialized_rollback, available_apps _DjangoDb = tuple[bool, bool, _DjangoDbDatabases, bool, _DjangoDbAvailableApps] From f9c99f84cd2de7f9202e1b28e42fca4fa1fc2f61 Mon Sep 17 00:00:00 2001 From: Javier Buzzi Date: Mon, 1 Dec 2025 04:57:11 -0500 Subject: [PATCH 07/11] oopss --- .github/workflows/main.yml | 169 +++++++++++++++++++++++++++++++++++++ 1 file changed, 169 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index e69de29b..2016a7da 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -0,0 +1,169 @@ +name: main + + + +on: + push: + branches: + - main + tags: + - "*" + pull_request: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} + +env: + PYTEST_ADDOPTS: "--color=yes" + +# Set permissions at the job level. +permissions: {} + +jobs: + test: + runs-on: ubuntu-24.04 + continue-on-error: ${{ matrix.allow_failure }} + timeout-minutes: 15 + permissions: + contents: read + security-events: write + env: + TOXENV: ${{ matrix.name }} + steps: + - uses: actions/checkout@v5 + with: + persist-credentials: false + + - uses: actions/setup-python@v6 + with: + python-version: ${{ matrix.python }} + + - name: Setup mysql + if: contains(matrix.name, 'mysql') + run: | + sudo systemctl start mysql.service + echo "TEST_DB_USER=root" >> $GITHUB_ENV + echo "TEST_DB_PASSWORD=root" >> $GITHUB_ENV + - name: Setup postgresql + if: contains(matrix.name, 'postgres') + run: | + sudo systemctl start postgresql.service + sudo -u postgres createuser --createdb $USER + - name: Install dependencies + run: | + python -m pip install uv + uv tool install tox==4.28.4 --with tox-uv + - name: Run tox + run: tox + + - name: Upload zizmor SARIF report into the GitHub repo code scanning + if: contains(matrix.name, 'linting') + uses: github/codeql-action/upload-sarif@v4 + with: + sarif_file: zizmor.sarif + category: zizmor + + - name: Report coverage + if: contains(matrix.name, 'coverage') + uses: codecov/codecov-action@v5 + with: + fail_ci_if_error: true + files: ./coverage.xml + token: ${{ secrets.CODECOV_TOKEN }} + + strategy: + fail-fast: false + matrix: + include: + - name: linting,docs + python: '3.13' + allow_failure: false + + # Explicitly test min pytest. + - name: py313-dj52-sqlite-pytestmin-coverage + python: '3.13' + allow_failure: false + + - name: py314-djmain-postgres-xdist-coverage + python: '3.14' + allow_failure: true + + - name: py314-dj52-postgres-xdist-coverage + python: '3.14' + allow_failure: false + + - name: py313-dj52-postgres-xdist-coverage + python: '3.13' + allow_failure: false + + - name: py313-dj51-postgres-xdist-coverage + python: '3.13' + allow_failure: false + + - name: py312-dj42-postgres-xdist-coverage + python: '3.12' + allow_failure: false + + - name: py311-dj50-postgres-xdist-coverage + python: '3.11' + allow_failure: false + + - name: py311-dj42-postgres-xdist-coverage + python: '3.11' + allow_failure: false + + - name: py310-dj52-postgres-xdist-coverage + python: '3.10' + allow_failure: false + + - name: py310-dj51-postgres-xdist-coverage + python: '3.10' + allow_failure: false + + - name: py310-dj42-postgres-xdist-coverage + python: '3.10' + allow_failure: false + + - name: py311-dj51-mysql-coverage + python: '3.11' + allow_failure: false + + - name: py310-dj42-mysql-coverage + python: '3.10' + allow_failure: false + + - name: py313-djmain-sqlite-coverage + python: '3.13' + allow_failure: true + + - name: py313-dj52-sqlite-coverage + python: '3.13' + allow_failure: false + + - name: py312-dj51-sqlite-xdist-coverage + python: '3.12' + allow_failure: false + + - name: py311-dj42-sqlite-xdist-coverage + python: '3.11' + allow_failure: false + + # pypy3: not included with coverage reports (much slower then). + - name: pypy3-dj42-postgres + python: 'pypy3.10' + allow_failure: false + + check: # This job does nothing and is only used for the branch protection + if: always() + + needs: + - test + + runs-on: ubuntu-24.04 + + steps: + - name: Decide whether the needed jobs succeeded or failed + uses: re-actors/alls-green@2765efec08f0fd63e83ad900f5fd75646be69ff6 + with: + jobs: ${{ toJSON(needs) }} \ No newline at end of file From c6e15eaaa7ef036315316e297cd840b7b0cced01 Mon Sep 17 00:00:00 2001 From: Javier Buzzi Date: Mon, 1 Dec 2025 04:58:03 -0500 Subject: [PATCH 08/11] .. --- .github/workflows/main.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 2016a7da..6d909771 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,7 +1,5 @@ name: main - - on: push: branches: @@ -166,4 +164,4 @@ jobs: - name: Decide whether the needed jobs succeeded or failed uses: re-actors/alls-green@2765efec08f0fd63e83ad900f5fd75646be69ff6 with: - jobs: ${{ toJSON(needs) }} \ No newline at end of file + jobs: ${{ toJSON(needs) }} From 3f177e8212019f2401b85fa07d161b4da3a9628f Mon Sep 17 00:00:00 2001 From: Javier Buzzi Date: Mon, 1 Dec 2025 04:58:41 -0500 Subject: [PATCH 09/11] .. --- .github/workflows/main.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 6d909771..9e1deb81 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -43,15 +43,18 @@ jobs: sudo systemctl start mysql.service echo "TEST_DB_USER=root" >> $GITHUB_ENV echo "TEST_DB_PASSWORD=root" >> $GITHUB_ENV + - name: Setup postgresql if: contains(matrix.name, 'postgres') run: | sudo systemctl start postgresql.service sudo -u postgres createuser --createdb $USER + - name: Install dependencies run: | python -m pip install uv uv tool install tox==4.28.4 --with tox-uv + - name: Run tox run: tox From 2ee811921bdd944e65c8b60f3e2221c6de19423c Mon Sep 17 00:00:00 2001 From: kingbuzzman Date: Mon, 1 Dec 2025 05:08:10 -0500 Subject: [PATCH 10/11] Fully compatible --- pytest_django/asserts.py | 3 ++- pytest_django/fixtures.py | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/pytest_django/asserts.py b/pytest_django/asserts.py index 1066516d..0dbc47bf 100644 --- a/pytest_django/asserts.py +++ b/pytest_django/asserts.py @@ -27,7 +27,7 @@ class MessagesTestCase(MessagesTestMixin, TestCase): if TYPE_CHECKING: from collections.abc import Callable, Collection, Iterator, Sequence from contextlib import AbstractContextManager - from typing import overload, Any + from typing import Any, overload from django import forms from django.db.models import Model, QuerySet, RawQuerySet @@ -64,6 +64,7 @@ def assertion_func(*args: Any, **kwargs: Any) -> Any: if TYPE_CHECKING: + def assertRedirects( response: HttpResponseBase, expected_url: str, diff --git a/pytest_django/fixtures.py b/pytest_django/fixtures.py index 9a4f8eaf..209133ef 100644 --- a/pytest_django/fixtures.py +++ b/pytest_django/fixtures.py @@ -16,7 +16,7 @@ if TYPE_CHECKING: - from typing import Any, Literal, Union + from typing import Any, Literal import django import django.test @@ -24,7 +24,7 @@ from . import DjangoDbBlocker from .django_compat import _User, _UserModel - _DjangoDbDatabases = Union[Literal["__all__"] | Iterable[str]] + _DjangoDbDatabases = Literal["__all__"] | Iterable[str] | None _DjangoDbAvailableApps = list[str] | None # transaction, reset_sequences, databases, serialized_rollback, available_apps _DjangoDb = tuple[bool, bool, _DjangoDbDatabases, bool, _DjangoDbAvailableApps] From 6beb5e25cd4dc36e8784657e98590aa5d2a535fd Mon Sep 17 00:00:00 2001 From: Javier Buzzi Date: Mon, 1 Dec 2025 06:46:41 -0500 Subject: [PATCH 11/11] Update changelog for Python version support changes --- docs/changelog.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog.rst b/docs/changelog.rst index 59fcce51..8c256e4e 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -8,6 +8,7 @@ Compatibility ^^^^^^^^^^^^^ * Official Python 3.14 support. +* Dropped support for Python 3.9, minimum version is now Python 3.10. Improvements ^^^^^^^^^^^^