summaryrefslogtreecommitdiff
path: root/django/dispatch
diff options
context:
space:
mode:
authorThomas Grainger <tagrain@gmail.com>2025-04-10 09:43:32 +0100
committerJacob Walls <jacobtylerwalls@gmail.com>2025-11-07 13:17:25 -0500
commit27687475265f88bc0a0bcbfe2ba26da306bbfc20 (patch)
tree782339a9a58101b25dfd065426016978bcfa08eb /django/dispatch
parent2501958b5127020411df6271445ccfd0906df70e (diff)
Fixed #36315 -- Used TaskGroup instead of asyncio.gather().
Diffstat (limited to 'django/dispatch')
-rw-r--r--django/dispatch/dispatcher.py42
1 files changed, 32 insertions, 10 deletions
diff --git a/django/dispatch/dispatcher.py b/django/dispatch/dispatcher.py
index 120f2ac6de..63fb75285e 100644
--- a/django/dispatch/dispatcher.py
+++ b/django/dispatch/dispatcher.py
@@ -22,6 +22,28 @@ NONE_ID = _make_id(None)
NO_RECEIVERS = object()
+async def _gather(*coros):
+ if len(coros) == 0:
+ return []
+
+ if len(coros) == 1:
+ return [await coros[0]]
+
+ async def run(i, coro):
+ results[i] = await coro
+
+ try:
+ async with asyncio.TaskGroup() as tg:
+ results = [None] * len(coros)
+ for i, coro in enumerate(coros):
+ tg.create_task(run(i, coro))
+ return results
+ except BaseExceptionGroup as exception_group:
+ if len(exception_group.exceptions) == 1:
+ raise exception_group.exceptions[0]
+ raise
+
+
class Signal:
"""
Base class for all signals
@@ -186,7 +208,7 @@ class Signal:
If any receivers are asynchronous, they are called after all the
synchronous receivers via a single call to async_to_sync(). They are
- also executed concurrently with asyncio.gather().
+ also executed concurrently with asyncio.TaskGroup().
Arguments:
@@ -211,7 +233,7 @@ class Signal:
if async_receivers:
async def asend():
- async_responses = await asyncio.gather(
+ async_responses = await _gather(
*(
receiver(signal=self, sender=sender, **named)
for receiver in async_receivers
@@ -235,7 +257,7 @@ class Signal:
sync_to_async() adaption before executing any asynchronous receivers.
If any receivers are asynchronous, they are grouped and executed
- concurrently with asyncio.gather().
+ concurrently with asyncio.TaskGroup().
Arguments:
@@ -268,9 +290,9 @@ class Signal:
async def sync_send():
return []
- responses, async_responses = await asyncio.gather(
+ responses, async_responses = await _gather(
sync_send(),
- asyncio.gather(
+ _gather(
*(
receiver(signal=self, sender=sender, **named)
for receiver in async_receivers
@@ -294,7 +316,7 @@ class Signal:
If any receivers are asynchronous, they are called after all the
synchronous receivers via a single call to async_to_sync(). They are
- also executed concurrently with asyncio.gather().
+ also executed concurrently with asyncio.TaskGroup().
Arguments:
@@ -340,7 +362,7 @@ class Signal:
return response
async def asend():
- async_responses = await asyncio.gather(
+ async_responses = await _gather(
*(
asend_and_wrap_exception(receiver)
for receiver in async_receivers
@@ -359,7 +381,7 @@ class Signal:
sync_to_async() adaption before executing any asynchronous receivers.
If any receivers are asynchronous, they are grouped and executed
- concurrently with asyncio.gather.
+ concurrently with asyncio.TaskGroup.
Arguments:
@@ -414,9 +436,9 @@ class Signal:
return err
return response
- responses, async_responses = await asyncio.gather(
+ responses, async_responses = await _gather(
sync_send(),
- asyncio.gather(
+ _gather(
*(asend_and_wrap_exception(receiver) for receiver in async_receivers),
),
)