¿Por qué ejecutar el servidor de desarrollo Flask se ejecuta solo dos veces?

107

Estoy usando Flask para desarrollar un sitio web y mientras estoy en desarrollo ejecuto flask usando el siguiente archivo:

#!/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')

Cuando inicio el servidor, o cuando se reinicia automáticamente porque los archivos se han actualizado, siempre muestra la línea de impresión dos veces:

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

Aunque realmente no es un problema (el resto funciona como se esperaba), simplemente me pregunto por qué se comporta así. ¿Algunas ideas?

kramer65
fuente

Respuestas:

153

El cargador de Werkzeug genera un proceso hijo para que pueda reiniciar ese proceso cada vez que cambia su código. Werkzeug es la biblioteca que proporciona a Flask el servidor de desarrollo cuando llamas app.run().

Ver el restart_with_reloader()código de función ; su script se ejecuta de nuevo con subprocess.call().

Si se establece use_reloaderque Falsepodrás ver el comportamiento desaparece, pero luego también se pierde la funcionalidad de recarga:

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

También puede deshabilitar el cargador cuando usa el flask runcomando:

FLASK_DEBUG=1 flask run --no-reload

Puede buscar la WERKZEUG_RUN_MAINvariable de entorno si desea detectar cuándo está en el proceso secundario de recarga:

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

Sin embargo, si necesita configurar módulos globales, entonces debería usar el @app.before_first_requestdecorador en una función y hacer que esa función configure dichos globales. Se llamará solo una vez después de cada recarga cuando llegue la primera solicitud:

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

Tenga en cuenta que si ejecuta esto en un servidor WSGI a gran escala que usa bifurcaciones o nuevos subprocesos para manejar solicitudes, esos before_first_requestcontroladores pueden ser invocados para cada nuevo subproceso.

Martijn Pieters
fuente
2
Ah ok. ¡Gracias por la explicación! Entonces, ¿se considera un comportamiento normal? Al menos bien que mal de nada con mi código .. :)
kramer65
1
@ kramer65: es un comportamiento completamente normal y esperado. :-)
Martijn Pieters
1
¿Existe una forma práctica de ejecutar código de inicialización lento solo una vez, asegurándose de que también se llame cuando se ejecute bajo wsgi (es decir, no desde app.run), pero sin esperar la primera solicitud? No quiero que esa primera solicitud se cargue con el costo de inicialización.
Kylotan
1
@Kylotan: tendrías que inspeccionar el medio ambiente; si solo configura DEBUG cuando se ejecuta en desarrollo, puede buscar la WERKZEUG_RUN_MAINvariable de entorno y solo ejecutar su código cuando DEBUGes falso o WERKZEUG_RUN_MAINestá configurado, por ejemplo. Se vuelve un poco tedioso.
Martijn Pieters
Solo para aclarar, pensé que "recargar la funcionalidad" significaba reactividad (que frustraría todo el propósito de usar dashpara mí). Para cualquier otro noobscomo yo, esto solo significa la funcionalidad en la que editar / guardar el archivo activa una actualización en vivo.
Hendy
12

Si está utilizando el flask runcomando moderno , no app.runse utiliza ninguna de las opciones para . Para deshabilitar el cargador por completo, pase --no-reload:

FLASK_DEBUG=1 flask run --no-reload

Además, __name__ == '__main__'nunca será cierto porque la aplicación no se ejecuta directamente. Use las mismas ideas de la respuesta de Martijn , excepto sin el __main__bloque.

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
davidismo
fuente
7

Tuve el mismo problema y lo resolví estableciendo app.debugen False. Establecerlo en Trueestaba causando que me __name__ == "__main__"llamaran dos veces.

Carvell Wakeman
fuente
Mi __main__todavía corre dos veces con ambos app.debug = Falsey app.run_server(debug=False). ¿Está seguro de que lo hizo por usted o podría publicar algún código reproducible para probar?
Hendy
Cambiar app.debug fue todo lo que hice para resolverlo por mí. ¿Puede confirmar que main solo se está ejecutando dos veces cuando se inicia el servidor de matraces? Intente ejecutar un ejemplo de trabajo mínimo y vea si ocurre el problema. También intente ejecutar un ejemplo mínimo que falla en varias versiones de Python, que puede haber sido un problema. Desde entonces, he migrado mi proyecto a Java y SparkJava en lugar de python y flask, por lo que no recuerdo exactamente qué solucionó el problema.
Carvell Wakeman
Estoy usando flaskvia plotly dash, y descubrí que recientemente cambiaron el debug argumento predeterminado que se pasó a flask. Voy a suponer que me equivoqué anteriormente y tal vez lo hice app.debug=False(lo que tal vez esté anulado por los argumentos predeterminados run_server), o solo lo intenté sin pasar True, sin configurar explícitamente como se muestra arriba. Esto está funcionando correctamente para mí ahora (asegurándome de eso debug=False). ¡Gracias!
Hendy
2

Desde Flask 0.11, se recomienda ejecutar su aplicación con en flask runlugar de python application.py. El uso de este último podría resultar en ejecutar su código dos veces.

Como se indica aquí :

... a partir del frasco 0.11 se recomienda el método del frasco. La razón de esto es que debido a cómo funciona el mecanismo de recarga, hay algunos efectos secundarios extraños (como ejecutar cierto código dos veces ...)

salsa_man
fuente
0

Una de las posibles razones por las que la aplicación Flask se ejecuta sola dos veces es una configuración de WEB_CONCURRENCYconfiguración en Heroku. Para configurarlo en uno, puede escribir en la consola heroku config:set WEB_CONCURRENCY=1

trojek
fuente
-1

Una observación sobre los hilos

Esto es particularmente molesto cuando su aplicación usa subprocesos, ya que se activarán dos veces al inicio. Por lo que probé, los singleton tampoco remedian esto (lo cual es sorprendente). Sin embargo, agregar un retraso inicial de unos segundos antes de que se inicie el hilo puede resolver el problema.

Si la aplicación se reinicia más rápido que antes de que finalice el período de demora, el hilo dado solo se genera una vez, después del reinicio.

pfabri
fuente
@Dowvoter: ¿te importaría explicar por qué?
pfabri
-1

Tuve el mismo problema. Lo resolví modificando mi main e insertando use_reloader = False en él. Si alguien está aquí buscando una solución para este problema, el siguiente código lo ayudará a comenzar, sin embargo, la funcionalidad de los cambios en el código que se detectan automáticamente y reinicia la aplicación no funcionará. Tendrá que detener y reiniciar manualmente su aplicación después de cada edición en el código.

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