Right, in Trio 0.1.0, exception propagation, auto-cancellation, etc. only worked when the parent task was blocked in the nursery’s
__aexit__. We called this the “parenting is a full time job” rule – you were supposed to make sure that after you opened a nursery, you proceeded quickly to the
__aexit__, without doing anything that would block for long.
It turns out this was confusing, and in particular caused problems for libraries that needed background tasks, like for AMQP or Websockets or whatever. So in 0.3.0 we got rid of this rule, and made it so that the body of the
async with open_nursery() block was treated like a child task, and the exception propagation/cancellation stuff happens magically in the background as part of the runtime, instead of just in
__aexit__. And the new way also turns out to be way simpler to reason about, explain, etc.
Change log: https://trio.readthedocs.io/en/latest/history.html#trio-0-3-0-2017-12-28
The issue where this was discussed: https://github.com/python-trio/trio/issues/136
So for @malinoff’s original question: trio does force you to use some kind of
async with block to create the background task – so the idiomatic way would be to tell your users to write:
async with amqproto.open_connection(HOST, PORT) as amqp_connection:
await amqp_connection.send(...) # or whatever
and you’d implement this as something like:
# This decorator available in python 3.7, or the async_generator library
async def open_connection(host, port):
async with trio.open_nursery() as nursery:
# You could also expose AMQPConnection directly as an "expert API"
# (e.g. for those who want to use their own nursery)
amqp_connection = AMQPConnection()
await amqp_connection.connect(host, port, nursery)
async with amqp_connection: # make sure to close the connection at the end
This feels a bit weird at first because now your users have to use
async with to create a connection. (Of course they probably should be doing this anyway, but usually in Python this is something we recommend as being good style, not something you get forced to do. So it’s a bit weird.)
But once you do that, Trio automatically takes care of the problem you raised at the beginning of the thread: if the background task you spawn into that nursery crashes, then trio will immediately notice and make sure that the exception doesn’t get lost.
(Of course if you want some other behavior you can do that too, e.g. you could catch exceptions in the background task and set a flag so that the next time the user calls a method on the AMQP connection it raises an error.)