Mengapa flask CLI direkomendasikan daripada Flask.run?

13

Dalam Flask 0,11 flaskCLI diperkenalkan. Kedua dokumen dan negara changelog ini direkomendasikan.

Dokumen Server Pengembangan :

Dimulai dengan Flask 0.11 ada beberapa cara built-in untuk menjalankan server pengembangan. Yang terbaik adalah utilitas baris perintah labu tetapi Anda juga dapat terus menggunakan Flask.run()metode ini.

Garis komando

The termos baris perintah skrip (Command Line Interface) sangat disarankan untuk pengembangan karena memberikan pengalaman ulang unggul karena bagaimana itu beban aplikasi. Penggunaan dasar seperti ini:

$ export FLASK_APP=my_application
$ export FLASK_DEBUG=1
$ flask run

Changelog :

  • Ditambahkan flaskdan flask.climodul untuk memulai server debug lokal melalui sistem klik CLI. Ini direkomendasikan daripada flask.run()metode lama karena bekerja lebih cepat dan lebih dapat diandalkan karena desain yang berbeda dan juga diganti Flask-Script.

Sejauh ini saya tidak melihat "pengalaman reload superior" ini. Saya gagal melihat titik menggunakan CLI di atas skrip khusus.

Jika menggunakan Flask.run, saya cukup menulis file python:

#!/usr/bin/env python3
from my_app import app


if __name__ == '__main__':
    app.run(debug=True)

Jika menggunakan CLI, seseorang harus menentukan variabel lingkungan. Dalam dokumen CLI dinyatakan bahwa ini dapat diintegrasikan dalam activateskrip virtualenvwrapper. Secara pribadi saya menganggap ini sebagai bagian dari aplikasi dan berpikir itu harus di bawah kontrol versi. Sayangnya, skrip shell diperlukan:

#!/usr/bin/env bash
export FLASK_APP=my_app:app
export FLASK_DEBUG=1

flask run

Tentu saja ini akan disertai dengan skrip kelelawar tambahan segera setelah pengguna Windows mulai berkolaborasi.

Juga opsi pertama memungkinkan pengaturan ditulis dengan Python sebelum memulai aplikasi yang sebenarnya.

Ini memungkinkan misalnya

  • untuk mengurai argumen baris perintah dengan Python
  • untuk mengatur logging sebelum menjalankan aplikasi

Mereka tampaknya mempromosikan bahwa mungkin untuk menambahkan perintah khusus. Saya gagal melihat mengapa ini lebih baik daripada menulis skrip Python sederhana, opsional diekspos melalui titik masuk.

Contoh mencatat hasil saat menggunakan logger yang dikonfigurasi menggunakan skrip run Python:

$ ./run.py 
   DEBUG 21:51:22 main.py:95) Configured logging
    INFO 21:51:22 _internal.py:87)  * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
    INFO 21:51:22 _internal.py:87)  * Restarting with inotify reloader
   DEBUG 21:51:22 main.py:95) Configured logging
 WARNING 21:51:22 _internal.py:87)  * Debugger is active!
    INFO 21:51:22 _internal.py:87)  * Debugger pin code: 263-225-431
   DEBUG 21:51:25 inotify_buffer.py:61) in-event <InotifyEvent: src_path=b'my_app/main.py', wd=272, mask=IN_MODIFY, cookie=0, name=b'main.py'>
   DEBUG 21:51:25 inotify_buffer.py:61) in-event <InotifyEvent: src_path=b'my_app/main.py', wd=272, mask=IN_MODIFY, cookie=0, name=b'main.py'>
    INFO 21:51:25 _internal.py:87)  * Detected change in 'my_app/main.py', reloading
    INFO 21:51:26 _internal.py:87)  * Restarting with inotify reloader
   DEBUG 21:51:26 main.py:95) Configured logging
 WARNING 21:51:26 _internal.py:87)  * Debugger is active!
    INFO 21:51:26 _internal.py:87)  * Debugger pin code: 263-225-431

Contoh logging output ketika menggunakan logger yang dikonfigurasi menggunakan CLI :, perhatikan bahwa root logger tidak dapat diatur cukup awal dalam proses.

$ ./run.sh 
 * Serving Flask app "appsemble.api.main:app"
 * Forcing debug mode on
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
 * Restarting with inotify reloader
   DEBUG 21:51:33 main.py:95) Configured logging
 * Debugger is active!
 * Debugger pin code: 187-758-498
   DEBUG 21:51:34 main.py:95) Configured logging
   DEBUG 21:51:37 inotify_buffer.py:61) in-event <InotifyEvent: src_path=b'my_app/main.py', wd=272, mask=IN_MODIFY, cookie=0, name=b'main.py'>
   DEBUG 21:51:37 inotify_buffer.py:61) in-event <InotifyEvent: src_path=b'my_app/main.py', wd=272, mask=IN_MODIFY, cookie=0, name=b'main.py'>
 * Detected change in 'my_app/main.py', reloading
    INFO 21:51:37 _internal.py:87)  * Detected change in 'my_app/main.py', reloading
 * Restarting with inotify reloader
    INFO 21:51:38 _internal.py:87)  * Restarting with inotify reloader
 * Debugger is active!
 * Debugger pin code: 187-758-498
   DEBUG 21:51:38 main.py:95) Configured logging

Pertanyaan saya sebenarnya adalah:

Mengapa labu CLI direkomendasikan Flask.run?

Remco Haszing
sumber

Jawaban:

11

Di server pengembangan dokumen, mereka menyatakan ada masalah dengan panggilan run () dan memuat ulang kode secara otomatis:

Ini berfungsi baik untuk kasus umum tetapi tidak bekerja dengan baik untuk pengembangan yang mengapa dari Flask 0,11 dan seterusnya metode labu dianjurkan. Alasan untuk ini adalah karena cara mekanisme reload bekerja ada beberapa efek samping yang aneh (seperti mengeksekusi kode tertentu dua kali, kadang-kadang crash tanpa pesan atau sekarat ketika sintaks atau kesalahan impor terjadi).

Mereka mengklaim CLI tidak menderita masalah ini.

Komit pertama yang tampaknya menyentuh masalah ini adalah ini: https://github.com/pallets/flask/commit/3bdb90f06b9d3167320180d4a5055dcd949bf72f

Dan di sana Armin Ronacher menulis:

Tidak disarankan untuk menggunakan fungsi ini untuk pengembangan dengan memuat ulang otomatis karena ini sangat tidak didukung. Sebaliknya, Anda harus menggunakan dukungan flaskskrip baris perintah runserver.

Seperti yang disebutkan oleh Aaron Hall, sepertinya penggunaan run () bisa bermasalah karena fakta bahwa semua objek yang merupakan contoh kelas yang didefinisikan dalam modul yang diganti tidak akan diperkuat kembali, dan setiap kali modul dimuat kembali, modul yang diimpornya juga tidak dimuat ulang.

Rincian tentang ini dapat ditemukan untuk Python 3 di: https://docs.python.org/3/library/importlib.html?highlight=importlib#module-importlib

Ini menyatakan:

Seperti dengan semua objek lain di Python objek lama hanya direklamasi setelah jumlah referensi turun menjadi nol.

Referensi lain ke objek lama (seperti nama di luar modul) tidak kembali untuk merujuk ke objek baru dan harus diperbarui di setiap ruang nama tempat mereka muncul jika diinginkan.

Ketika sebuah modul dimuat kembali, kamusnya (berisi variabel global modul) dipertahankan. Redefinisi nama akan menimpa definisi lama, jadi ini umumnya tidak menjadi masalah. Jika versi baru modul tidak mendefinisikan nama yang ditentukan oleh versi lama, definisi lama tetap ada.

Jadi, dengan menciptakan proses baru dan membunuh yang lama, Anda secara alami menghilangkan semua referensi yang sudah usang.

CLI Flask juga menggunakan modul 'klik', membuatnya sangat mudah untuk menambahkan perintah kustom, tetapi yang paling penting, selain memperbaiki bug reload, CLI menawarkan cara standar untuk menjalankan aplikasi dan menambahkan perintah kustom. Ini kedengarannya seperti hal yang sangat baik, karena membuat Flask lebih terbiasa dengan tim dan aplikasi yang berbeda, daripada memiliki banyak cara untuk melakukan hal yang sama.

Sepertinya ini adalah cara asli untuk membuat Flask lebih sesuai dengan Zen Python:

Harus ada satu - dan lebih disukai hanya satu - cara yang jelas untuk melakukannya.

Martin Jungblut Schreiner
sumber
2
Inilah yang saya pikir Armin maksudkan dengan "sangat tidak didukung": Dengan Python, memuat ulang modul tidak memuat ulang modul yang diimpor modul tersebut, juga tidak menghubungkan kembali nama dalam modul lain dari menunjuk ke objek lama ke yang baru ke yang baru dari modul baru - begitu panas menukar modul baru ke dalam proses yang sama bermasalah. Anda jauh lebih baik memulai proses baru ketika Anda ingin membuat perubahan kode.
Aaron Hall
Sekarang setelah Anda menyebutkannya, saya ingat perilaku yang Anda jelaskan, terima kasih atas klarifikasi! Saya akan mengedit jawaban yang sesuai.
Martin Jungblut Schreiner
ok, tambah 1 karena mengutip saya. :)
Aaron Hall
Tambahan dari Aaron Hall mengklarifikasi untuk saya. Terima kasih. :)
Remco Haszing
7
Mengapa kita harus menggunakan variabel lingkungan FLASK_APP? Apakah itu intrinsik dengan cara kerjanya? Saya ingin tahu mengapa flask runtidak menerima yang sama dengan argumen, yang akan membuat pendatang baru yang lebih mudah. Terima kasih.
John Wheeler