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

gh-102780: Fix uncancel() call in asyncio timeouts #102815

Merged
merged 13 commits into from
Mar 22, 2023
Merged

gh-102780: Fix uncancel() call in asyncio timeouts #102815

merged 13 commits into from
Mar 22, 2023

Conversation

kristjanvalur
Copy link
Contributor

@kristjanvalur kristjanvalur commented Mar 18, 2023

Record the previous cancellation state when entering asyncio.timeout.Timeout
This allows us to use asyncio.timeout even when handling a CancelledError
exception. The Timeout context manager will still not handle a CancelledError
if a new cancellation request is delivered in its scope.

  • Fix Timeout context manager
  • Add regression tests
  • Add documentiation, clarifying when it is appropriate to call Task.uncancel()

This needs to be backported to 3.11

@gvanrossum
Copy link
Member

Thanks! This looks like the crux of the matter, and beautifully executed. I'll properly review it later this weekend.

Copy link
Member

@gvanrossum gvanrossum left a comment

Choose a reason for hiding this comment

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

I only really have doc nits, plus a feeling that maybe possibly TaskGroup could have the same problem?

I'm asking @kumaraditya303 to also review this, to make sure I didn't miss anything.

Doc/library/asyncio-task.rst Show resolved Hide resolved
Doc/library/asyncio-task.rst Outdated Show resolved Hide resolved
# handling this.
raise TimeoutError
raise TimeoutError from exc_val
Copy link
Member

Choose a reason for hiding this comment

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

This is a nice improvement that maybe ought to be called out in the commit message? (If you agree I can handle that when I merge it.)

Copy link
Contributor

@kumaraditya303 kumaraditya303 Mar 19, 2023

Choose a reason for hiding this comment

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

Isn't this done by default? I see the same traceback with and without this on this example:

import asyncio

async def func():
    await asyncio.sleep(1)

async def main():
    async with asyncio.timeout(0.1):
        await func()

asyncio.run(main())

Copy link
Member

Choose a reason for hiding this comment

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

Not quite. When using raise exc from ... the CancelledError instance gets stored in exc.__cause__, while without it, it gets stored in exc.__context__, and the connecting phrase in the output is different:

__cause__:

The above exception was the direct cause of the following exception:

__context__:

During handling of the above exception, another exception occurred:

(If both are set it follows __cause__.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Precicely. I has bothered me for a while that timeouts happen with the latter message, as if somehow, there was an error during exception handling. I wanted to use the opportunity to roll that fix in :)

Copy link
Contributor Author

@kristjanvalur kristjanvalur Mar 20, 2023

Choose a reason for hiding this comment

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

This is a nice improvement that maybe ought to be called out in the commit message? (If you agree I can handle that when I merge it.)

Thank you, agreed.

Copy link
Contributor

Choose a reason for hiding this comment

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

Not quite. When using raise exc from ... the CancelledError instance gets stored in exc.cause, while without it, it gets stored in exc.context, and the connecting phrase in the output is different:

Ah, right! I missed that. Adding a test would be nice.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll add that in a few hours.

kristjanvalur and others added 2 commits March 20, 2023 11:10
Copy link
Contributor

@kumaraditya303 kumaraditya303 left a comment

Choose a reason for hiding this comment

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

I would like to have a test for the exception chaining else LGTM. I leave the docs for Guido to review.

@gvanrossum Feel free to merge as you like.

Copy link
Member

@AlexWaygood AlexWaygood left a comment

Choose a reason for hiding this comment

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

The docs read okay to me now, but I'll let Guido or Kumar merge since I'm not an asyncio expert!

Copy link
Member

@gvanrossum gvanrossum left a comment

Choose a reason for hiding this comment

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

Needs a news entry, otherwise LGTM.

@kristjanvalur
Copy link
Contributor Author

Needs a news entry, otherwise LGTM.

I'll have a go at it.

@gvanrossum gvanrossum changed the title gh-102780 gh-102780: Fix uncancel() call in asyncio timeouts Mar 22, 2023
@gvanrossum gvanrossum merged commit 04adf2d into python:main Mar 22, 2023
@gvanrossum gvanrossum added the needs backport to 3.11 only security fixes label Mar 22, 2023
@miss-islington
Copy link
Contributor

Thanks @kristjanvalur for the PR, and @gvanrossum for merging it 🌮🎉.. I'm working now to backport this PR to: 3.11.
🐍🍒⛏🤖

miss-islington pushed a commit to miss-islington/cpython that referenced this pull request Mar 22, 2023
…2815)

Also use `raise TimeOut from <CancelledError instance>` so that the CancelledError is set
in the `__cause__` field rather than in the `__context__` field.

(cherry picked from commit 04adf2d)

Co-authored-by: Kristján Valur Jónsson <sweskman@gmail.com>
Co-authored-by: Guido van Rossum <gvanrossum@gmail.com>
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
@bedevere-bot
Copy link

GH-102923 is a backport of this pull request to the 3.11 branch.

@bedevere-bot bedevere-bot removed the needs backport to 3.11 only security fixes label Mar 22, 2023
miss-islington added a commit that referenced this pull request Mar 22, 2023
Also use `raise TimeOut from <CancelledError instance>` so that the CancelledError is set
in the `__cause__` field rather than in the `__context__` field.

(cherry picked from commit 04adf2d)

Co-authored-by: Kristján Valur Jónsson <sweskman@gmail.com>
Co-authored-by: Guido van Rossum <gvanrossum@gmail.com>
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
@kristjanvalur kristjanvalur deleted the kristjan/uncancel branch March 23, 2023 08:48
Fidget-Spinner pushed a commit to Fidget-Spinner/cpython that referenced this pull request Mar 27, 2023
Also use `raise TimeOut from <CancelledError instance>` so that the CancelledError is set
in the `__cause__` field rather than in the `__context__` field.

Co-authored-by: Guido van Rossum <gvanrossum@gmail.com>
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
warsaw pushed a commit to warsaw/cpython that referenced this pull request Apr 11, 2023
Also use `raise TimeOut from <CancelledError instance>` so that the CancelledError is set
in the `__cause__` field rather than in the `__context__` field.

Co-authored-by: Guido van Rossum <gvanrossum@gmail.com>
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
@graingert
Copy link
Contributor

does this need a corresponding change in asyncio.TaskGroup? eg here

if self._parent_task.uncancel() == 0:

@kristjanvalur
Copy link
Contributor Author

Interesting. It would be an issue if we entered a TaskGroup while cancellation state was not 0, e.g. during a finally of an outer cancel. I'm not qualified, though, to judge if that would ever be a real issue.

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.

8 participants