web-dev-qa-db-fra.com

Flacon jeté 'travaillant en dehors du contexte de la demande' lors du démarrage du sous-thread

J'essaie de démarrer un nouveau thread en Python dans une application Flask. Je fais un travail en arrière-plan qui est déclenché par la demande, mais je n'ai pas besoin d'attendre que le travail soit fait pour répondre à la demande.

Est-il possible de définir la demande de flacon dans cette sous-menace par rapport à la demande reçue? Raison d'être, notre liste de contrôle d'accès sur nos requêtes à notre base de données (mongoengine devant mongoDB) repose sur l'utilisateur de la requête (il la récupère de l'objet requête du flask) pour voir s'il a accès aux objets non disponible dans le sous-fil.

Toute pensée serait très appréciée.

Voici le pseudo-code de la façon dont je le gère maintenant, mais cela ne fonctionne pas.

@app.route('/my_endpoint', methods=['POST'])
def my_endpoint_handler():
    #do tracking in sub-thread so we don't hold up the page
    def handle_sub_view(req):
        from flask import request
        request = req
        # Do Expensive work
    thread.start_new_thread(handle_sub_view, (request))
    return "Thanks"
45
MattoTodd

Enveloppez le code de votre fil dans un test_request_context afin d’avoir accès à locals de contexte } _:

@app.route('/my_endpoint', methods=['POST'])
def my_endpoint_handler():
    #do tracking in sub-thread so we don't hold up the page
    def handle_sub_view(req):
        with app.test_request_context():
            from flask import request
            request = req
            # Do Expensive work
    thread.start_new_thread(handle_sub_view, (request))
    return "Thanks"

Edit: il est intéressant de noter que le thread aura un contexte différent de celui de la requête d'origine. Vous devez extraire toutes les données de demande intéressantes, telles que l'ID utilisateur, avant de générer le thread. Vous pouvez ensuite récupérer un objet utilisateur (différent) dans le sous-fil à l'aide de l'ID.

57
Alex Morega

Depuis la version 0.10, il existe un moyen de le faire: http://flask.pocoo.org/docs/api/#flask.copy_current_request_context

Si vous voulez que les hooks before_request s'exécutent, vous devez appeler current_app.preprocess_request() à l'intérieur de la fonction décorée.

30
runfalk

Vous pouvez copier les informations souhaitées et les transmettre:

@app.route('/my_endpoint', methods=['POST'])
def my_endpoint_handler():
    #do tracking in sub-thread so we don't hold up the page
    def handle_sub_view(data):
        # Use the data in subprocess
    data = request.get_json()  # copy the data
    thread.start_new_thread(handle_sub_view, data)
    return "Thanks"
5
vim

Comme @runfalk l'a signalé, vous devrez utiliser @copy_current_request_context . Voici un extrait de code fonctionnel:

import threading

from flask import request, jsonify, copy_current_request_context


@app.route('/foo')
def get_foo():
    @copy_current_request_context
    def foo_main():
        # insert your code here
        print(request.url)

    threading.Thread(target=foo_main).start()

    return jsonify({'status': 'started'})
0
jrc