diff --git a/docs/changelog.rst b/docs/changelog.rst index e1c8a982..2474fb30 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -9,6 +9,15 @@ Compatibility * Official Django 6.1 support. +Bugfixes +^^^^^^^^ + +* Fixed the :fixture:`settings` fixture restoring the settings of an + ``override_settings`` that was entered after the fixture was set up but + exited before it was torn down, which leaked those settings into later + tests and could eventually cause a ``RecursionError`` + (`#809 `__). + v4.14.0 (2026-08-10) -------------------- diff --git a/pytest_django/fixtures.py b/pytest_django/fixtures.py index 2acf0e2f..09f16dae 100644 --- a/pytest_django/fixtures.py +++ b/pytest_django/fixtures.py @@ -562,8 +562,15 @@ def __init__( assert _is_pytest_django, ( "Settings should only be instantiated from the `settings` fixture" ) + from django.test import override_settings + + # Disabled last, so the settings active when the fixture was set up + # are restored even if an override_settings entered later exits + # before the fixture is finalized. + base = override_settings() + base.enable() self._to_restore: list[django.test.override_settings] - object.__setattr__(self, "_to_restore", []) + object.__setattr__(self, "_to_restore", [base]) def __delattr__(self, attr: str) -> None: from django.test import override_settings diff --git a/tests/test_fixtures.py b/tests/test_fixtures.py index 5626de01..01803cb4 100644 --- a/tests/test_fixtures.py +++ b/tests/test_fixtures.py @@ -467,6 +467,38 @@ def test_set_non_existent(settings): ] ) + def test_override_settings_exited_before_teardown( + self, django_pytester: DjangoPytester + ) -> None: + django_pytester.create_test_module( + """ + import pytest + + from django.conf import settings as django_settings + from django.test import override_settings + + + @pytest.fixture + def overrides(): + with override_settings(OVERRIDDEN=1): + yield + + + def test_override_inside_settings_fixture(settings, overrides): + settings.ADDED = 1 + assert django_settings.OVERRIDDEN == 1 + assert django_settings.ADDED == 1 + + + def test_settings_restored(): + assert not hasattr(django_settings, "OVERRIDDEN") + assert not hasattr(django_settings, "ADDED") + """ + ) + + result = django_pytester.runpytest_subprocess("-v") + result.assert_outcomes(passed=2) + class TestLiveServer: @pytest.mark.skipif("PYTEST_XDIST_WORKER" in os.environ, reason="xdist in use")