summaryrefslogtreecommitdiff
path: root/django/core
diff options
context:
space:
mode:
authorThomas Grainger <tagrain@gmail.com>2025-11-07 11:14:06 -0500
committerJacob Walls <jacobtylerwalls@gmail.com>2025-11-07 12:23:31 -0500
commit2501958b5127020411df6271445ccfd0906df70e (patch)
tree8b4acb38b1f3372e9e79328f84efa0f2eca5f63a /django/core
parent796cf3d325b4a1b9d9b5361c2c8c28a5edcfe89b (diff)
Refs #36315 -- Replaced manual task and cancellation handling with TaskGroup in ASGIHandler.
Diffstat (limited to 'django/core')
-rw-r--r--django/core/handlers/asgi.py56
1 files changed, 16 insertions, 40 deletions
diff --git a/django/core/handlers/asgi.py b/django/core/handlers/asgi.py
index dae545f3b3..af8582d539 100644
--- a/django/core/handlers/asgi.py
+++ b/django/core/handlers/asgi.py
@@ -187,49 +187,25 @@ class ASGIHandler(base.BaseHandler):
await sync_to_async(error_response.close)()
return
- async def process_request(request, send):
- response = await self.run_get_response(request)
+ class RequestProcessed(Exception):
+ pass
+
+ response = None
+ try:
try:
- await self.send_response(response, send)
- except asyncio.CancelledError:
- # Client disconnected during send_response (ignore exception).
+ async with asyncio.TaskGroup() as tg:
+ tg.create_task(self.listen_for_disconnect(receive))
+ response = await self.run_get_response(request)
+ await self.send_response(response, send)
+ raise RequestProcessed
+ except* (RequestProcessed, RequestAborted):
pass
+ except BaseExceptionGroup as exception_group:
+ if len(exception_group.exceptions) == 1:
+ raise exception_group.exceptions[0]
+ raise
- return response
-
- # Try to catch a disconnect while getting response.
- tasks = [
- # Check the status of these tasks and (optionally) terminate them
- # in this order. The listen_for_disconnect() task goes first
- # because it should not raise unexpected errors that would prevent
- # us from cancelling process_request().
- asyncio.create_task(self.listen_for_disconnect(receive)),
- asyncio.create_task(process_request(request, send)),
- ]
- await asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED)
- # Now wait on both tasks (they may have both finished by now).
- for task in tasks:
- if task.done():
- try:
- task.result()
- except RequestAborted:
- # Ignore client disconnects.
- pass
- except AssertionError:
- body_file.close()
- raise
- else:
- # Allow views to handle cancellation.
- task.cancel()
- try:
- await task
- except asyncio.CancelledError:
- # Task re-raised the CancelledError as expected.
- pass
-
- try:
- response = tasks[1].result()
- except asyncio.CancelledError:
+ if response is None:
await signals.request_finished.asend(sender=self.__class__)
else:
await sync_to_async(response.close)()