Por que a execução do servidor de desenvolvimento do Flask é executada duas vezes?

106

Estou usando o Flask para desenvolver um site e, durante o desenvolvimento, executo o flask usando o seguinte arquivo:

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

Quando eu inicio o servidor ou quando ele reinicia automaticamente porque os arquivos foram atualizados, ele sempre mostra a linha de impressão duas vezes:

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

Embora não seja realmente um problema (o resto funciona conforme o esperado), eu simplesmente me pergunto por que ele se comporta assim? Alguma ideia?

Kramer65
fonte

Respostas:

152

O recarregador Werkzeug gera um processo filho para que possa reiniciar esse processo sempre que seu código for alterado. Werkzeug é a biblioteca que fornece ao Flask o servidor de desenvolvimento quando você chama app.run().

Veja o restart_with_reloader()código da função ; seu script é executado novamente com subprocess.call().

Se você definir use_reloadercomo, Falseverá o comportamento desaparecer, mas também perderá a funcionalidade de recarregamento:

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

Você também pode desativar o recarregador ao usar o flask runcomando:

FLASK_DEBUG=1 flask run --no-reload

Você pode procurar a WERKZEUG_RUN_MAINvariável de ambiente se quiser detectar quando está no processo filho de recarregamento:

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

No entanto, se você precisa configurar globais de módulo, então você deve usar o @app.before_first_requestdecorador em uma função e fazer com que essa função configure tais globais. Ele será chamado apenas uma vez após cada recarga, quando a primeira solicitação chegar:

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

Leve em consideração que se você executar isso em um servidor WSGI em escala real que usa bifurcação ou novos subprocessos para lidar com solicitações, esses before_first_requestmanipuladores podem ser chamados para cada novo subprocesso.

Martijn Pieters
fonte
2
Ah ok. Obrigada pelo esclarecimento! Portanto, é considerado um comportamento normal? Pelo menos bom que não há nada de errado com meu código .. :)
kramer65
1
@ kramer65: é um comportamento totalmente normal e esperado. :-)
Martijn Pieters
1
Existe uma maneira prática de executar o código de inicialização lenta apenas uma vez, garantindo que ele também seja chamado ao ser executado sob wsgi (ou seja, não de app.run), mas não aguardando a primeira solicitação? Não quero que o primeiro pedido seja sobrecarregado com o custo de inicialização.
Kylotan
1
@Kylotan: você teria que inspecionar o ambiente; se você definir DEBUG apenas ao executar em desenvolvimento, poderá procurar a WERKZEUG_RUN_MAINvariável de ambiente e apenas executar seu código quando DEBUGfor falso ou WERKZEUG_RUN_MAINestiver definido, por exemplo. Fica um pouco entediante.
Martijn Pieters
Só para esclarecer, pensei que "recarregar a funcionalidade" significava reatividade (o que anularia todo o propósito de usar dashpara mim). Para qualquer outro noobscomo eu, isso significa apenas a funcionalidade em que editar / salvar o arquivo aciona uma atualização ao vivo.
Hendy de
12

Se você estiver usando o flask runcomando moderno , nenhuma das opções a app.runserá usada. Para desativar o recarregador completamente, passe --no-reload:

FLASK_DEBUG=1 flask run --no-reload

Além disso, __name__ == '__main__'nunca será verdade porque o aplicativo não é executado diretamente. Use as mesmas ideias da resposta de Martijn , mas sem o __main__bloqueio.

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
fonte
7

Eu tive o mesmo problema e resolvi definindo app.debugcomo False. Definir como Trueestava fazendo com que eu __name__ == "__main__"fosse chamado duas vezes.

Carvell Wakeman
fonte
Meu __main__still é executado duas vezes com ambos app.debug = Falsee app.run_server(debug=False). Tem certeza de que fez isso por você ou poderia postar algum código reproduzível para tentar?
Hendy de
Alterar app.debug foi tudo que fiz para resolver para mim. Você pode confirmar que main está rodando apenas duas vezes quando o servidor flask é iniciado? Tente executar um exemplo de funcionamento mínimo e veja se o problema ocorre. Além disso, tente executar um exemplo mínimo que falha em várias versões de python, que pode ter sido um problema. Desde então, migrei meu projeto para Java e SparkJava em vez de python e flask, então não me lembro exatamente o que corrigiu o problema.
Carvell Wakeman
Estou usando flaskvia plotly dashe descobri que eles alteraram recentemente odebug argumento padrão passado para flask. Vou supor que me enganei acima e talvez tenha feito app.debug=False(que talvez seja substituído pelos args padrão run_server), ou apenas tentei sem passar True, não definindo explicitamente como mostrado acima. Isso está funcionando corretamente para mim agora (certificando-se disso debug=False). Obrigado!
Hendy
2

No Flask 0.11, é recomendado executar seu aplicativo com, em flask runvez de python application.py. Usar o último pode resultar na execução de seu código duas vezes.

Conforme declarado aqui :

... do Flask 0.11 em diante, o método do frasco é recomendado. A razão para isso é que, devido à forma como o mecanismo de recarga funciona, existem alguns efeitos colaterais bizarros (como executar determinado código duas vezes ...)

salsa_man
fonte
0

Um dos possíveis motivos para o aplicativo Flask rodar sozinho duas vezes é uma configuração de WEB_CONCURRENCYconfiguração no Heroku. Para configurar em um, você pode escrever no console heroku config:set WEB_CONCURRENCY=1

trojek
fonte
-1

Uma observação sobre tópicos

Isso é particularmente irritante quando seu aplicativo usa threads, pois eles serão disparados duas vezes na inicialização. Pelo que eu tentei, os singletons também não corrigiram isso (o que é surpreendente). No entanto, adicionar um atraso inicial de alguns segundos antes do início do thread pode resolver o problema.

Se o aplicativo for reiniciado mais rápido do que antes de seu período de atraso terminar, o thread fornecido é gerado apenas uma vez, após a reinicialização.

pfabri
fonte
@Dowvoter: gostaria de explicar por quê?
pfabri
-1

Eu tive o mesmo problema. Eu resolvi isso modificando meu principal e inserindo use_reloader = False nele. Se alguém estiver aqui procurando uma solução alternativa para este problema, o código abaixo o ajudará a começar, entretanto, a funcionalidade de alterações no código detectadas automaticamente e reiniciar o aplicativo não funcionará. Você terá que parar e reiniciar manualmente seu aplicativo após cada edição no código.

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