Using emit in route Flask python - python

I am having trouble using emit in my route, I have tried many things but nothing seems to be working. My code is as follows:
app.py:
from flask import Flask, render_template
from flask_socketio import SocketIO, emit
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socketio = SocketIO(app)
#app.route('/abc')
def index():
emit('message', {'data': 'Demo'})
return render_template('index.html')
#socketio.on('disconnect')
def on_disconnect():
print('Client disconnected')
if __name__ == '__main__':
socketio.run(app)
index.html:
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js" integrity="sha512-q/dWJ3kcmjBLU4Qc47E4A9kTB4m3wuTY7vkFJDTZKjTs8jhyGQnaUrxa0Ytd0ssMZhbNua9hE+E7Qv1j+DyZwA==" crossorigin="anonymous"></script>
<script>
var socket = io();
socket.on('connect', function () {
console.log("Connected")
});
socket.on('message', function (msg) {
console.log(msg.data);
});
</script>
(?) How can I use emit in route?

Your design is flawed, since you are emitting too early.
Here is the sequence of events:
Client connects to the /abc route from the browser
The index() route runs and calls emit(). At this point the client is not connected, so it does not receive the message.
The index() function returns the index.html template to the browser.
The browser renders the HTML page, and as part of that executes the JavaScript code in it, which makes a Socket.IO connection to the server.
What you can do to verify that the emit is working is open this route in multiple browser tabs. When you open the first one nothing will happen, but when you open the second tab, the client on the first tab is going to receive the emit, since it is already connected. When you open the third tab, tabs 1 and 2 will receive the emit.

Related

FLASK with socketio: Displaying a template if a post request is received from local script during a socket connection

So, I have a local script, which should output a specific message to display to the client of an user connected in the flask app.
From local script to server I've used:
import requests
requests.post(address, json=json)
And the message is received correctly to the server. Problem is that I can't manage to get it displayed through the client because doing something like:
from flask import render_template, url_for, redirect, request
#app.route('/home', methods=['GET', 'POST'])
def home():
if request.method == 'POST':
result = request.json
return render_template('home.html', result=result, title='Homepage', current_user=current_user)
Doesn't effectively render any template.
I suspect that it's due to the nature of a socket connection, so probably I should set up a similar architecture using sockets events.
Problem is that I'd like to have for each user a unique message displayed on the home route and I fear that with sockets it would be impossible to do so.
Forgive me for the bad explanation of my question, I'm still a newbie regarding networks and web development with Flask.
Resolved, it was easier than I imagined. I ended up using socket events instead of http in the end. Here's the code from the server and client side (simplified for others that might have a similar situation).
The only thing that sucked was not being able to use jinja2 to display elements in the webpage. But with a bit of (bad) js I resolved.
Server side:
#app.route('/home', methods=['GET', 'POST'])
def home():
if request.method == 'POST':
result = request.json
socketio.emit('my_socket_event',
result,
broadcast=True #use broadcast=True if you want the message to be displayed to all connected clients
)
Client side:
<script>
var socket = io.connect('http://' + document.domain + ':' + location.port);
socket.on('my_socket_event',function(result) {
console.log('[EVENT CALLED] my_socket_event')
for (var elem in result) {
let htmlFrag = "<p>" + elem + "</p>"
$('.container').append(htmlFrag);
}
})
</script>

Push message to all online users using Flask, WITHOUT creating threads for each users

Suppose ther is a chat group.
Is there a way to append new messages sent by any member of a particular group to the html page of all the online users in that group using FLASK.
WITHOUT: creating a thread for each users and monitoring for latest updates in the chats.
This is an example, with a very minimal code. If you are interested then you can explore more and do the experiments.
Firstly, you need the Flask-SocketIO module, run the below command to install it.
pip install flask-socketio
With this example, the project directory will be something like below:
ProjectDirectory
|
|-> templates
| |
| |-> index.html
|
|-> app.py
app.py
from flask import Flask, render_template
from flask_socketio import SocketIO, emit
app = Flask(__name__)
app.config['SECRET_KEY'] = 'randomSecretKEy#123'
socket = SocketIO(app)
#app.route('/')
def index():
return render_template('index.html')
#socket.on('message')
def message(data):
print(data)
emit('receive_message', data, broadcast=True)
if __name__ == '__main__':
socket.run(app)
index.html
<script src="//cdnjs.cloudflare.com/ajax/libs/socket.io/2.2.0/socket.io.js" integrity="sha256-yr4fRk/GU1ehYJPAs8P4JlTgu0Hdsp4ZKrx8bDEDC3I=" crossorigin="anonymous"></script>
<script src="//code.jquery.com/jquery-1.12.4.min.js" integrity="sha256-ZosEbRLbNQzLpnKIkEdrPv7lOy9C27hHQ+Xp8a4MxAQ=" crossorigin="anonymous"></script>
<script type="text/javascript" charset="utf-8">
var socket = io();
socket.on('connect', function() {
console.log('Connected to server');
});
socket.on('receive_message', function(msg) {
console.log('Received message: ', msg)
})
function sendMessage() {
msg = $('#message').val()
socket.emit('message', msg)
}
</script>
<input type="text" id="message"><button onclick="sendMessage()">Send</button>
Run the Flask app, open two browser tabs, with web developer tools opened side by side. AS soon as you open, you will see that Connected to server message has been logged. Now, type some message and hit the send button, in one tab, and you will see that, the message has been received in another tab's console.
I hope you get a direction with this and it is helpful for you.
Note: Again, I am telling this is a very basic example. Off course, you can add more logic and functionality, and make it more like a chatting app, but it will be out of the scope of this question. So, I will leave it to you and your imagination, to which extent you can learn and take this ahead. You can read about the module's documentation here.

Notification from Flask to JS using socket.IO

I'm using the Flask-SocketIO library which works fine but I need to send a notification with emit to the outside of a socket.io decorator and it's a real pain. Looking at the solutions, many people use rabbitmq or redis but I don't know how to use them.
Here's my code :
from flask import Flask, render_template
from flaskwebgui import FlaskUI
from flask_socketio import SocketIO, emit
app = Flask(__name__)
async_mode = None
app.config['SECRET_KEY'] = 'hello'
socketio = SocketIO(app, async_mode=async_mode, message_queue='amqp:///socketio')
def run_sock():
socketio.run(app, debug=True)
ui = FlaskUI(app, fullscreen=True, server=run_sock,)
#app.route("/")
def index():
return render_template('index.html')
#socketio.on('test', namespace='/test')
def test():
print("test")
if __name__ == "__main__":
ui.run()
io = SocketIO(message_queue='amqp:///socketio')
io.emit('test_emit', {'data': 'toto'}, namespace='/test')
My JS front-end never gets the test_emit message, how do I do?
The problem with your emit is that it appears below the ui.run() call, which does not return until you close the application. Move the emit to any function in your application that executes while the server is running (such as a Flask view function) and it should work just fine.
Why do you have two SocketIO objects in the same process? The socketio instance that you defined near the top of the script can be used anywhere within the process, no need to create a second instance. You do not need to use a message queue for this problem, since you have all the usages of Socket.IO within a single process.

Flask socket IO emit from another module

I have almost read every piece of article available on the internet but nothing seems to work for my case. I have installed flask-socketio and everything works fine until I emit the messages from a module other than app.py.
I have tried several ways to accomplish this and I have also read in the doc about it by using Redis but it also did not work for me. Here are the code snippets that I have.
app.py
from flask import Flask
from flask import request
from flask_socketio import send, SocketIO, emit, join_room
app = Flask(__name__)
# This is to stop force sorting in response, by default jsonify sorts the response keys alphabetically
app.config["JSON_SORT_KEYS"] = False
socketio = SocketIO(app, cors_allowed_origins="*")
#socketio.on('join')
def client_join_room(data):
print(type(data))
room = data['room']
join_room(room)
send('you have entered the room.', room=room)
#app.route('/msg')
def send_message():
socketio.emit("message", "Server message", room='my_room')
return "I got you."
if __name__ == '__main__':
socketio.run(host="0.0.0.0", port=5001, debug=True, app=app)
my_module.py
def some_method():
import app
app.socketio.emit("message", "Some information about process", room='my_room', broadcast=True)
Note that I have imported app inside the method because app.py also imports my_module.py
I am able to join room.
When I call localhost:5001/msg it does emit to 'my_room'.
The emit does not work inside my_module.py and I have no idea why.
I am consoling the messages that I get from the server at the front-end so I know for sure which messages are received and which are not.
Also, the some_method() here is called by an API request from app.py. Just in case if that is relevant.
I have made logger=True and then I get this message printed on the terminal for each emit call. Even with the one inside some_method()
emitting event "message" to my_room [/]
Does that mean message is actually sent? If yes, then why am I not getting it in the jquery at front-end.
This is what I am doing in html page
$(document).ready(function () {
// start up the SocketIO connection to the server
var socket = io.connect('http://localhost:5001/');
// this is a callback that triggers when the "message" event is emitted by the server.
socket.on('message', function(msg){
console.log(msg)
});
socket.emit('join', {room: 'my_room'});
});
Please try and install Redis and eventlet for asynchronous calls and to send messages from other modules. As described in the documentation then you can change your line in app.py to
socketio = SocketIO(app, cors_allowed_origins="*", message_queue='redis://', async_mode='eventlet')

Can't modify the value of the session within event handlers for websockets events

I'm building a little flask&pyhon-based app and my main feature is based on websockets. I discovered that I can't modify the value of the sesssion within event handlers for websockets events(I'm using flask-socketio) because flask stores its session on the client side. So, as the author of the extension recomanded I installed flask-kvsession to store the session on the server-side in a redis-based backend.
I followed the instructions presented http://pythonhosted.org/Flask-KVSession/, but the problem persists. So I created a little program to show you what I'm talking about.
# main.py
from flask import Flask, session, render_template
from flask.ext.socketio import SocketIO
from pprint import pprint
import redis
from flask_kvsession import KVSessionExtension
from simplekv.memory.redisstore import RedisStore
store = RedisStore(redis.StrictRedis())
app = Flask(__name__)
app.debug = True
app.secret_key = 'secret!'
KVSessionExtension(store, app)
socketio = SocketIO(app)
#app.route('/')
def index():
pprint(session)
return render_template("client.html")
#socketio.on('connect')
def handle_connect(message):
session['debug'] = 'debug'
pprint(session)
if __name__ == "__main__":
socketio.run(app)
<!-- templates/client.html -->
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/
socket.io/0.9.16/socket.io.min.js"></script>
<script type="text/javascript">
var sock = io.connect('http:localhost:5000');
sock.emit('connect', {debug: 'debug'});
</script>
</body>
</html>
Here is the output of the werkzeug debuging server:
* Running on http://127.0.0.1:5000/
* Restarting with reloader
<KVSession {}>
127.0.0.1 - - [2014-07-04 21:25:51] "GET / HTTP/1.1" 200 442 0.004452
<KVSession {'debug': 'debug'}>
<KVSession {}>
127.0.0.1 - - [2014-07-04 21:26:02] "GET / HTTP/1.1" 200 442 0.000923
<KVSession {'debug': 'debug'}>
I'd expect that the second time when I will access that page the contents of the session to be 'debug': 'debug' but it is not.
Here is what happened on the redis server while I was running this app:
127.0.0.1:6379> MONITOR
OK
1404498351.888321 [0 127.0.0.1:38129] "GET" "136931c509f674e3_53b6e25b"
1404498352.073011 [0 127.0.0.1:38129] "GET" "136931c509f674e3_53b6e25b"
1404498362.455320 [0 127.0.0.1:38129] "GET" "136931c509f674e3_53b6e25b"
1404498362.612346 [0 127.0.0.1:38129] "GET" "136931c509f674e3_53b6e25b"
As you can see, the value of the session is accessed 4 times, but is never modfied.
So, what should I do to fix this bug?
As explained in the documentation, Flask-SocketIO does not write sessions back to the session store, it just keeps them in memory and accessible to socket handlers.
If you want changes to the server-side session to persist and be available to regular HTTP clients you have to save the session yourself after you make those changes. I haven't tested this myself, but I think this will do:
session.modified = True
app.save_session(session, make_response('dummy'))
Note that save_session() gets a dummy response. This will only work if the session was already established in a regular HTTP call before this. Obviously a new session will not be created with the code above, since the response will be discarded and never reach the client.

Categories