Pourquoi le serveur de développement Flask s'exécute-t-il deux fois?

107

J'utilise Flask pour développer un site Web et pendant le développement, j'exécute flask en utilisant le fichier suivant:

#!/usr/bin/env python
from datetime import datetime
from app import app
import config

if __name__ == '__main__':
    print '################### Restarting @', datetime.utcnow(), '###################'
    app.run(port=4004, debug=config.DEBUG, host='0.0.0.0')

Lorsque je démarre le serveur, ou lorsqu'il redémarre automatiquement parce que les fichiers ont été mis à jour, il affiche toujours deux fois la ligne d'impression:

################### Restarting @ 2014-08-26 10:51:49.167062 ###################
################### Restarting @ 2014-08-26 10:51:49.607096 ###################

Bien que ce ne soit pas vraiment un problème (le reste fonctionne comme prévu), je me demande simplement pourquoi il se comporte comme ça? Des idées?

kramer65
la source

Réponses:

153

Le rechargement Werkzeug génère un processus enfant afin qu'il puisse redémarrer ce processus chaque fois que votre code change. Werkzeug est la bibliothèque qui fournit à Flask le serveur de développement lorsque vous appelez app.run().

Voir le restart_with_reloader()code de fonction ; votre script est à nouveau exécuté avec subprocess.call().

Si vous définissez use_reloadersur, Falsevous verrez le comportement disparaître, mais vous perdrez également la fonctionnalité de rechargement:

app.run(port=4004, debug=config.DEBUG, host='0.0.0.0', use_reloader=False)

Vous pouvez également désactiver le rechargeur lorsque vous utilisez la flask runcommande:

FLASK_DEBUG=1 flask run --no-reload

Vous pouvez rechercher la WERKZEUG_RUN_MAINvariable d'environnement si vous souhaitez détecter lorsque vous êtes dans le processus enfant de rechargement:

import os
if os.environ.get('WERKZEUG_RUN_MAIN') == 'true':
    print '################### Restarting @ {} ###################'.format(
        datetime.utcnow())

Cependant, si vous avez besoin de configurer des globaux de module, vous devez à la place utiliser le @app.before_first_requestdécorateur sur une fonction et que cette fonction configure ces globaux. Il ne sera appelé qu'une seule fois après chaque rechargement lorsque la première demande arrivera:

@app.before_first_request
def before_first_request():
    print '########### Restarted, first request @ {} ############'.format(
        datetime.utcnow())

Tenez compte du fait que si vous l'exécutez sur un serveur WSGI à grande échelle qui utilise la fourche ou de nouveaux sous-processus pour gérer les demandes, ces before_first_requestgestionnaires peuvent être appelés pour chaque nouveau sous-processus.

Martijn Pieters
la source
2
Ah ok. Merci pour l'explication! Donc son comportement considéré comme normal? Au moins bon que rien ne cloche avec mon code .. :)
kramer65
1
@ kramer65: c'est un comportement tout à fait normal et attendu. :-)
Martijn Pieters
1
Existe-t-il un moyen pratique d'exécuter du code d'initialisation lente une seule fois, en s'assurant qu'il est également appelé lors de l'exécution sous wsgi (c'est-à-dire pas depuis app.run), mais sans attendre la première requête? Je ne veux pas que cette première demande soit surchargée par le coût d'initialisation.
Kylotan
1
@Kylotan: il faudrait inspecter l'environnement; si vous ne définissez DEBUG que lors de l'exécution en développement, vous pouvez rechercher la WERKZEUG_RUN_MAINvariable d'environnement et n'exécuter votre code que lorsque DEBUGest false ou WERKZEUG_RUN_MAINest défini, par exemple. Devient un peu fastidieux.
Martijn Pieters
Juste pour clarifier, je pensais que "fonctionnalité de rechargement" signifiait réactivité (ce qui irait à l'encontre de l'objectif de l'utilisation dashpour moi). Pour tout autre noobscomme moi, cela signifie uniquement la fonctionnalité dans laquelle l'édition / l'enregistrement du fichier déclenche une mise à jour en direct.
Hendy
12

Si vous utilisez la flask runcommande moderne , aucune des options à app.runn'est utilisée. Pour désactiver complètement le rechargeur, passez --no-reload:

FLASK_DEBUG=1 flask run --no-reload

De plus, __name__ == '__main__'cela ne sera jamais vrai car l'application n'est pas exécutée directement. Utilisez les mêmes idées de la réponse de Martijn , sauf sans le __main__blocage.

if os.environ.get('WERKZEUG_RUN_MAIN') != 'true':
    # do something only once, before the reloader

if os.environ.get('WERKZEUG_RUN_MAIN') == 'true':
    # do something each reload
davidisme
la source
7

J'ai eu le même problème et je l'ai résolu en réglant app.debugsur False. Le paramétrer Trueme faisait __name__ == "__main__"appeler deux fois.

Carvell Wakeman
la source
Mon __main__fonctionne toujours deux fois avec app.debug = Falseet app.run_server(debug=False). Êtes-vous sûr que cela l'a fait pour vous, ou pourriez-vous publier du code reproductible pour essayer?
Hendy
Changer app.debug a été tout ce que j'ai fait pour le résoudre pour moi. Pouvez-vous confirmer que main ne s'exécute que deux fois lorsque le serveur flask est démarré? Essayez d'exécuter un exemple de travail minimal et voyez si le problème se produit. Essayez également d'exécuter un exemple minimal qui échoue dans plusieurs versions de Python, cela peut avoir posé un problème. Depuis, j'ai migré mon projet vers Java et SparkJava au lieu de python et flask, donc je ne me souviens pas exactement de ce qui a résolu le problème.
Carvell Wakeman
J'utilise flaskvia plotly dash, et j'ai découvert qu'ils avaient récemment modifié l' debug argument par défaut passé à flask. Je vais deviner que je me suis trompé ci-dessus et que je l'ai peut-être faitapp.debug=False (ce qui est peut-être remplacé par l'argument par défaut de run_server), ou seulement essayé sans passer True, pas explicitement comme indiqué ci-dessus. Cela fonctionne correctement pour moi maintenant (en vous assurant que debug=False). Merci!
Hendy
2

À partir de Flask 0.11, il est recommandé d'exécuter votre application avec flask runplutôt que python application.py. L'utilisation de ce dernier pourrait entraîner l'exécution de votre code deux fois.

Comme indiqué ici :

... à partir du flacon 0.11, la méthode du flacon est recommandée. La raison en est qu'en raison du fonctionnement du mécanisme de rechargement, il y a des effets secondaires bizarres (comme exécuter certains codes deux fois ...)

salsa_man
la source
0

L'une des raisons possibles pour lesquelles l'application Flask s'exécute deux fois est une configuration de WEB_CONCURRENCY paramètre sur Heroku. Pour définir en un, vous pouvez écrire dans la console heroku config:set WEB_CONCURRENCY=1

Trojek
la source
-1

Une observation concernant les fils

Ceci est particulièrement ennuyeux lorsque votre application utilise des threads car ils seront déclenchés deux fois au démarrage. Pour autant que j'ai essayé les singletons, cela ne remédie pas non plus (ce qui est surprenant). Cependant, l'ajout d'un délai initial de quelques secondes avant le démarrage de votre thread peut résoudre le problème.

Si l'application redémarre plus rapidement qu'avant la fin de votre délai, le thread donné n'est généré qu'une seule fois, après le redémarrage.

pfabri
la source
@Dowvoter: voulez-vous expliquer pourquoi?
pfabri
-1

J'ai eu le même problème. Je l'ai résolu en modifiant mon principal et en y insérant use_reloader = False. Si un organisme est ici à la recherche d'une solution de contournement à ce problème, le code ci-dessous vous permettra de démarrer, mais la fonctionnalité des modifications de code détectées automatiquement et le redémarrage de l'application ne fonctionneront pas. Vous devrez arrêter et redémarrer manuellement votre application après chaque modification du code.

if __name__ == '__main__':
    app.run(debug=True, use_reloader=False)
LA
la source