python-3.xasync-awaitpython-trio

Trio: why are channels documented as using `async with`, rather than `with`?


Relevant documentation: https://trio.readthedocs.io/en/latest/reference-core.html#synchronizing-and-communicating-between-tasks

Relevant code: https://github.com/python-trio/trio/blob/master/trio/_channel.py

The documentation mentions, in close() docs, that "Using with receive_channel: will close the channel object on leaving the with block". On the other hand, the following section of the docs demonstrating the interface exclusively uses async with receive_channel: where the close() docs used regular with.

As per the code link, the relevant classes only have __enter__ and __exit__, there's no __aenter__ or __aexit__. So why does the demonstration in the docs use only async with? If I use regular with in my code, do I run the risk of future api changes breaking my code or some such?


Solution

  • It seems this is to aid refactoring, and generally to promote loose coupling via dependency inversion.
    The producer and consumer functions in the simple channel example from the docs do take an arbitrary SendChannel and ReceiveChannel respectively, not a memory channel specifically - admittedly it's hard to see given the lack of type annocations :-P

    SendChannel / ReceiveChannel objects also implement the AsyncResource interface, so they can be closed by calling aclose or using an async with block.

    The documentation of MemoryReceiveChannel.close that you already quoted states explicitly

    All channel objects have an asynchronous aclose method. Memory channels can also be closed synchronously.

    (emphasis mine)

    So you can use normal with blocks and the close() method just fine with memory channels, there is no risk of breaking API changes. But if you ever want to change or reuse your code to work with a different channel implementation, say, a DTLSChannel or a web socket, you will prefer to have used async with in the first place.