Warum wird Flask CLI über Flask.run empfohlen?

13

In Kolben 0.11 wurde eine flaskCLI eingeführt. Sowohl in den Dokumenten als auch im Änderungsprotokoll wird dies empfohlen.

Development Server-Dokumente :

Ab Flask 0.11 gibt es mehrere integrierte Möglichkeiten, um einen Entwicklungsserver auszuführen. Das beste ist das Kommandozeilen-Dienstprogramm flask. Sie können die Flask.run()Methode aber auch weiter verwenden .

Befehlszeile

Das Flask- Befehlszeilenskript (Command Line Interface) wird für die Entwicklung dringend empfohlen, da es aufgrund des Ladevorgangs der Anwendung ein hervorragendes Nachladeerlebnis bietet. Die grundlegende Verwendung ist wie folgt:

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

Änderungsprotokoll :

  • Hinzugefügt flaskund das flask.cliModul zum Starten des lokalen Debug-Servers über das Click-CLI-System. Dies wird gegenüber der alten flask.run()Methode empfohlen , da sie aufgrund eines anderen Designs schneller und zuverlässiger arbeitet und auch ersetzt Flask-Script.

Bisher habe ich dieses "überlegene Reload-Erlebnis" nicht bemerkt. Ich sehe keinen Sinn darin, die CLI über ein benutzerdefiniertes Skript zu verwenden.

Bei Verwendung Flask.runwürde ich einfach eine Python-Datei schreiben:

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


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

Bei Verwendung der CLI müssten Umgebungsvariablen angegeben werden. In den CLI-Dokumenten wird angegeben, dass dies in das activateSkript von virtualenvwrapper integriert werden kann. Persönlich betrachte ich dies als Teil der Anwendung und denke, es sollte unter Versionskontrolle sein. Leider wird ein Shell-Skript benötigt:

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

flask run

Dies wird natürlich von einem zusätzlichen Fledermausskript begleitet, sobald Windows-Benutzer anfangen, zusammenzuarbeiten.

Mit der ersten Option kann das Setup auch in Python geschrieben werden, bevor die eigentliche App gestartet wird.

Dies ermöglicht zum Beispiel

  • zum Parsen von Befehlszeilenargumenten in Python
  • zum Einrichten der Protokollierung vor dem Ausführen der App

Sie scheinen zu fördern, dass es möglich ist, benutzerdefinierte Befehle hinzuzufügen. Ich verstehe nicht, warum dies besser ist als das Schreiben einfacher Python-Skripte, die optional über Einstiegspunkte verfügbar gemacht werden.

Beispiel für die Protokollausgabe bei Verwendung eines konfigurierten Protokollierers mit dem Python-Ausführungsskript:

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

Beispiel für eine Protokollausgabe bei Verwendung eines konfigurierten Protokollierers mit der Befehlszeilenschnittstelle: Beachten Sie, dass der Stammprotokollierer nicht früh genug eingerichtet werden konnte.

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

Meine eigentliche Frage ist einfach:

Warum wird flask CLI empfohlen Flask.run?

Remco Haszing
quelle

Antworten:

11

In den Entwicklungsserverdokumenten wird angegeben, dass es Probleme beim Aufrufen von run () und beim automatischen Neuladen von Code gibt:

Dies funktioniert gut für den normalen Fall, aber nicht für die Entwicklung, weshalb ab Kolben 0.11 die Kolbenmethode empfohlen wird. Der Grund dafür ist, dass aufgrund der Funktionsweise des Reload-Mechanismus einige bizarre Nebenwirkungen auftreten (z. B. zweimaliges Ausführen von Code, manchmal Absturz ohne Meldung oder Absturz bei Auftreten eines Syntax- oder Importfehlers).

Sie behaupten, die CLI leide nicht unter diesem Problem.

Das erste Commit, das dieses Problem zu beheben scheint, lautet: https://github.com/pallets/flask/commit/3bdb90f06b9d3167320180d4a5055dcd949bf72f

Und dort schrieb Armin Ronacher:

Es wird nicht empfohlen, diese Funktion für die Entwicklung mit automatischem Neuladen zu verwenden, da dies schlecht unterstützt wird. Stattdessen sollten Sie flaskdie runserverUnterstützung des Befehlszeilenskripts verwenden .

Wie von Aaron Hall erwähnt, scheint die Verwendung von run () problematisch zu sein, da alle Objekte, bei denen es sich um Instanzen von Klassen handelt, die in den zu ersetzenden Modulen definiert sind, nicht erneut instanziiert werden Module, die importiert werden, werden ebenfalls nicht neu geladen.

Details dazu finden Sie in Python 3 unter: https://docs.python.org/3/library/importlib.html?highlight=importlib#module-importlib

Es sagt aus:

Wie bei allen anderen Objekten in Python werden die alten Objekte erst zurückgefordert, nachdem ihre Referenzanzahl auf Null gesunken ist.

Andere Verweise auf die alten Objekte (z. B. modulexterne Namen) werden nicht neu gebunden, um auf die neuen Objekte zu verweisen, und müssen in jedem Namespace aktualisiert werden, in dem sie auftreten, wenn dies gewünscht wird.

Beim erneuten Laden eines Moduls wird dessen Wörterbuch (das die globalen Variablen des Moduls enthält) beibehalten. Neudefinitionen von Namen überschreiben die alten Definitionen, daher ist dies im Allgemeinen kein Problem. Wenn die neue Version eines Moduls keinen Namen definiert, der von der alten Version definiert wurde, bleibt die alte Definition erhalten.

Wenn Sie also einen neuen Prozess erstellen und den alten beenden, eliminieren Sie natürlich alle veralteten Referenzen.

Die CLI von Flask verwendet außerdem das Click-Modul, mit dem benutzerdefinierte Befehle auf einfache Weise hinzugefügt werden können. Vor allem bietet die CLI jedoch nicht nur die Möglichkeit, den Fehler beim erneuten Laden zu beheben, sondern auch standardisierte Methoden zum Ausführen von Anwendungen und Hinzufügen benutzerdefinierter Befehle. Das klingt nach einer sehr guten Sache, da dadurch die Vertrautheit mit Flask besser zwischen verschiedenen Teams und Anwendungen übertragen werden kann, anstatt mehrere Möglichkeiten zu haben, dasselbe zu tun.

Es scheint eine echte Möglichkeit zu sein, Flask mehr nach dem Zen of Python zu formen:

Es sollte einen - und am besten nur einen - offensichtlichen Weg geben, dies zu tun.

Martin Jungblut Schreiner
quelle
2
Ich denke, Armin bedeutet "schlecht unterstützt": In Python werden durch das erneute Laden eines Moduls weder die von diesem Modul importierten Module erneut geladen, noch werden Namen in anderen Modulen neu zugeordnet, damit sie nicht auf alte Objekte verweisen, sondern auf neue aus dem neuen Modul. Das Austauschen eines neuen Moduls im laufenden Betrieb ist daher problematisch. Es ist weitaus besser, einen neuen Prozess zu starten, wenn Sie einen Code ändern möchten.
Aaron Hall
Nachdem Sie es erwähnt haben, erinnere ich mich an das von Ihnen beschriebene Verhalten. Vielen Dank für die Klarstellung! Ich werde die Antwort entsprechend bearbeiten.
Martin Jungblut Schreiner
ok, plus 1 für mich zu zitieren. :)
Aaron Hall
Der Zusatz von Aaron Hall hat es für mich geklärt. Vielen Dank. :)
Remco Haszing
7
Warum müssen wir die Umgebungsvariable verwenden FLASK_APP? Ist das wesentlich für die Funktionsweise? Ich bin neugierig, warum ich flask runnicht das Gleiche als Argument akzeptiere, was das Einbinden von Neulingen erleichtern würde. Vielen Dank.
John Wheeler