0

Quart is a Python web framework which re-implements the Flask API on top of the asyncio coroutine system of Python. In my particular case, I have a Quart websocket endpoint which is supposed to have not just one source of incoming events, but two possible sources of events which are supposed to continue the asynchronous loop.

An example with one event source:

from quart import Quart, websocket
app = Quart(__name__)

@app.websocket("/echo")
def echo():
    while True:
        incoming_message = await websocket.receive()
        await websocket.send(incoming_message)

Taken from https://pgjones.gitlab.io/quart/

This example has one source: the incoming message stream. But what is the correct pattern if I had two possible sources, one being await websocket.receive() and another one being something along the lines of await system.get_next_external_notification() .

If either of them arrives, I'd like to send a websocket message.
I think I'll have to use asyncio.wait(..., return_when=FIRST_COMPLETED), but how do I make sure that I miss no data (i.e. for the race condition that websocket.receive() and system.get_next_external_notification() both finish almost exactly at the same time) ? What's the correct pattern in this case?

fameman
  • 3,451
  • 1
  • 19
  • 31

1 Answers1

1

An idea you could use is a Queue to join the events together from different sources, then have an async function listening in the background to that queue for requests. Something like this might get you started:

import asyncio
from quart import Quart, websocket
app = Quart(__name__)

@app.before_serving
async def startup():
    print(f'starting')
    app.q = asyncio.Queue(1)
    asyncio.ensure_future(listener(app.q))

async def listener(q):
    while True:
        returnq, msg = await q.get()
        print(msg)
        await returnq.put(f'hi: {msg}')

@app.route("/echo/<message>")
async def echo(message):
    while True:
        returnq = asyncio.Queue(1)
        await app.q.put((returnq, message))
        response = await returnq.get()
        return response

@app.route("/echo2/<message>")
async def echo2(message):
    while True:
        returnq = asyncio.Queue(1)
        await app.q.put((returnq, message))
        response = await returnq.get()
        return response
Jay Tuckey
  • 91
  • 1
  • 7