pythonpython-3.xpython-asynciopython-contextvars

Sharing state between two async programs in python using asyncio and contextvars


I currently have two infinite asynchronous tasks running and want to share state between them. One task is a websocket connection that reads in messages then sends messages and the other reads in incoming light data. I want to send a boolean between the two tasks that says whether or not the websocket connection is successful.

Here is how I'm initializing the context.

client_connect_var = contextvars.ContextVar('client_connect',default = False)
client_connect_var.set(False)
ctx = contextvars.copy_context()
async def main():
  message = json.dumps({'payload': {
                        'payload'})
  loop = asyncio.get_event_loop()
  start_light = asyncio.create_task(calculate_idle(3))
  await asyncio.gather(init_connection(message), start_light)

ctx.run(asyncio.run(main()))

Here is my code in my init_connection:

async def init_connection(message):
  async with websockets.connect(uri) as websocket:
      #This should set the global context variable to true
      client_connect_var.set(True)
      CLIENT_WS = websocket
      client_connect = client_connect_var.get()
    # send init message
    await websocket.send(message)
    print("Connection is open")
    while client_connect:
        await handleMessages(websocket, message)
    await websocket.close()

Here is where it's trying to get the current state in the light code

async def calculate_idle(t):
    orig_time = t
    while True:
        await asyncio.sleep(5)
        #This should be true, but it's false
        client_connect = client_connect_var.get()
        await asyncio.sleep(5)

In calculate_idle, the variable in the context is still set to false. I'm not sure how to get it to be set to true from inside init_connection. I'd like to expand this to updating other values in state that are objects. Any help would be appreciated!


Solution

  • ContextVar serves the purpose opposite of what you're trying to use it for: it's for data that is local to a task, something like a "task-local" global variable.

    To share data between two tasks you can use normal Python sharing mechanisms, e.g. a global variable or an ordinary mutable value (e.g. a dict or an instance of a class of your choosing). You can also use synchronization like asyncio.Event (https://docs.python.org/3/library/asyncio-sync.html#asyncio.Event) to notify the other task when the value changes.