Repository: django/django
File: django/dispatch/dispatcher.py (Python)

Code before:
import asyncio
import contextvars
import logging
import threading
import weakref

from asgiref.sync import async_to_sync, iscoroutinefunction, sync_to_async

from django.utils.inspect import func_accepts_kwargs

logger = logging.getLogger("django.dispatch")


def _make_id(target):
    if hasattr(target, "__func__"):
        return (id(target.__self__), id(target.__func__))
    return id(target)


NONE_ID = _make_id(None)

# A marker for caching
NO_RECEIVERS = object()


async def _run_parallel(*coroutines):
    """
    Executes multiple asynchronous entities in parallel.
    """
    context = contextvars.copy_context()

    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)

    if len(coroutines) == 0:
        return []

    async def run(i, cor):
        results[i] = await cor

    try:
        async with asyncio.TaskGroup() as tg:
            results = [None] * len(coroutines)

Review comment (suggestion):
Summary:

- Removed the `len(coros) == 1` optimization. It doesn’t appear to provide any real benefit.

```
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┓
┃                        Benchmark ┃ Min     ┃ Max     ┃ Mean    ┃ Min (+)         ┃ Max (+)         ┃ Mean (+)        ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━┩
│ TaskGroup instead of create_task │ 0.001   │ 0.001   │ 0.001   │ 0.001 (1.1x)    │ 0.002 (-1.1x)   │ 0.001 (1.1x)    │
└──────────────────────────────────┴─────────┴─────────┴─────────┴─────────────────┴─────────────────┴─────────────────┘
```

- All coroutines now share the same context, which is restored to the current context after completion.


btw:

- `_restore_context` is just a copy-paste from the `asgiref` library

Code after:
import asyncio
import contextvars
import logging
import threading
import weakref

from asgiref.sync import async_to_sync, iscoroutinefunction, sync_to_async

from django.utils.inspect import func_accepts_kwargs

logger = logging.getLogger("django.dispatch")


def _make_id(target):
    if hasattr(target, "__func__"):
        return (id(target.__self__), id(target.__func__))
    return id(target)


NONE_ID = _make_id(None)

# A marker for caching
NO_RECEIVERS = object()


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 []

    async def run(i, coro):
        results[i] = await coro