python-trio / trio-asyncio

a re-implementation of the asyncio mainloop on top of Trio
Other
188 stars 37 forks source link

Proper cancellation support for async generators. #19

Closed miracle2k closed 6 years ago

miracle2k commented 6 years ago

Until now, async generators running in asyncio could not be cancelled. This means that they would run until exhausted (or otherwise failing), potentially hanging the trio program (which wants them to be cancelled).

This implements proper cancellation support.

codecov[bot] commented 6 years ago

Codecov Report

Merging #19 into master will decrease coverage by <.01%. The diff coverage is 60%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master      #19      +/-   ##
==========================================
- Coverage   75.26%   75.26%   -0.01%     
==========================================
  Files          11       11              
  Lines        1035     1043       +8     
  Branches      132      135       +3     
==========================================
+ Hits          779      785       +6     
  Misses        213      213              
- Partials       43       45       +2
Impacted Files Coverage Δ
trio_asyncio/util.py 78.68% <60%> (-0.56%) :arrow_down:

Continue to review full report at Codecov.

Legend - Click here to learn more Δ = absolute <relative> (impact), ø = not affected, ? = missing data Powered by Codecov. Last update 97dd2dd...0a970cc. Read the comment docs.

njsmith commented 6 years ago

Huh.

Am I right that run_generator is a convenience function to let you iterate over an asyncio-mode async iterator, from trio mode? If so, wouldn't it be simpler to implement it like:

@async_generator
async def asyncio_iterator(ait):
    ait = ait.__aiter__()
    try:
        while True:
            await yield_(await run_asyncio(ait.__anext__))
    except StopAsyncIteration:
        return

?

miracle2k commented 6 years ago

Maybe? :) I am a bit in over my head here, I was just glad to get this working.

Yes, the goal is to use an asyncio iterator from trio. Basically, my code might be this:

@trio2aio
async def stream(asyncio_api):
    async for reply in asyncio_api.stream_messages():
        yield reply

# running in trio
api = create_asyncio_api()
async for reply in stream(api):
     print(reply)

The idea was to make the trio2aio decorator work when applied on top of async generator functions.