-
-
Notifications
You must be signed in to change notification settings - Fork 8.3k
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. |
Yes I know this is one of >300 open PRs … nevertheless, I'd appreciate feedback WRT this patch's mergeability. |
Added support for Columbia DSL Sensor board
Once again I try and use this, convinced that it was merged already, only to crash and burn 🙈 Having task groups in CPython may have ruined me… |
Yeah. Sigh. I'll do a re-merge shortly. |
Signed-off-by: Matthias Urlichs <matthias@urlichs.de>
Ugh. Don't ask me what this CI error means … |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Coming in late to this. I haven't used Taskgroups in CPython but I can see the utility - this looks really useful. Thanks for all the work and persistence.
Ugh. Don't ask me what this CI error means …
+Traceback (most recent call last):
+ File "<stdin>", line 13, in <module>
+AttributeError: 'module' object has no attribute 'ExceptionGroup'
+
FAILURE /home/runner/work/micropython/micropython/tests/results/extmod_asyncio_taskgroup.py
webassembly
port has its own version of the asyncio module under ports/webassembly/asyncio
. I think you've got two options here:
- Add
extmod/asyncio/taskgroup.py
toports/webassembly/variants/manifest.py
andports/webassembly/asyncio/__init__.py
- Or update the test to skip if the asyncio module doesn't contain this name
extmod/asyncio/stream.py
Outdated
@@ -170,6 +171,47 @@ async def start_server(cb, host, port, backlog=5): | |||
return srv | |||
|
|||
|
|||
# Helper task to run a TCP stream server. | |||
# Callbacks (i.e. connection handlers) may run in a different taskgroup. | |||
async def run_server(cb, host, port, backlog=5, taskgroup=None): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you end up bringing this up with CPython, @smurfix? It's probably the biggest remaining hurdle - to try and keep asyncio module contents close to CPython.
I guess the alternative is to hide this in a different module somewhere (maybe in micropython-lib). However if CPython were up for solving this as well, that would be ideal.
# Try not to allocate a SleepHandler on the heap if possible | ||
def sleep_ms(t, sgen=SleepHandler()): | ||
if sgen.state is not None: # the static one is busy | ||
sgen = SleepHandler() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This definitely needs to be understood better. It should be that the scheduler always iterates the singleton immediately. If that's no longer the case then we need to understand why and document it.
Signed-off-by: Matthias Urlichs <matthias@urlichs.de>
Signed-off-by: Matthias Urlichs <matthias@urlichs.de>
00d4d17
to
2be6e78
Compare
Signed-off-by: Matthias Urlichs <matthias@urlichs.de>
`run_server` is not in CPython. We'd like to avoid that. Signed-off-by: Matthias Urlichs <matthias@urlichs.de>
Signed-off-by: Matthias Urlichs <matthias@urlichs.de>
Signed-off-by: Matthias Urlichs <matthias@urlichs.de>
Signed-off-by: Matthias Urlichs <matthias@urlichs.de>
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 res In MicroPython this is a nicely concise way of saying "give me the result of |
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.