Transition request API
I think the current request API is bad -- returning from context.request(x) a Response that has an awaitable .response and possibly an aiter-able .observation is inherently racy (or needs additional queuing), and the tricks around making sure that even if the user forgets about .observation it is destroyed and the observation is dropped network-side are ugly.
The refactoring currently in process to accommodate allow CoAP over TCP and proper OSCORE still implements that version for compatibility, but a process towards a new request API should be started.
I'm thinking along the lines of
ctx = await NewAPIContext.create_client_endpoint()
# A unicast non-observe request gets awaited directly and returns
# a message again
direct_response = await ctx.request(Message(...))
print(direct_response.payload)
# A unicast observe request is an iterable on its own; no distinction
# between first and future responses (because why should there)
async for latest in ctx.observe(Message(...)):
print(latest.payload)
Multicast would have dedicated methods as before:
# Multicast behaves as an iterator as well:
async for response in ctx.multicast_request(Message(...)):
print(f"Response from {response.get_request_uri()}: {response.payload}")
(Multicast observation would give an iterator of iterators -- but is that even a thing?)
Current users, would that change work well for you? How long would you need both APIs around to migrate?
For the record: Another issue with the current request/observation API is that it only produces observations after the result has been awaited. That used to be a side-effect of what was a future and what a task, and is kept as a requirement because that gives the API user time to register callbacks with the observation without introducing a race condition.
I was mistaken (fixed in https://github.com/chrysn/aiocoap/commit/bc994364b9579daf14b8fa80ff4cbbef8705e115) about the current API requiring awaiting .response -- that hasn't been a requirement for a long time, and the current API is therefore racy. (Not a particularly bad race condition as ctx.request(...) returns right away and adding a callback to the observation will be processed even before control is given off to anything that could've processed incoming data, but still it's not clean.)