Skip to content

Conversation

graingert
Copy link
Contributor

@graingert graingert commented Apr 10, 2025

Trac ticket number

ticket-36315

Branch description

Provide a concise overview of the issue or rationale behind the proposed changes.

Checklist

  • This PR targets the main branch.
  • The commit message is written in past tense, mentions the ticket number, and ends with a period.
  • I have checked the "Has patch" ticket flag in the Trac system.
  • I have added or updated relevant tests.
  • I have added or updated relevant docs, including release notes if applicable.
  • I have attached screenshots in both light and dark modes for any UI changes.

@github-actions github-actions bot added the no ticket Based on PR title, no linked Trac ticket label Apr 10, 2025
@graingert graingert force-pushed the use-tg-instead-of-gather branch from 8e9df78 to 014268a Compare April 10, 2025 08:47
@graingert graingert changed the title use TaskGroup in asgi handler use taskgroup instead of gather in dispatcher Apr 10, 2025
@graingert graingert changed the title use taskgroup instead of gather in dispatcher use asyncio.TaskGroup instead of gather in dispatcher Apr 10, 2025
@graingert graingert marked this pull request as ready for review April 10, 2025 08:50
@graingert graingert force-pushed the use-tg-instead-of-gather branch from 014268a to ca58d03 Compare April 10, 2025 08:50
@graingert graingert marked this pull request as draft April 10, 2025 08:50
@graingert graingert marked this pull request as ready for review April 10, 2025 12:14
@graingert graingert force-pushed the use-tg-instead-of-gather branch from ca58d03 to 682c753 Compare April 10, 2025 12:18
@carltongibson carltongibson self-assigned this Apr 10, 2025
@graingert graingert force-pushed the use-tg-instead-of-gather branch from 682c753 to c0f12c8 Compare April 10, 2025 12:24
@graingert graingert changed the title use asyncio.TaskGroup instead of gather in dispatcher Refs ticket-36315 - Replaced calls to asyncio.gather with TaskGroup. Apr 10, 2025
@nessita nessita changed the title Refs ticket-36315 - Replaced calls to asyncio.gather with TaskGroup. Refs #36315 -- Replaced calls to asyncio.gather with TaskGroup. Apr 10, 2025
@github-actions github-actions bot removed the no ticket Based on PR title, no linked Trac ticket label Apr 10, 2025
Copy link
Contributor

@g-nie g-nie left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @graingert, I left one suggestion.

I noticed that the asyncio.create_task() calls in handles/asgi.py can benefit from this change as well (there are probably more places too).

Comment on lines +25 to +44
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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be simplified to:

Suggested change
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
async def gather_tasks(*coros):
async with asyncio.TaskGroup() as tg:
tasks = [tg.create_task(coro) for coro in coros]
return [task.result() for task in tasks]

unless there's reason for the error handling? (BaseExceptionGroup).

Also, it might be worth moving the function to a utils.py file, so that it can easily be reused by other places.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes the error handling is important to maintain backwards compatibility with the old use of gather

Copy link
Contributor Author

@graingert graingert Apr 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keeping an extra reference to tasks here will result in reference cycles in the traceback of any errors that will delay garbage collection if there's any errors in signals

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To maintain backward compatibility, shouldn't the first exception be raised even when the exception group contains multiple exceptions?

@graingert
Copy link
Contributor Author

Hi @graingert, I left one suggestion.

I noticed that the asyncio.create_task() calls in handles/asgi.py can benefit from this change as well (there are probably more places too).

I have a PR for that here #19366

@hartungstenio
Copy link

asyncio.TaskGroup cancels every task in the group on the first exception it encounters. asyncio.gather raises this first exception, but it keeps the other tasks running.

Don't know if this may raise some compatibility issues.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants