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?

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.


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

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.


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 ...)


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


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


@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)
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.