Mengapa menjalankan server dev Flask berjalan sendiri dua kali?

107

Saya menggunakan Flask untuk mengembangkan situs web dan saat dalam pengembangan saya menjalankan flask menggunakan file berikut:

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

Ketika saya memulai server, atau ketika restart otomatis karena file telah diperbarui, itu selalu menampilkan baris cetak dua kali:

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

Meskipun ini sebenarnya bukan masalah (sisanya berfungsi seperti yang diharapkan), saya hanya bertanya-tanya mengapa ini berperilaku seperti ini? Ada ide?

kramer65
sumber

Jawaban:

154

Pemuat ulang Werkzeug memunculkan proses anak sehingga dapat memulai ulang proses itu setiap kali kode Anda berubah. Werkzeug adalah pustaka yang memasok Flask dengan server pengembangan saat Anda menelepon app.run().

Lihat restart_with_reloader()kode fungsi ; skrip Anda dijalankan lagi dengan subprocess.call().

Jika Anda menyetel use_reloaderke FalseAnda akan melihat perilakunya hilang, tetapi kemudian Anda juga kehilangan fungsionalitas memuat ulang:

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

Anda juga dapat menonaktifkan reloader saat menggunakan flask runperintah:

FLASK_DEBUG=1 flask run --no-reload

Anda dapat mencari WERKZEUG_RUN_MAINvariabel lingkungan jika Anda ingin mendeteksi saat Anda dalam proses anak reload:

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

Namun, jika Anda perlu menyiapkan global modul, Anda harus menggunakan @app.before_first_requestdekorator pada sebuah fungsi dan mengatur fungsi tersebut secara global. Ini akan dipanggil hanya sekali setelah setiap reload ketika permintaan pertama masuk:

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

Perhatikan bahwa jika Anda menjalankan ini di server WSGI skala penuh yang menggunakan forking atau subproses baru untuk menangani permintaan, before_first_requestpenangan tersebut dapat dipanggil untuk setiap subproses baru.

Martijn Pieters
sumber
2
Ah ok. Terima kasih untuk penjelasannya! Jadi itu dianggap perilaku normal? Setidaknya bagus bahwa tidak ada yang salah dengan kode saya .. :)
kramer65
1
@ kramer65: itu sepenuhnya normal dan perilaku yang diharapkan. :-)
Martijn Pieters
1
Adakah cara praktis untuk menjalankan kode inisialisasi lambat hanya sekali, memastikan bahwa ia juga dipanggil saat berjalan di bawah wsgi (misalnya, bukan dari app.run), tetapi tidak menunggu permintaan pertama? Saya tidak ingin permintaan pertama itu dibebani dengan biaya inisialisasi.
Kylotan
1
@Kylotan: Anda harus memeriksa lingkungan; jika Anda hanya menyetel DEBUG saat berjalan dalam pengembangan, Anda dapat mencari WERKZEUG_RUN_MAINvariabel lingkungan dan hanya menjalankan kode Anda saat DEBUGsalah atau WERKZEUG_RUN_MAINdisetel, misalnya. Agak membosankan.
Martijn Pieters
Hanya untuk memperjelas, saya pikir "fungsi reload" berarti reaktivitas (yang akan mengalahkan seluruh tujuan penggunaan dashuntuk saya). Untuk orang lain noobsseperti saya, ini hanya berarti fungsionalitas di mana pengeditan / penyimpanan file memicu pembaruan langsung.
Hendy
12

Jika Anda menggunakan flask runperintah modern , tidak ada opsi yang app.runakan digunakan. Untuk menonaktifkan reloader sepenuhnya, berikan --no-reload:

FLASK_DEBUG=1 flask run --no-reload

Selain itu, __name__ == '__main__'tidak akan pernah benar karena aplikasi tidak dijalankan secara langsung. Gunakan ide yang sama dari jawaban Martijn , kecuali tanpa __main__blok.

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
sumber
8

Saya memiliki masalah yang sama, dan saya menyelesaikannya dengan menyetel app.debugke False. Mengaturnya ke Truemenyebabkan saya __name__ == "__main__"dipanggil dua kali.

Carvell Wakeman
sumber
Saya __main__masih berlari dua kali dengan app.debug = Falsedan app.run_server(debug=False). Apakah Anda yakin melakukannya untuk Anda, atau dapatkah Anda memposting beberapa kode yang dapat direproduksi untuk dicoba?
Hendy
Mengubah app.debug adalah semua yang saya lakukan untuk menyelesaikannya untuk saya. Dapatkah Anda memastikan bahwa main hanya berjalan dua kali ketika server flask dijalankan? Coba jalankan contoh kerja minimal dan lihat apakah masalahnya terjadi. Coba juga jalankan contoh minimal yang gagal di beberapa versi python, yang mungkin menjadi masalah. Saya telah memigrasikan proyek saya ke Java dan SparkJava, bukan python dan flask, jadi saya tidak ingat persis apa yang memperbaiki masalah ini.
Carvell Wakeman
Saya menggunakan flaskvia plotly dash, dan menemukan mereka baru-baru ini mengubah debug argumen default yang diteruskan ke flask. Saya akan menebak bahwa saya salah di atas dan mungkin melakukannya app.debug=False(yang mungkin diganti oleh argumen default ke run_server), atau hanya mencoba tanpa melewatkan True, tidak secara eksplisit mengatur seperti yang ditunjukkan di atas. Ini berfungsi dengan benar untuk saya sekarang (pastikan itu debug=False). Terima kasih!
Hendy
2

Dari Flask 0,11, dianjurkan untuk menjalankan aplikasi Anda dengan flask runbukan python application.py. Menggunakan yang terakhir dapat mengakibatkan menjalankan kode Anda dua kali.

Seperti yang dinyatakan di sini :

... dari Flask 0.11 dan seterusnya, metode flask direkomendasikan. Alasan untuk ini adalah karena cara kerja mekanisme reload ada beberapa efek samping yang aneh (seperti mengeksekusi kode tertentu dua kali ...)

salsa_man
sumber
0

Salah satu kemungkinan alasan mengapa aplikasi Flask berjalan sendiri dua kali adalah konfigurasi WEB_CONCURRENCYpengaturan di Heroku. Untuk mengatur menjadi satu, Anda dapat menulis di konsol heroku config:set WEB_CONCURRENCY=1

trojek
sumber
-1

Saya memiliki masalah yang sama. Saya menyelesaikannya dengan memodifikasi main saya dan memasukkan use_reloader = False ke dalamnya. Jika ada badan di sini yang mencari solusi untuk masalah ini, maka kode di bawah ini akan membantu Anda memulai, namun fungsionalitas perubahan dalam kode yang terdeteksi secara otomatis oleh dan memulai ulang aplikasi tidak akan berfungsi. Anda harus menghentikan dan memulai ulang aplikasi secara manual setelah setiap pengeditan kode.

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