lilydjwg
lilydjwg

Reputation: 1713

Future/Promise like stuff for Trio in Python?

Say I have a class Messenger which is responsible for sending and receiving messages. Now I have a service that sends out requests and waits for responses via it, matching each pair with an id field in the message. In asyncio I would do:

class Service:
  ...

  async def request(self, req):
    new_id = self._gen_id()
    req.id = new_id
    fu = asyncio.Future()
    self._requests[new_id] = fu
    await self._messenger.send(req)
    return await fu

  def handle_response(self, res):
    try:
      fu = self._requests.pop(res.req_id)
    except KeyError:
      return

    fu.set_result(res)

So I could send out multiple requests from different tasks, and in each task wait for the corresponding response. (And some messages may not have a corresponding response that are handled in another way.)

But how do I do this in Trio? Should I create an Event / Condition / Queue for each request and put the response in a predefined place? If yes, which is the best for this scenario? Or there is another way to do this?

Upvotes: 3

Views: 822

Answers (2)

Arthur Tacca
Arthur Tacca

Reputation: 10018

There is a simple Future class in aioresult. (Disclaimer: I wrote aioresult.) It is a tiny class with a trio.Event and the result or exception, but it saves a few lines of code compared to implementing it yourself.

from aioresult import Future

class Service:
  ...

  async def request(self, req):
    new_id = self._gen_id()
    req.id = new_id
    fu = Future()
    self._requests[new_id] = fu
    await self._messenger.send(req)
    return fu

  def handle_response(self, res):
    try:
      fu = self._requests.pop(res.req_id)
    except KeyError:
      return

    fu.set_result(res)

async def use_service(service, req):
    response_future = service.request(req)
    await response_future.wait_done()
    print(f"for {req} got {response_future.result()}")

Upvotes: 0

Matthias Urlichs
Matthias Urlichs

Reputation: 2544

You could create a simple class that contains an Event and your result.

However, strictly speaking events are overkill because multiple tasks can wait on an event, which you don't need, so you should use trio.hazmat.wait_task_rescheduled. That also gives you a hook you can use to do something when the requesting task gets cancelled before receiving its reply.

http://trio.readthedocs.io/en/latest/reference-hazmat.html#low-level-blocking

Upvotes: 1

Related Questions