Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RuntimeError when using Ctrl+C while awaiting a task using await TaskGrup.start() #693

Closed
2 tasks done
adam-pawluczuk opened this issue Feb 26, 2024 · 5 comments
Closed
2 tasks done
Labels
bug Something isn't working

Comments

@adam-pawluczuk
Copy link

Things to check first

  • I have searched the existing issues and didn't find my bug already reported there

  • I have checked that my bug is still present in the latest release

AnyIO version

4.3.0

Python version

3.12.0

What happened?

Hello.

Consider this snippet:

from asyncio import CancelledError
import anyio


async def run():
    async def task(task_status = anyio.TASK_STATUS_IGNORED):
        try:
            await anyio.sleep_forever()
        except CancelledError as err:
            task_status.started(err)

    async with anyio.create_task_group() as tg:
        await tg.start(task)

anyio.run(run)

When running this script and using Ctrl+C, this is what I'm seeing:

^CTraceback (most recent call last):
  File "/Users/apawlucz/src/scripts/anyio/test_task_started.py", line 13, in run
    await tg.start(task)
  File "/Users/apawlucz/opt/anaconda3/envs/rd_312/lib/python3.12/site-packages/anyio/_backends/_asyncio.py", line 787, in start
    await task
  File "/Users/apawlucz/src/scripts/anyio/test_task_started.py", line 10, in task
    task_status.started(err)
  File "/Users/apawlucz/opt/anaconda3/envs/rd_312/lib/python3.12/site-packages/anyio/_backends/_asyncio.py", line 623, in started
    raise RuntimeError(
RuntimeError: called 'started' twice on the same task status

During handling of the above exception, another exception occurred:

  + Exception Group Traceback (most recent call last):
  |   File "/Users/apawlucz/src/scripts/anyio/test_task_started.py", line 15, in <module>
  |     anyio.run(run)
  |   File "/Users/apawlucz/opt/anaconda3/envs/rd_312/lib/python3.12/site-packages/anyio/_core/_eventloop.py", line 73, in run
  |     return async_backend.run(func, args, {}, backend_options)
  |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |   File "/Users/apawlucz/opt/anaconda3/envs/rd_312/lib/python3.12/site-packages/anyio/_backends/_asyncio.py", line 2001, in run
  |     return runner.run(wrapper())
  |            ^^^^^^^^^^^^^^^^^^^^^
  |   File "/Users/apawlucz/opt/anaconda3/envs/rd_312/lib/python3.12/asyncio/runners.py", line 118, in run
  |     return self._loop.run_until_complete(task)
  |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |   File "/Users/apawlucz/opt/anaconda3/envs/rd_312/lib/python3.12/asyncio/base_events.py", line 664, in run_until_complete
  |     return future.result()
  |            ^^^^^^^^^^^^^^^
  |   File "/Users/apawlucz/opt/anaconda3/envs/rd_312/lib/python3.12/site-packages/anyio/_backends/_asyncio.py", line 1989, in wrapper
  |     return await func(*args)
  |            ^^^^^^^^^^^^^^^^^
  |   File "/Users/apawlucz/src/scripts/anyio/test_task_started.py", line 12, in run
  |     async with anyio.create_task_group() as tg:
  |   File "/Users/apawlucz/opt/anaconda3/envs/rd_312/lib/python3.12/site-packages/anyio/_backends/_asyncio.py", line 678, in __aexit__
  |     raise BaseExceptionGroup(
  | ExceptionGroup: unhandled errors in a TaskGroup (2 sub-exceptions)
  +-+---------------- 1 ----------------
    | Traceback (most recent call last):
    |   File "/Users/apawlucz/src/scripts/anyio/test_task_started.py", line 13, in run
    |     await tg.start(task)
    |   File "/Users/apawlucz/opt/anaconda3/envs/rd_312/lib/python3.12/site-packages/anyio/_backends/_asyncio.py", line 787, in start
    |     await task
    |   File "/Users/apawlucz/src/scripts/anyio/test_task_started.py", line 10, in task
    |     task_status.started(err)
    |   File "/Users/apawlucz/opt/anaconda3/envs/rd_312/lib/python3.12/site-packages/anyio/_backends/_asyncio.py", line 623, in started
    |     raise RuntimeError(
    | RuntimeError: called 'started' twice on the same task status
    +---------------- 2 ----------------
    | Traceback (most recent call last):
    |   File "/Users/apawlucz/src/scripts/anyio/test_task_started.py", line 13, in run
    |     await tg.start(task)
    |   File "/Users/apawlucz/opt/anaconda3/envs/rd_312/lib/python3.12/site-packages/anyio/_backends/_asyncio.py", line 787, in start
    |     await task
    |   File "/Users/apawlucz/src/scripts/anyio/test_task_started.py", line 10, in task
    |     task_status.started(err)
    |   File "/Users/apawlucz/opt/anaconda3/envs/rd_312/lib/python3.12/site-packages/anyio/_backends/_asyncio.py", line 623, in started
    |     raise RuntimeError(
    | RuntimeError: called 'started' twice on the same task status
    +------------------------------------

It is unexpected in this sense that actually task_status was called only once, not twice.

How can we reproduce the bug?

Code snippet as above.

@adam-pawluczuk adam-pawluczuk added the bug Something isn't working label Feb 26, 2024
@agronholm
Copy link
Owner

agronholm commented Feb 26, 2024

How did you find this problem? That example looks like an abuse of start().

@adam-pawluczuk
Copy link
Author

adam-pawluczuk commented Feb 27, 2024

That was our legitimate code. We basically were catching BaseException and then invoking task_status.started(exception).

Otherwise anyio raises RuntimError when the task status started is not called.

But when I read through documentation again it looks like we shouldn't be doing that, and just let the exception to propagate.

Reason behind doing that is to not let the whole task group to cancel when the exception will be propagated from the task.

@agronholm
Copy link
Owner

Why are you using start() in the first place if you don't intend to call task_status.started()?

@agronholm
Copy link
Owner

The task group is supposed to be cancelled if there's an unhandled exception. If you don't want unhandled exceptions to cancel the entire task group, then catch the exception in the task code.

@agronholm
Copy link
Owner

Fixed via #707.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants