From 497d9cdc67f0bdae929fcde677b5f441e94a6c8b Mon Sep 17 00:00:00 2001 From: Elias Hernandis Date: Wed, 18 Feb 2026 08:10:40 +0100 Subject: [PATCH 1/4] Fixed #36951 -- Removed empty exc_info from log_task_finished signal handler. Before, if no exception occurred, "None Type: None" was logged. --- django/tasks/signals.py | 5 +++-- docs/releases/6.0.3.txt | 3 +++ tests/tasks/test_immediate_backend.py | 9 +++++++++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/django/tasks/signals.py b/django/tasks/signals.py index 288fe08e328a..919dae022221 100644 --- a/django/tasks/signals.py +++ b/django/tasks/signals.py @@ -49,6 +49,8 @@ def log_task_started(sender, task_result, **kwargs): @receiver(task_finished) def log_task_finished(sender, task_result, **kwargs): + # Signal is sent inside exception handlers, so exc_info() is available. + exc_info = sys.exc_info() logger.log( ( logging.ERROR @@ -59,6 +61,5 @@ def log_task_finished(sender, task_result, **kwargs): task_result.id, task_result.task.module_path, task_result.status, - # Signal is sent inside exception handlers, so exc_info() is available. - exc_info=sys.exc_info(), + exc_info=exc_info if exc_info[0] else None, ) diff --git a/docs/releases/6.0.3.txt b/docs/releases/6.0.3.txt index 3f8c93e63d50..048921db8bdb 100644 --- a/docs/releases/6.0.3.txt +++ b/docs/releases/6.0.3.txt @@ -19,3 +19,6 @@ Bugfixes * Fixed a visual regression where fieldset legends were misaligned in the admin (:ticket:`36920`). + +* Prevented the :data:`django.tasks.signals.task_finished` signal from writing + extraneous log messages when no exceptions are encountered (:ticket:`36951`). diff --git a/tests/tasks/test_immediate_backend.py b/tests/tasks/test_immediate_backend.py index 356e9ab2649d..36b63faff801 100644 --- a/tests/tasks/test_immediate_backend.py +++ b/tests/tasks/test_immediate_backend.py @@ -228,6 +228,15 @@ def test_failed_logs(self): self.assertIn("state=FAILED", captured_logs.output[2]) self.assertIn(result.id, captured_logs.output[2]) + def test_successful_task_no_none_in_logs(self): + with self.assertLogs("django.tasks", level="DEBUG") as captured_logs: + result = test_tasks.noop_task.enqueue() + + self.assertEqual(result.status, TaskResultStatus.SUCCESSFUL) + + for log_output in captured_logs.output: + self.assertNotIn("None", log_output) + def test_takes_context(self): result = test_tasks.get_task_id.enqueue() From 4aefc9ea51cc2d78f43b1dc2aa69732e55d18a56 Mon Sep 17 00:00:00 2001 From: LincolnPuzey <18750802+LincolnPuzey@users.noreply.github.com> Date: Thu, 26 Feb 2026 03:55:21 +0800 Subject: [PATCH 2/4] Fixed #36848 -- Mentioned BadRequest exception in docs/ref/views.txt. --- docs/ref/views.txt | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/docs/ref/views.txt b/docs/ref/views.txt index b60ffc2ed8ae..d7ad83a64ac9 100644 --- a/docs/ref/views.txt +++ b/docs/ref/views.txt @@ -148,16 +148,20 @@ The 400 (bad request) view .. function:: defaults.bad_request(request, exception, template_name='400.html') -When a :exc:`~django.core.exceptions.SuspiciousOperation` is raised in Django, +Similarly, Django has a view to handle 400 Bad Request errors. + +This view either produces a "Bad Request" message or loads and renders the +template ``400.html`` if you created it in your root template directory. +It returns with status code 400 indicating that the error condition was the +result of a client operation. By default, nothing related to the exception that +triggered the view is passed to the template context, as the exception message +might contain sensitive information like filesystem paths. + +``django.views.defaults.bad_request`` is triggered by a +:exc:`~django.core.exceptions.BadRequest` exception. Also, when a +:exc:`~django.core.exceptions.SuspiciousOperation` is raised in Django, it may be handled by a component of Django (for example resetting the session data). If not specifically handled, Django will consider the current request a -'bad request' instead of a server error. - -``django.views.defaults.bad_request``, is otherwise very similar to the -``server_error`` view, but returns with the status code 400 indicating that -the error condition was the result of a client operation. By default, nothing -related to the exception that triggered the view is passed to the template -context, as the exception message might contain sensitive information like -filesystem paths. +'bad request' instead of a server error, and handle it with ``bad_request``. ``bad_request`` views are also only used when :setting:`DEBUG` is ``False``. From a91e03d7c388ada569a212bafdbdff9c9bff47f2 Mon Sep 17 00:00:00 2001 From: pmppk Date: Wed, 25 Feb 2026 16:53:22 -0500 Subject: [PATCH 3/4] Refs #36879, #36936 -- Fixed typo in RedisCacheTests.test_client_driver_info. --- tests/cache/tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/cache/tests.py b/tests/cache/tests.py index b36e3a6a0602..a94a8a63cd04 100644 --- a/tests/cache/tests.py +++ b/tests/cache/tests.py @@ -1992,7 +1992,7 @@ def test_client_driver_info(self): if {"lib-name", "lib-ver"}.issubset(client_info): version = django.get_version() if hasattr(self.lib, "DriverInfo"): - info = self._lib.DriverInfo().add_upstream_driver("django", version) + info = self.lib.DriverInfo().add_upstream_driver("django", version) correct_lib_name = info.formatted_name else: correct_lib_name = f"redis-py(django_v{version})" From f991a1883889a520679fe41112281435581211e1 Mon Sep 17 00:00:00 2001 From: pmppk Date: Wed, 25 Feb 2026 17:02:00 -0500 Subject: [PATCH 4/4] Refs #36652, #36936 -- Improved path manipulation in a migration test launching a subprocess. --- tests/migrations/test_loader.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/migrations/test_loader.py b/tests/migrations/test_loader.py index 705d49d06959..52aeb8c21bbd 100644 --- a/tests/migrations/test_loader.py +++ b/tests/migrations/test_loader.py @@ -698,7 +698,9 @@ def test_loading_order_does_not_create_circular_dependency(self): test_settings.write(f"INSTALLED_APPS += {INSTALLED_APPS}\n") test_environ = os.environ.copy() - test_environ["PYTHONPATH"] = str(tests_dir) + test_python_path = sys.path.copy() + test_python_path.append(str(tests_dir)) + test_environ["PYTHONPATH"] = os.pathsep.join(test_python_path) # Ensure deterministic failures. test_environ["PYTHONHASHSEED"] = "1"