Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

flask and flask-socketio flush all emit events

How do you flush a flask-socketio emit event so that multiple lines stream to the client, as opposed to multiple lines all coming through at one time?

I'm working with Flask and flask-socketIO to make a web app that let's users play chess against open source chess AI's, like gnuchess and crafty. The chess AI's print their "thinking" as stdout to the server terminal, but I am using flask-socketIO to emit that thinking to the user.

It's all setup and works, but the problem I have is that each line of the thinking all comes through in one big chunk, as opposed to outputting each individual line as the AI prints it.

For example, when playing against gnuchess, the server prints thinking like this:

AI thinking: Thinking...
AI thinking: 1 +4 0 2 a5
AI thinking: 1 +9 0 4 b5
AI thinking: 1 +51 0 8 d5
AI thinking: 1 +53 0 21 Nc6
AI thinking: 2 -1 0 47 Nc6 Nc3
AI thinking: 3 +53 0 155 Nc6 Nc3 Nf6
AI thinking: 4 -1 0 613 Nc6 Nf3 Nf6 Nc3

And these lines stream to my server. On the client-side, I emit the AI lines, which show up to the client (right now) as a ul, which looks like this:

4 -1 0 613 Nc6 Nf3 Nf6 Nc3
3 +53 0 155 Nc6 Nc3 Nf6
2 -1 0 47 Nc6 Nc3
1 +53 0 21 Nc6
1 +51 0 8 d5
1 +9 0 4 b5
1 +4 0 2 a5
Thinking...

Here's the relevant code:

@main.route('/getpythondata')
def get_python_data():

    # gets current proc by finding user's most recent game...
    current_game = get_current_game(current_user)
    current_proc = get_current_proc(current_game)
    line = current_proc.stdout.readline().rstrip()
    socketio.emit(
            'echo',
            {'echo': line})
    # for gnu chess
    if current_game.ai == "GNU chess":
        while ("My move is" not in line):
            socketio.emit(
                'echo',
                {'echo': line})
            print "AI thinking: " + line
            line = current_proc.stdout.readline().rstrip()

        colon_index = line.index(":")
        line_length = len(line)
        pythondata = line[(colon_index + 2):(line_length)]
        current_game.cpu_moves.append(pythondata)
        return json.dumps(pythondata)

The problem here is that all the socketio.emit events come through as one big chunk of text after the flask route returns. Maybe they're getting stuck in a buffer? Is there a way to flush them out?

The "print "AI thinking:" + line code streams to the server many lines of text, one line at a time, right as the AI outputs them. But, the socketio.emit code all comes through at once to the client, instead of streaming out line-by-line.

Any ideas?

like image 910
Jeff Maxim Avatar asked Sep 22 '15 15:09

Jeff Maxim


2 Answers

Use eventlet.sleep(0) after your socketio.emit() statement.

Of course you'll have to import eventlet.

Source: https://github.com/miguelgrinberg/Flask-SocketIO/issues/141

like image 128
JahMyst Avatar answered Nov 19 '22 03:11

JahMyst


First import the socketio object (make sure the path points to where you create it)

from .. import socketio

then use

socketio.sleep(0)

I created a function to do the sleeping for me :)

def send_status(data):
    emit('status', data, json=True)
    socketio.sleep(0)

calling it with the string I want to send as a JSON

elehelper.send_status({'code': 201, 'message': 'Jana\'s skull stripper started!'})
like image 23
florian Avatar answered Nov 19 '22 03:11

florian