J'ai trouvé le travail de Miguel Grinberg très inspirant et j'ai choisi, pour un projet universitaire, d'utiliser Flask avec la bibliothèque FlaskSocketIO pour faire de l'instrumentation Java (trucs assez compliqué mais pour les curieux je travaille sur this project).Python Flask Serveur: AssertionError ("write() before start_response()")
Fondamentalement, la partie python crée simplement une commande, l'exécutant dans un thread d'arrière-plan et poussant chaque journal à l'écran, grâce à cette grande bibliothèque Flask SocketIO. Tout ce qui est décrit fonctionne plutôt bien MAIS j'ai un problème quand je veux relancer la tâche en appelant une seconde fois la même URL, je reçois cette erreur et je n'ai trouvé aucun fil dessus donc je suppose que c'est trivial ou très méchant (mais rien au milieu!). Est-ce que quelqu'un a une idée?
File "/Library/Python/2.7/site-packages/eventlet/wsgi.py", line 485, in handle_one_response
write(b'')
File "/Library/Python/2.7/site-packages/eventlet/wsgi.py", line 380, in write
raise AssertionError("write() before start_response()")
AssertionError: write() before start_response()
Et, bien sûr, voici mon code (inspiré de l'exemple de Miguel parce que je ne pouvais pas le mettre moi-même ...)
#!/usr/bin/env python
async_mode = None
if async_mode is None:
try:
import eventlet
async_mode = 'eventlet'
except ImportError:
pass
if async_mode is None:
try:
from gevent import monkey
async_mode = 'gevent'
except ImportError:
pass
if async_mode is None:
async_mode = 'threading'
print('async_mode is ' + async_mode)
# monkey patching is necessary because this application uses a background
# thread
if async_mode == 'eventlet':
import eventlet
eventlet.monkey_patch()
elif async_mode == 'gevent':
from gevent import monkey
monkey.patch_all()
import time
from threading import Thread
import subprocess
from os import chdir
from flask import Flask, render_template
from flask_socketio import SocketIO, emit
import InstrumentationScripts as IS
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socketio = SocketIO(app, async_mode=async_mode)
thread = None
def background_thread():
time.sleep(1)
socketio.emit('my response',
{'data': "Thread Started", 'count': 0},
namespace='/test')
cb = IS.CommandBuilder()
args = cb.createCommand().split()
chdir(cb.InstrumentationPepDirectory)
process = subprocess.Popen(args, stdout=subprocess.PIPE)
for out in iter(process.stdout.readline, b""):
out = '<pre>' + out + '</pre>'
socketio.emit('my response', {'data': out, 'count':0}, namespace='/test')
time.sleep(0.001)
socketio.emit('my response',
{'data': "Thread Finished", 'count': 0},
namespace='/test')
@app.route('/')
def index():
global thread
if thread is None:
thread = Thread(target=background_thread)
thread.daemon = True
thread.start()
return render_template('index.html')
@socketio.on('connect', namespace='/test')
def test_connect():
emit('my response', {'data': 'Connected', 'count': 0})
if __name__ == '__main__':
socketio.run(app)
Si vous devez avoir la ensemble du code, voici le repo
Je ne suis pas sûr de comprendre comment relancer le thread bg. Vous avez copié l'exemple officiel, qui utilise un fil qui ne finit jamais. Dans votre cas, votre thread se termine, mais la variable globale qui pointe dessus n'est pas réinitialisée lorsque cela se produit. Au moins, il n'est pas clair pour moi comment cela se passe si c'est le cas.Aussi, je ne comprends pas ce que vous entendez par "appeler une seconde fois la même URL". Qui appelle? Quelle URL? – Miguel
Je vois, je comprends un peu plus encore. Ce que je voulais dire par "appeler une seconde fois la même URL" est de refaire un appel sur le "/" afin que le 'index()' soit appelé à nouveau (juste en le rouvrant dans le navigateur). J'ai une très mauvaise connaissance du sujet multithread mais théoriquement je pourrais juste enlever la condition 'if' dans l'index et ne pas rendre mon fil global ne pourrais pas je? –
C'est plus compliqué que ça, je pense. Le fil bg dans mon exemple d'application diffuse des messages à tous les clients, sans les adresser à un client spécifique. On dirait que ce que vous voulez est de démarrer un thread bg par client, et dans ce thread, vous n'émettez des messages qu'à ce client. Vous devez également arrêter les threads lorsque leur client associé s'éloigne (c'est-à-dire que vous obtenez l'événement de déconnexion). – Miguel