diff --git a/django/test/utils.py b/django/test/utils.py index 9bb9257861e9..e69f3494825e 100644 --- a/django/test/utils.py +++ b/django/test/utils.py @@ -787,6 +787,17 @@ def disable(self): ) +requires_gil = skipUnless( + # sys._is_gil_enabled() is available on Python 3.13+, and only returns + # False on a free-threaded build with the GIL disabled. Assume the GIL is + # enabled otherwise. + getattr(sys, "_is_gil_enabled", lambda: True)(), + "This test relies on CPython's reference counting to free objects as soon " + "as they become unreachable, which isn't guaranteed on a free-threaded " + "build where the cyclic garbage collector may reclaim them instead.", +) + + @contextmanager def extend_sys_path(*paths): """Context manager to temporarily add paths to sys.path.""" diff --git a/docs/ref/contrib/gis/gdal.txt b/docs/ref/contrib/gis/gdal.txt index 44383b24a134..540140c32b8a 100644 --- a/docs/ref/contrib/gis/gdal.txt +++ b/docs/ref/contrib/gis/gdal.txt @@ -2091,9 +2091,9 @@ supported. You can use them by prepending the provided path with the appropriate ``/vsi*/`` prefix. See the `GDAL Virtual Filesystems documentation`_ for more details. -.. warning: +.. warning:: - Rasters with names starting with `/vsi*/` will be treated as rasters from + Rasters with names starting with ``/vsi*/`` will be treated as rasters from the GDAL virtual filesystems. Django doesn't perform any extra validation. Compressed rasters diff --git a/tests/backends/base/test_base.py b/tests/backends/base/test_base.py index 2d19840851fb..e096802ff139 100644 --- a/tests/backends/base/test_base.py +++ b/tests/backends/base/test_base.py @@ -10,7 +10,7 @@ skipUnlessDBFeature, ) from django.test.runner import DebugSQLTextTestResult -from django.test.utils import CaptureQueriesContext, override_settings +from django.test.utils import CaptureQueriesContext, override_settings, requires_gil from ..models import Person, Square @@ -62,6 +62,7 @@ def test_check_database_version_supported_with_none_as_database_version(self): with patch.object(connection.features, "minimum_database_version", None): connection.check_database_version_supported() + @requires_gil def test_release_memory_without_garbage_collection(self): # Schedule the restore of the garbage collection settings. self.addCleanup(gc.set_debug, 0) diff --git a/tests/model_regress/test_state.py b/tests/model_regress/test_state.py index 8abcc58382c5..fc1e1551f6ea 100644 --- a/tests/model_regress/test_state.py +++ b/tests/model_regress/test_state.py @@ -2,7 +2,7 @@ from django.db.models.base import ModelState, ModelStateFieldsCacheDescriptor from django.test import SimpleTestCase -from django.test.utils import garbage_collect +from django.test.utils import garbage_collect, requires_gil from .models import Worker, WorkerProfile @@ -11,6 +11,7 @@ class ModelStateTests(SimpleTestCase): def test_fields_cache_descriptor(self): self.assertIsInstance(ModelState.fields_cache, ModelStateFieldsCacheDescriptor) + @requires_gil def test_one_to_one_field_cycle_collection(self): self.addCleanup(gc.set_debug, gc.get_debug()) gc.set_debug(gc.DEBUG_SAVEALL) diff --git a/tests/select_related/tests.py b/tests/select_related/tests.py index 97c146ed92c7..06faff4f4b40 100644 --- a/tests/select_related/tests.py +++ b/tests/select_related/tests.py @@ -3,7 +3,7 @@ from django.core.exceptions import FieldError from django.db.models import FETCH_PEERS from django.test import SimpleTestCase, TestCase -from django.test.utils import garbage_collect, ignore_warnings +from django.test.utils import garbage_collect, ignore_warnings, requires_gil from django.utils.deprecation import RemovedInDjango70Warning from .models import ( @@ -62,15 +62,19 @@ def setUpTestData(cls): ) def setup_gc_debug(self): + self.addCleanup(gc.garbage.clear) self.addCleanup(gc.set_debug, 0) self.addCleanup(gc.enable) gc.disable() garbage_collect() gc.set_debug(gc.DEBUG_SAVEALL) - def assert_no_memory_leaks(self): + def assert_no_local_function_leaks(self): garbage_collect() - self.assertEqual(gc.garbage, []) + local_functions_leaked = [ + obj for obj in gc.garbage if "" in getattr(obj, "__qualname__", "") + ] + self.assertEqual(local_functions_leaked, []) def test_access_fks_without_select_related(self): """ @@ -157,10 +161,11 @@ def test_select_related_with_extra(self): ) self.assertEqual(s.id + 10, s.a) + @requires_gil def test_select_related_memory_leak(self): self.setup_gc_debug() list(Species.objects.select_related("genus")) - self.assert_no_memory_leaks() + self.assert_no_local_function_leaks() def test_certain_fields(self): """