Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/benchmark.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
timeout-minutes: 60
steps:
- name: Checkout Benchmark Repo
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
repository: django/django-asv
path: "."
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/check_commit_messages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
persist-credentials: false

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/coverage_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
timeout-minutes: 60
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
fetch-depth: 0
persist-credentials: false
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
timeout-minutes: 60
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
persist-credentials: false
- name: Set up Python
Expand All @@ -47,7 +47,7 @@ jobs:
timeout-minutes: 60
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
persist-credentials: false
- name: Set up Python
Expand All @@ -71,7 +71,7 @@ jobs:
timeout-minutes: 60
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
persist-credentials: false
- name: Set up Python
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/labels.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
persist-credentials: false

Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/linters.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
timeout-minutes: 60
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
persist-credentials: false
- name: Set up Python
Expand All @@ -44,7 +44,7 @@ jobs:
timeout-minutes: 60
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
persist-credentials: false
- name: Set up Python
Expand All @@ -64,7 +64,7 @@ jobs:
timeout-minutes: 60
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
persist-credentials: false
- name: black
Expand All @@ -75,7 +75,7 @@ jobs:
timeout-minutes: 60
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
persist-credentials: false
- name: Run zizmor
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/postgis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ jobs:
timeout-minutes: 60
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
persist-credentials: false
- name: Set up Python
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/python_matrix.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
timeout-minutes: 60
steps:
- name: Checkout code
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
persist-credentials: false
- id: set-matrix
Expand All @@ -40,7 +40,7 @@ jobs:
timeout-minutes: 60
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
persist-credentials: false
- name: Set up Python ${{ matrix.python-version }}
Expand Down
12 changes: 6 additions & 6 deletions .github/workflows/schedule_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ jobs:
continue-on-error: true
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
persist-credentials: false
- name: Set up Python
Expand All @@ -46,7 +46,7 @@ jobs:
timeout-minutes: 60
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
persist-credentials: false
- name: Set up Python
Expand Down Expand Up @@ -75,7 +75,7 @@ jobs:
timeout-minutes: 60
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
persist-credentials: false
- name: Set up Node.js
Expand All @@ -93,7 +93,7 @@ jobs:
timeout-minutes: 60
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
persist-credentials: false
- name: Set up Python
Expand Down Expand Up @@ -132,7 +132,7 @@ jobs:
--health-retries 5
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
persist-credentials: false
- name: Set up Python
Expand Down Expand Up @@ -180,7 +180,7 @@ jobs:
--health-retries 5
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
persist-credentials: false
- name: Set up Python
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/screenshots.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
timeout-minutes: 60
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
persist-credentials: false
- name: Set up Python
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/selenium.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
timeout-minutes: 60
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
persist-credentials: false
- name: Set up Python
Expand Down Expand Up @@ -61,7 +61,7 @@ jobs:
--health-retries 5
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
persist-credentials: false
- name: Set up Python
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:
timeout-minutes: 60
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
persist-credentials: false
- name: Set up Python
Expand All @@ -50,7 +50,7 @@ jobs:
timeout-minutes: 60
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
persist-credentials: false
- name: Set up Node.js
Expand Down
57 changes: 37 additions & 20 deletions django/dispatch/dispatcher.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import asyncio
import contextvars
import logging
import threading
import weakref
Expand All @@ -22,26 +23,45 @@ def _make_id(target):
NO_RECEIVERS = object()


async def _gather(*coros):
def _restore_context(context):
"""
Check for changes in contextvars, and set them to the current
context for downstream consumers.
"""
for cvar in context:
cvalue = context.get(cvar)
try:
if cvar.get() != cvalue:
cvar.set(cvalue)
except LookupError:
cvar.set(cvalue)


async def _run_parallel(*coros):
"""
Execute multiple asynchronous coroutines in parallel,
sharing the current context between them.
"""
context = contextvars.copy_context()

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))
tg.create_task(run(i, coro), context=context)
return results
except BaseExceptionGroup as exception_group:
if len(exception_group.exceptions) == 1:
raise exception_group.exceptions[0]
raise
finally:
_restore_context(context=context)


class Signal:
Expand Down Expand Up @@ -233,7 +253,7 @@ def send(self, sender, **named):
if async_receivers:

async def asend():
async_responses = await _gather(
async_responses = await _run_parallel(
*(
receiver(signal=self, sender=sender, **named)
for receiver in async_receivers
Expand Down Expand Up @@ -275,6 +295,7 @@ async def asend(self, sender, **named):
):
return []
sync_receivers, async_receivers = self._live_receivers(sender)

if sync_receivers:

@sync_to_async
Expand All @@ -290,14 +311,12 @@ def sync_send():
async def sync_send():
return []

responses, async_responses = await _gather(
sync_send(),
_gather(
*(
receiver(signal=self, sender=sender, **named)
for receiver in async_receivers
)
),
responses = await sync_send()
async_responses = await _run_parallel(
*(
receiver(signal=self, sender=sender, **named)
for receiver in async_receivers
)
)
responses.extend(zip(async_receivers, async_responses))
return responses
Expand Down Expand Up @@ -362,7 +381,7 @@ async def asend_and_wrap_exception(receiver):
return response

async def asend():
async_responses = await _gather(
async_responses = await _run_parallel(
*(
asend_and_wrap_exception(receiver)
for receiver in async_receivers
Expand Down Expand Up @@ -436,11 +455,9 @@ async def asend_and_wrap_exception(receiver):
return err
return response

responses, async_responses = await _gather(
sync_send(),
_gather(
*(asend_and_wrap_exception(receiver) for receiver in async_receivers),
),
responses = await sync_send()
async_responses = await _run_parallel(
*(asend_and_wrap_exception(receiver) for receiver in async_receivers),
)
responses.extend(zip(async_receivers, async_responses))
return responses
Expand Down
Loading