-
-
Notifications
You must be signed in to change notification settings - Fork 8.6k
Backport py3.11 asyncio's taskgroups. #8791
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
base: master
Are you sure you want to change the base?
Conversation
5a46a80 to
b5723ea
Compare
|
Sigh. It's not my commit message that's failing the test! |
|
Wow, thanks for this! It's very nice to see that it can be implemented in pure Python. I don't know anything about TaskGroup so will need to learn how it works. |
You're right. That CI check will need fixing so it allows "Revert" commit messages. |
Actually the CI check is working OK, it's only supposed to check the commit messages that are unique to the PR. If you rebase (and force push) on latest master it will hopefully get the CI green. |
|
Pushed. Writing tests, found a significant bug (related to cancellation of course). Investigating. |
1a0dffd to
7d20532
Compare
|
Huh. I have no idea where those test failures are coming from. |
|
The CI logs say error is: It looks like the bytecode emitter allows 2 args but the native emitter only allows 1. |
Well, yeah, I saw that line, but what has that got to do with implementing |
|
Assuming you are using the unix port, to reproduce locally, try There are a number of tests that are disabled when emit=native because of I suspect you have written some Python code similar to one of these that causes the same error. |
|
I'm running this on a Pyboard 1.1 - TaskGroups are a very nice feature. Something Google seems loath to divulge is how to access return values from the members of a group (assuming they terminate normally). |
|
@peterhinch Glad that my code is of use. I'll try to fix up the CI errors shortly; battling with Covid aftereffects, so if somebody else wants to fix up this PR, feel free. You need to return any values explicitly, e.g. by setting an object attribute to it or queuing the value or whatever. |
|
Sorry you're unwell - I hope you feel better soon.
Is this still outstanding or is the code ready for review? I very much hope this is implemented: in many applications it's a big improvement over |
No, that's done. The workaround I implemented admittedly isn't particularly clean, but it gets the job done and doesn't alter non-taskgroup code (as that might introduce bugs or slowdowns). |
|
@dpgeorge @jimmo This is to advocate for Consider a communication link between two peers using an unreliable medium such as WiFi. This may be near the limit of range and the link can fail in a variety of ways. The simplest way to recover is the "belt and braces" approach: if a peer encounters an unrecoverable error, it takes the link down for a period long enough for the other peer also to suffer an unrecoverable error. That way both peers start from a "power up" state. A
If any of these experience an error that cannot be handled locally, the exception is passed up to be handled by the task that created the group. When this occurs, every task in the group terminates in an orderly way, running cleanup code in The |
|
@peterhinch Exactly. Unstructured tasks (like asyncio's baseline tasks) mean that you have to keep track of what's still running and what might have to be cancelled and/or restarted on your own. Code doing that tends to contain a heap of bugs you can't test for, and basically doesn't scale. In contrast, when a taskgroup's async context manager has ended you know that there's no dangling tasks, unprocessed The latter point is much more powerful than you'd assume at first glance. |
619a1bc to
32608f0
Compare
|
Updated to current master. The CPython discussion on what to do about the
Umm, that used not to happen AFAIR?
Well, it appears not to. In any case, by its nature async code is async: nothing prevents me from calling a bare async def not_too_fast(delay, proc):
dly = asyncio.sleep(delay)
res = await proc()
await dly
return resIn MicroPython this is a nicely concise way of saying "give me the result of |
That would be superb. Please shout if I can help, e.g. by rebasing things onto the main branch. |
This patch adds a mostly-compatible backport of CPython 3.11's `TaskGroup` class to uasyncio. Also, there is a new `run_server` method in uasyncio/stream.py which supports task groups and doesn't run in the background (no need). TODO: Write some more tests. Closes micropython#8508. Signed-off-by: Matthias Urlichs <[email protected]>
The C implementation (_uasyncio.Task) was not. Signed-off-by: Matthias Urlichs <[email protected]>
Tasks can be cancelled before they have an opportunity to run. Handling this situation is non-trivial in MicroPython's context. Signed-off-by: Matthias Urlichs <[email protected]>
If two tasks try to start sleeping at the same time, the SingletonGenerator will be in use, thus we need to allocate a new temporary handler on the heap. As the singleton isn't that single any more, it gets renamed. Found by taskgroup test micropython#12. Signed-off-by: Matthias Urlichs <[email protected]>
This fixes some edge cases with cancellation. Signed-off-by: Matthias Urlichs <[email protected]>
still failing, though Signed-off-by: Matthias Urlichs <[email protected]>
Rudimentary but works. Signed-off-by: Matthias Urlichs <[email protected]>
If there's an exception (or more) *and* a cancellation in a taskgroup, the cancellation must be ignored. Signed-off-by: Matthias Urlichs <[email protected]>
This mirrors Python change 594c369. Signed-off-by: Matthias Urlichs <[email protected]>
Cancelling a taskgroup that's no longer active should not do anything. Signed-off-by: Matthias Urlichs <[email protected]>
Signed-off-by: Matthias Urlichs <[email protected]>
Signed-off-by: Matthias Urlichs <[email protected]>
Signed-off-by: Matthias Urlichs <[email protected]>
Signed-off-by: Matthias Urlichs <[email protected]>
Signed-off-by: Matthias Urlichs <[email protected]>
`run_server` is not in CPython. We like to avoid that. Signed-off-by: Matthias Urlichs <[email protected]>
Signed-off-by: Matthias Urlichs <[email protected]>
Signed-off-by: Matthias Urlichs <[email protected]>
This backports 3.11's TaskGroup class to uasyncio.
I don't care much about the
except *stuff, not in µPy context anyway, but sane error recovery is crucial and taskgroups make this job a whole lot easier, not to mention much less bug prone. (Writing from a lot of Trio and anyio experience here.)Bottom line: I refuse to write async code without using taskgroups. So here you are.
TODO: Write a couple of tests.Closes #8508.