How to block a python-socketio client until a server event response is received?

Question:

I’m trying to write a python script that connects to a nodejs server using socket.io package. The server receives the events from the client and responds with other events. As an example, let’s say that the client sends an "getHome" events and the server responds with a "homePage" event with some data. What I want is so be able to send an event with the client and block the execution of the script until the response is received, process the response and then do something else based on the server response. The code I wrote is:

#!/usr/bin/python3
import socketio

sio = socketio.Client()

@sio.event
def message(data):
    print(data)

@sio.event
def homePage(data):
    print(data)

sio.connect('http://docedit/socket.io/')

print("First call")
sio.emit("getHome")
print("Second call")
sio.emit("getHome")

The problem is that the second call to "emit" is done before receiving the response for the first one. The output of the script is something like:

First call
Second call
Welcome to Home  <- response from the server
Welcome to Home  <- response from the server

Reading the documentation, I tried to use "call" instead of "emit" but then the execution blocks forever, even if the homePage function executes normally:

#!/usr/bin/python3
import socketio

sio = socketio.Client()

@sio.event
def message(data):
    print(data)

@sio.event
def homePage(data):
    print(data)

sio.connect('http://docedit/socket.io/')

print("First call")
sio.call("getHome")
print("Second call")
sio.call("getHome")

Output:

First call
Welcome to Home <- response from the server

I didn’t find an example with call so maybe I’m using it wrong…any help?

Asked By: Nooneye

||

Answers:

Best way is to use some kind of lock

from threading import Lock
lock_me = Lock()

lock_me.acquire() #ensure lock is acquire beforehand

import socketio

sio = socketio.Client()

@sio.event
def message(data):
    print(data)

@sio.event
def homePage(data):
    print(data)
    lock_me.release()

sio.connect('http://docedit/socket.io/')

print("First call")
sio.emit("getHome")
lock_me.acquire()
print("Second call")
sio.emit("getHome")

Another way is with conditions or notify 🙂

Answered By: Indesejavel Coisa

You should use asyncio:

import asyncio
import socketio

sio = socketio.AsyncClient()

async def get_home():
    await sio.emit("getHome")
    await sio.wait_for("homePage")

@sio.event
async def homePage(data):
    print(data)

await sio.connect('http://docedit/socket.io/')

print("First call")
await get_home()
print("Second call")
await get_home()

You can also automatically handle connections and disconnections using async with:

import asyncio
import socketio

async def get_home():
    await sio.emit("getHome")
    await sio.wait_for("homePage")

@sio.event
async def homePage(data):
    print(data)

async with socketio.AsyncClient() as sio:
    await sio.connect('http://docedit/socket.io/')
    print("First call")
    await get_home()
    print("Second call")
    await get_home()
Answered By: Hassan
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.