I am experimenting with Flask-Sockets. Support for Blueprints was added in the past, something I really do need.
from flask import Flask, request, abort, redirect, url_for, render_template, make_response, Response, jsonify, session
import json
#Importing blueprints
from play import play, play_socket
app = Flask(__name__)
sockets = Sockets(app)
app.register_blueprint(play, url_prefix=r"/play")
sockets.register_blueprint(play_socket, url_prefix=r"/play")
#sockets.route('/echo')
def echo_socket(ws):
while not ws.closed:
message = ws.receive()
response = json.dumps({"Message":message,"Response":"Message received"})
ws.send(response)
So, connecting a websocket in JavaScript and setting the endpoint to 'echo' (i.e. var ws = new WebSocket("ws://HOST:PORT/"+"echo")) works perfectly. I can send messages and they get bounced right back at me.
However, when I want to move this function to a blueprint (in the blueprint play_socket, it doesn't work anymore. Assume I changed the endpoint to '/status_ping' in the javascript:
#play_socket.route('/status_ping')
def ping(ws):
while not ws.closed:
message = ws.receive()
response = json.dumps({"Message":message,"Response":"Message received"})
ws.send(response)
The websocket is connected successfully from the client-side and I can confirm this by inserting a simple print("HERE") or whatever in the def ping(socket):, but it's immediately closed afterwards, rendering it useless.
I found out that if I move the body of the while not ws.closed: loop above the header (copy it), it 'works'. However, I can't use this as I need the socket to push data from the server to the clients. It seems to be going wrong when this while loop is executed. The socket is immediately closed for some reason. changing while not ws.closed: to while True: has no effect.
I tried to isolate the problem as much as I could, but please let me know if you need more info.
EDIT: Codesample for blueprint
from flask import Flask, request, abort, redirect, url_for, render_template, make_response, Response, jsonify, session, current_app
import sys
sys.path.append('../')
from flask_sockets import Sockets
import json
import time
api_blueprint = Blueprint('api_blueprint', __name__)
#sockets.route('/update_status')
def echo_socket(ws):
message = ws.receive()
while not ws.closed:
ws.send(json.dumps({"data":message}))
time.sleep(1)
if ws.closed:
session.clear()
print("session cleared")
sockets = Sockets(current_app)
sockets.register_blueprint(api_blueprint, url_prefix=r"/api")
The main run.py file where the app context is available, is started with this:
if __name__ == "__main__":
from gevent import pywsgi
from geventwebsocket.handler import WebSocketHandler
server = pywsgi.WSGIServer(('0.0.0.0', 15080), app, handler_class=WebSocketHandler)
print(server)
server.serve_forever()
Hi Lennart Kloppenburg,
Try to put the #play_socket.route('/status_ping') blueprint route before sockets = Sockets(app).
The following works for me and I can talk to the ping websocket on /play/status_ping:
from flask import Blueprint, Flask, request, abort, redirect, url_for, render_template, make_response, Response, jsonify, session
from flask_sockets import Sockets
import json
play = Blueprint('simple_page', __name__)
play_socket = Blueprint('simple_page2', __name__)
#play_socket.route('/status_ping')
def ping(ws):
while not ws.closed:
message = ws.receive()
response = json.dumps({"Message":message,"Response":"Message received"})
ws.send(response)
app = Flask(__name__)
sockets = Sockets(app)
app.register_blueprint(play, url_prefix=r"/play")
sockets.register_blueprint(play_socket, url_prefix=r"/play")
Related
As I'm using the session to close after 10min the user is inactive. But now I want to clear the Session when a user closes the browser or tab.
from flask import Flask, render_template, request, session, redirect, url_for, flash, Response, Blueprint, jsonify, \
send_file,copy_current_request_context
import string
import random
import base64
import binascii
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
import os
app = Flask(__name__,template_folder='templates',static_folder='static')
secret_key_=os.urandom(24)
app.secret_key = secret_key_
app.config['SESSION_TYPE'] = 'filesystem'
app.debug = True
BLOCK_SIZE = 16
def pad(data):
length = BLOCK_SIZE - (len(data) % BLOCK_SIZE)
return data + chr(length)*length
def unpad(data):
data = data[:-data[-1]]
return data
def decrypt(encrypted, key):
BLOCK_SIZE = 16
encrypted = base64.b64decode(encrypted)
IV = encrypted[:BLOCK_SIZE]
aes = AES.new(key[0:16], AES.MODE_CBC, IV)
return unpad(aes.decrypt(encrypted[BLOCK_SIZE:]))
#app.route('/', methods=["GET", "POST"])
def login():
if request.method == "OPTIONS":
return 403
else:
return render_template("home.html")
#app.route('/encryp', methods=["GET", "POST"])
def encryp():
if request.method == "OPTIONS":
return 403
else:
if session.get('page_buffer') is None:
print('here123')
key = b"gAAAAABfafxf7_uZ--GzUq5GMBc6h"
temp_=decrypt(str(request.form["fname"]),key)
temp_1 = decrypt(str(request.form["lname12"]), key)
session['page_buffer'] = "Yes"
session['fname']=temp_
session['lname12'] = temp_1
fname=session.get('fname')
password = session.get('lname12')
#password = decrypt(str(request.form["lname12"]),key)
return render_template("Cep.html",Name12=fname,Password12=password)
else:
print('here')
fname = session.get('fname')
password = session.get('lname12')
# password = decrypt(str(request.form["lname12"]),key)
return render_template("Cep.html", Name12=fname, Password12=password)
if __name__ == '__main__':
app.run()
Can Anyone please help me how to detect a user closing a browser or a tab in the flask? Because user should not be viewing the same page once he closes the browser/tab.
JavaScript examples (not working):
The below two are not working because when I'm redirecting to another page these two are getting hit but I don't want that I only want to monitor browser close.
window.addEventListener('beforeunload', function(event) {
console.log('I am the 2nd one.');
});
window.addEventListener('unload', function(event) {
console.log('I am the 4th and last oneā¦');
});
if you can suggest a better way to use JavaScript then it is okay for me.
You could try to set a cookie and once the route is accessed again, make Flask check whether such a cookie already exists. If it does, you know the user has been there before.
Cookie behavior is largely altered by browser though and can also be manipulated by users easily.
A more reliable and safer way would be to save it in a database, in case you can identify the user by email or something alike.
I'm a new with flask and now I need to makes my page dynamic.
That's why I'm trying to send data from JS to Python using the JSON format with AJAX but surfing the web I can't understand how to do it.
Can anyone show me a simple implementation of how AJAX is used on Flask to change the value of a variable every X time?
This my flask app:
from flask import Flask, render_template, request, redirect, url_for, session, flash, jsonify, make_response
app = Flask(__name__)
#app.route("/getdata", methods = ['POST', 'GET'])
def getdata():
#get JSON data and change it
return data
#app.route("/data")
def data():
# show dynamically the data
return render_template("data.html")
if __name__ == "__main__":
app.run(debug=True, port=5000)
This is my js function, the gauge array is the value to make dynamic
setInterval(
ciccio.refreshValue({{gauge[0]}})
,wrapper.refreshValue({{gauge[1]}})
,wrapper1.refreshValue({{gauge[2]}})
,wrapper2.refreshValue({{gauge[3]}})
,wrapper3.refreshValue({{gauge[4]}})
,wrapper4.refreshValue({{gauge[5]}})
,wrapper5.refreshValue({{gauge[6]}})
,wrapper6.refreshValue({{gauge[7]}})
,500);
)
I need to refresh the gauge array
I have resolved by this code
python:
def getdata():
return jsonify({'results' : sample(range(101), 2)}) # some data from the server
#app.route("/misuratore")
def data():
return render_template("misuratore.html")
javascript
setInterval(
function updateGauge() {
var updatedData = $.get('/getdata'); //get data from localhost/getdata
updatedData.done(function(results) { // when the get are completed launch function that apply the new value
pippo.refreshValue(results.results[0])
pluto.refreshValue(results.results[1])
});
},1000); // every second
Goal is simply to be able to make a thread queue of dictionaries and report them to client.
EDIT
This is different of Flask throwing 'working outside of request context' when starting sub thread because:
It is not done in a route function, it is done in socketio.start_background_task
The only socketio code takes place in context, with the socketio.emit we are sending a dictionary.
Strategy:
There are 2 different taks to perform in server side, for each build a thread, then in another socketio thread collect the results which are in a thread safe queue FIFO of dictionaries.
Then send these dictionaries to client and wait for each acknowledge.
So now the issue is reduced to solve:
RuntimeError: Working outside of request context.
from flask import Flask, flash, request, redirect, render_template, Response, escape, jsonify, url_for, session, copy_current_request_context
#socketio
from flask_socketio import SocketIO, send, emit, join_room, leave_room, close_room, rooms, disconnect
import threading
from threading import Thread, Event, Lock
import queue
import random
def ack(value):
if value != 'pong':
logger.info('unexpected return value')
def fn_i():
global q
while True:
time.sleep(1)
q.put({'key_i':random.random()})
return q
def fn_ii():
global q
while True:
time.sleep(10)
q.put({'key_ii':random.random()})
return q
app = Flask(__name__)
socketio = SocketIO(app, async_mode=async_mode)
thread1=None
thread2=None
collector_thread=None
q = queue.Queue()
thread_lock = Lock()
def background_thread_collector():
global thread1
global thread2
global q
thread1 = threading.Thread(target=fn_i)
thread1.start()
thread2 = threading.Thread(target=fn_ii)
thread2.start()
"""Example of how to send server generated events to clients."""
while True:
time.sleep(0.2)
while not q.empty():
socketio.emit('my_response',
q.get(), #{'data': 'Server generated event', 'count': count},
namespace='/test',
broadcast=True,
callback=ack
)
#app.route('/')
def index():
return render_template('index.html', async_mode=socketio.async_mode)
#socketio.on('connect', namespace='/test')
def test_connect():
global collector_thread
logger.info(' Client connected ' + request.sid)
with thread_lock:
if collector_thread is None:
collector_thread = socketio.start_background_task(background_thread_collector)
emit('my_response', {'data': 'Connected', 'count': 0})
if __name__ == '__main__':
socketio.run(app,
host='localhost',
port=10000,
debug=False) #True sends some exceptions and stops)
Cheers
This should be handled better by Flask-SocketIO, but the problem is that you are trying to use a callback on an emit that is set to broadcast to all clients:
socketio.emit('my_response',
q.get(), #{'data': 'Server generated event', 'count': count},
namespace='/test',
broadcast=True,
callback=ack
)
Remove the callback and the emit should work just fine.
Using threading queues with Flasksocketio is not straightforward because requires to handle the apps context among other things, for purpose of refreshing a server log file in client side found it was easier to simply use javascript in this case and the file can be updated accordingly. Even the former code with suggested alterations was not working because of apps context and nevertheless there are multiple blogs or stackoverflow which approach subject in none have found a complete working solution except implementing like explain because any other answer requires complete code which is working, and hence since able to implement here considering this is the accepted answer, Cheers.
So I have set up app.py, index.js, index.html in appropriate folder as flask suggests. Index.html gets rendered as when app.py runs then index.html runs index.js which grabs input data from user. I am trying to send this input and send it to python where I can call an API, grab data, and work with it however I cannot think of a way to do this.
my app.py:
import os
from flask import Flask, render_template, jsonify, request, redirect
app = Flask(__name__)
# This will run upon entrance
#app.route("/")
def home():
return render_template("index.html")
#app.route("/stock_data")
def get_stock_data():
# called from index.js Plot function
if __name__ == "__main__":
app.run()
and here is my javascript code:
console.log("everythin works fine.")
d3.select("#stocklabelsubmit").on("click", submitted)
function submitted(){
d3.event.preventDefault();
// grab label inputted.
var inputted_label = d3.select("#stockInput").node().value;
d3.select("#stockInput").node().value = "";
Plot(inputted_label);
};
function Plot(input){
var url = "full url"
// when this function is called call /stock_data function!!
// data is what is returned from python
d3.json(url).then(function(data){
})
}
Everything works fine, when I console log inputted_label at the end of function submitted it works. Now I want to send inputted_label variable to /stock_data. Thanks in advance!
var url = "/stock_data"
This needs to be a valid URL, not just the path to the Flask endpoint. That means it must start with "http://" or "https://" and include a domain. For development purposes, "http://localhost/stock_data" will work. If you ever deploy this to a server, you will want to create a configuration file so that the host name can be configured depending on what environment you are running in.
I've been trying very hard to figure this out but no luck so far.
So I am sending some data from server to client using socket in a loop however the client is unable to receive all the data and closes the socket in the middle of data transmission for no reason.
As you can see in the image below client successfully receives data till 11th iteration of the loop(refer to the server code below) however after that socket is closed cause of transport error. What possibly could I be doing wrong here?
Client side logs
Server side logs
(Python) server side code
from flask import Flask, render_template, request, flash, redirect, jsonify, make_response
from flask_socketio import SocketIO, emit, disconnect
import time
from gevent import monkey
monkey.patch_all()
app = Flask(__name__)
socketio = SocketIO(app, engineio_logger=True)
#socketio.on('run_tgt')
def run_tg(tg_args):
for x in range(20):
time.sleep(2)
emit('tg_output',x)
if __name__ == "__main__":
socketio.run(app, host='0.0.0.0', debug=True)
(Javascript) client side code
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.0.3/socket.io.js"></script>
var socket = io.connect('http://' + document.domain + ':' + location.port );
function run_tg() {
socket.emit('run_tgt', { 'data': 'data'});
socket.on('tg_output', function(tg_output) {
console.log(tg_output);
$("#output_div").append(tg_output);
});
}
Fixed the issue by adding monkey patch to the top of the script
from gevent import monkey
monkey.patch_all()
Also set async_handlers to true
socketio = SocketIO(app, async_handlers=True, engineio_logger=True)