Asynchronous updates to a webpage with Flask and Socket.io

communications with socket io and flask in python

Update: Updated May 2018 to work with Python 3 on GitHub.

This post is about creating Python Flask web pages that can be asynchronously updated by your Python Flask application at any point without any user interaction. We’ll be using Python Flask, and the Flask-SocketIO plug-in to achieve this. The working application is hosted on GitHub.

What I want to achieve here is a web page that is automatically updated for each user as a result of events that happened in the background on my server system. For example, allowing events like a continually updating message stream, a notification system, or a specific Twitter monitor / display. In this post, I show how to develop a bare-bones Python Flask application that updates connected clients with random numbers. Flask is an extremely lightweight and simple framework for building web applications using Python.

Flask logo

If you haven’t used Flask before, it’s amazingly simple, and to get started serving a very simple webpage only requires a few lines of Python:

# Basic Flask Python Web App
from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello World!"

if __name__ == "__main__":
    app.run()

Running this file with python application.py will start a server on your local machine with one page saying “Hello World!” A quick look through the documentation and the first few sections of the brilliant mega-tutorial by Miguel Grinberg will have you creating multi-page python-based web applications in no time. However, most of the tutorials out there focus on the production of non-dynamic pages that load on first accessed and don’t describe further updates.

For the purpose of updating the page once our user has first visited, we will be using Socket.io and the accomanying Flask addon built by the same Miguel Grinberg, Flask-Socketio (Miguel appears to be some sort of Python Flask God). Socket IO is a genius engine that allows real-time bidirectional event-based communication. Gone are the days of static HTML pages that load when you visit; with Socket technology, the server can continuously update your view with new information.

For Socket.io communication, “events” are triggered by either the server or connected clients, and corresponding callback functions are set to execute when these events are detected. Implementing event triggers or binding event callbacks are very simply implemented in Flask (after some initial setup) using:

# Basic blask server to catch events
from flask import Flask, render_template
from flask.ext.socketio import SocketIO, emit

app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socketio = SocketIO(app)

@socketio.on('my event')                          # Decorator to catch an event called "my event":
def test_message(message):                        # test_message() is the event callback function.
    emit('my response', {'data': 'got it!'})      # Trigger a new event called "my response" 
                                                  # that can be caught by another callback later in the program.

if __name__ == '__main__':
    socketio.run(app)

Four events are allowed in the @socketio.on() decorator – ‘connect’, ‘disconnect’, ‘message’, and ‘json’. Namespaces can also be assigned to keep things neatly separated, and the send() or emit() functions can be  used to send ‘message’ or custom events respectively – see the details on Miguel’s page.

On the client side, a little bit of JavaScript wizardry with jQuery is used to handle incoming and trigger outgoing events. I would really recommend the JavaScript path on CodeSchool if you are not familiar with these technologies.

// Client Side Javascript to receive numbers.
$(document).ready(function(){
    // start up the SocketIO connection to the server - the namespace 'test' is also included here if necessary
    var socket = io.connect('http://' + document.domain + ':' + location.port + '/test');
    // this is a callback that triggers when the "my response" event is emitted by the server.
    socket.on('my response', function(msg) {
        $('#log').append('<p>Received: ' + msg.data + '</p>');
    });
    //example of triggering an event on click of a form submit button
    $('form#emit').submit(function(event) {
        socket.emit('my event', {data: $('#emit_data').val()});
        return false;
    });
});

And that, effectively, is the bones of sending messages between client and server. In this specific example, we want the server to be continually working in the background generating new information, while at the same time allowing new clients to connect, and pushing new information to connected clients. For this purpose, we’ll be using the Python threading module to create a thread that generates random numbers regularly, and emits the newest value to all connected clients. Hence, in application.py, we define a thread object that will continually create random numbers and emit them using socketIO separately to the main flask process:

#Random Number Generator Thread

thread = Thread()
thread_stop_event = Event()

class RandomThread(Thread):
    def __init__(self):
        self.delay = 1
        super(RandomThread, self).__init__()

    def randomNumberGenerator(self):
        """
        Generate a random number every 1 second and emit to a socketio instance (broadcast)
        Ideally to be run in a separate thread?
        """
        #infinite loop of magical random numbers
        print("Making random numbers")
        while not thread_stop_event.isSet():
            number = round(random()*10, 3)
            print number
            socketio.emit('newnumber', {'number': number}, namespace='/test')
            sleep(self.delay)

    def run(self):
        self.randomNumberGenerator()

In our main Flask code then, we start this RandomThead running, and then catch the emitted numbers in Javascript on the client. Client presentation is done, for this example, using a simple bootstrap themed page contained in the Flask Template folder, and the number handling logic is maintained in the static JavaScript file application.js. A running list of 10 numbers is maintained and all connected clients will update simultaneously as new numbers are generated by the server.

 //Client-side Javascript code for handling random numbers
$(document).ready(function(){
    //connect to the socket server.
    var socket = io.connect('http://' + document.domain + ':' + location.port + '/test');
    var numbers_received = [];

    //receive details from server
    socket.on('newnumber', function(msg) {
        console.log("Received number" + msg.number);
        //maintain a list of ten numbers
        if (numbers_received.length >= 10){
            numbers_received.shift()
        }
        numbers_received.push(msg.number);
        numbers_string = '';
        for (var i = 0; i < numbers_received.length; i++){
            numbers_string = numbers_string + '<p>' + numbers_received[i].toString() + '</p>';
        }
        $('#log').html(numbers_string);
    });

});

And in the application.py file:

#Python code to start Asynchronous Number Generation

@app.route('/')
def index():
    #only by sending this page first will the client be connected to the socketio instance
    return render_template('index.html')

@socketio.on('connect', namespace='/test')
def test_connect():
    # need visibility of the global thread object
    global thread
    print('Client connected')

    #Start the random number generator thread only if the thread has not been started before.
    if not thread.isAlive():
        print("Starting Thread")
        thread = RandomThread()
        thread.start()

And that’s the job. Flask served web pages that react to events on the server. I was tempted to add a little graph using HighCharts or AmCharts, but I’m afraid time got the better of me. Perhaps in part2. The final output should look like this:

Flask screenshot

You can find all of the source code on GitHub, with instructions on how to install the necessary libraries etc. Feel free to adapt to your own needs, and leave any comments if you come up with something neat or have any problems.

This functionality is documented in the original documentation for Flask-SocketIO. The documentation and tutorials are quite comprehensive and worth working through if you are interested in more.

Subscribe
Notify of

29 Comments
Inline Feedbacks
View all comments

Hi,

Why is it only within the console are the random numbers being generated, but nothing on the browser after

Number list:

Thanks much appreciated.

Seb

Or simply updating it within the same TextField, similar to how real time stock quotes are displayed?

Hi,
Thanks for this tutorial, I downloaded your code, run it but got this :

(flaskiotest) C:\Users\alotfi\PycharmProjects\async_flask-master>python application.py
WebSocket transport not available. Install eventlet or gevent and gevent-websocket for improved performance.
* Running on http://127.0.0.1:5000/
* Restarting with reloader
WebSocket transport not available. Install eventlet or gevent and gevent-websocket for improved performance.

Thanks, your help is appreciated.

I Have the same problem, not element in number list…
any response ?
Thnx

That last part is essential !

Hi guys,
download the jquery and socketio files and put this in static directory.
change this code in the head:

change this:
import eventlet
eventlet.monkey_patch()
from flask_socketio import SocketIO, emit

and this:

#turn the flask app into a socketio app
async_mode = “eventlet”

socketio = SocketIO(app, async_mode=async_mode)

and it works

Which jquery and sockio files? download from where?

I also can’t find a python module called eventlet. And, monkey.py appears to be in the gevent module. No sign of the monkey_patch() in gevent.

My bad, I found the eventlet module and applied this to the application.py:
import eventlet
eventlet.monkey_patch()

It works without adding this:
async_mode = “eventlet”

socketio = SocketIO(app, async_mode=async_mode)

It also works when it is added…

BTW: I updated the index.html file to:

also modified the print statements to Python 3 style.

So, I can report it now works in python 3.5.2.

I have the same issues as SebastianC. Is there somebody that can help us with this issue?

Running python 3.5.1 on windows 10. Made appropriate changes to application.py but no other files (should i?). index.html displays nicely but no numbers updated. Can you please help out?

How would one go about if one wanted to have not the same random numbers for each client, but a different set of data for each?

Error on line :- async_mode=”eventlet”
–SyntaxError: Non-ASCII character ‘\xe2’ in file application.py on line 26, but no encoding declared; see http://python.org/dev/peps/pep-0263/ for details

after adding codecs at top:- still error occurred on same line saying :-
SyntaxError: ‘ascii’ codec can’t decode byte 0xe2 in position 0: ordinal not in range(128)

If possible please help

@chirag you need to replace the ” with ” that wraps eventlet. you cant copy and paste

Thank you for a great tutorial!

Could someone please answer the following question which i also see above:
Which jquery and sockio files? download from where?
I am trying this for the first time, not seeing any numbers probably because i haven’t done the above step.

Hi there. Thank you very much for this tutorial!

One issue I found is that in an attempt to clone the repository and then set up my virtual environment, you indicate to : ./flaskiotest/Scripts/activate
which returns : bash: ./flaskiotest/Scripts/activate: No such file or directory

At closer inspection, there does not appear to be a Scripts directory within /flaskiotest
I did, however, find ‘activate’ inside /flaskiotest/bin
I tried : ./flaskiotest/bin/activate
which returns : bash: ./flaskiotest/bin/activate: Permission denied

Any insight greatly appreciated.

That said, if I should instead try to run without virtual environment, are the only dependencies Flask, Flask_SocketIO, and the associated .js (and css) ?

Regarding my previous reply…
Instead of :
I tried:
source flaskiotest/bin/activate

and the virtual env appears to be working.

Now on to the fun stuff!

I noticed that a missing closing for

Something else to add on, In my case, I added this modification : when the client disconnects, also stop the thread using :

if thread.isAlive():
global thread_stop_event
thread_stop_event.set()
print(‘Disconected & thread stopped’)

inside test_disconnects() because I noticed the thread continued in the server even after client closes the browser.
And to restart it, something like:

thread_stop_event.clear() inside the test_connect() function, I noticed you initialised that variable and only performed checks but never manipulated it.

Saved the day! was struggling with this for hours!

How can you change the code to only show the most recent random number, not all 10?

replace the for loop in js code where the logic of getting all 10 numbers lies with something you need(like only the most recent number).

This can’t handle multiple clients

This is by far the simplest and clearest working up-to-date example of Flask-SocketIO – Thank you.