Wann sollte Flask.g verwendet werden?

173

Ich habe gesehen, dass dies gin Flask 0.10 vom Anforderungskontext in den App-Kontext verschoben wird, was mich über die beabsichtigte Verwendung von verwirrt hat g.

Mein Verständnis (für Flask 0.9) ist das:

  • g lebt im Anforderungskontext, dh wird beim Start der Anforderungen neu erstellt und ist bis zum Ende verfügbar
  • gsoll als "Anfragetafel" verwendet werden, an der ich für die Dauer der Anforderung relevante Informationen einfügen kann (dh am Anfang der Anforderung ein Flag setzen und am Ende bearbeiten, möglicherweise von einem before_request/ after_requestPaar).
  • gKann und sollte zusätzlich zum Halten des Status auf Anforderungsebene für das Ressourcenmanagement verwendet werden, dh zum Halten von Datenbankverbindungen usw.

Welche dieser Sätze sind in Flask 0.10 nicht mehr wahr? Kann mich jemand auf eine Ressource verweisen, in der die Gründe für die Änderung besprochen werden? Was soll ich als "Request Blackboard" in Flask 0.10 verwenden - sollte ich meinen eigenen app- / erweiterungsspezifischen threadlokalen Proxy erstellen und in den Kontextstapel verschieben before_request? Was bringt das Ressourcenmanagement im Anwendungskontext, wenn meine Anwendung lange lebt (nicht wie eine Anforderung) und die Ressourcen daher niemals freigegeben werden?

Yaniv Aknin
quelle
Ich stimme zu, das ist eine ziemlich merkwürdige Änderung. Hoffentlich implementiert Mitsuhiko eine Art Anforderungskontextobjekt, das gin 0.10 ersetzt werden soll. Andernfalls klingt es so, als würde viel Code einige abwegige Fehler entwickeln.
Anorov
11
FWIW, Armin Ronacher (Autor von Flask) hat eine Fortsetzung von "Advanced Flask Patterns" veröffentlicht, die einige Beispielcodes zur Verwendung des neuen enthält flask.g. Speakerdeck.com/mitsuhiko/advanced-flask-patterns-1
Markus Unterwaditzer
1
Auch ein neuer Anforderungskontext impliziert einen neuen App-Kontext, sodass er bei normaler Verwendung
Ronny,

Antworten:

119

Advanced Flask Patterns , wie von Markus verlinkt , erklärt einige der Änderungen gin 0.10:

  • g lebt jetzt im Anwendungskontext.
  • Jede Anforderung überträgt einen neuen Anwendungskontext , wobei der alte gelöscht wird, sodass gweiterhin Flags pro Anforderung gesetzt werden können, ohne dass der Code geändert werden muss.
  • Der Anwendungskontext wird nach dem teardown_request Aufruf geöffnet . (Armins Präsentation erklärt dies, weil Dinge wie das Erstellen von DB-Verbindungen Aufgaben sind, die die Umgebung für die Anforderung einrichten und nicht in before_requestund behandelt werden sollten. after_request)
theY4Kman
quelle
Wenn im Quellcode, mit dem Sie verknüpft sind, app_ctx is None or app_ctx.app != self.appFalse angezeigt wird, scheint der alte Anwendungskontext wiederverwendet zu werden. Dies scheint nicht richtig zu sein, da der Anwendungskontext "nicht zwischen Anforderungen geteilt wird" ...
nalzok
2
Beziehen Sie sich auf das Schieben vonapp.app_context() ? Wenn ja, sollte beachtet werden, dass bei app_context()jedem Aufruf ein neuer Anwendungskontext instanziiert wird - ein Kontext wird nie wiederverwendet.
theY4Kman
1
Ja, das stimmt, aber wenn app_ctx is not None and app_ctx.app == self.app, wird die app_ctx = self.app.app_context()Zeile nicht ausgeführt. Nur self._implicit_app_ctx_stack.append(None)wird in diesem Fall ausgeführt.
Nalzok
1
Oh, sorry, ich habe falsch verstanden! In einer Produktionseinstellung wird nur eine Anforderung pro Thread (oder Greenlet) bedient. Nur einer RequestContextwird gedrückt, also wird nur einer AppContextgedrückt. Wenn der Debug-Modus aktiviert ist und eine Anforderung fehlschlägt, speichert Flask den Kontext , sodass er mit dem Debugger verwendet werden kann . Nonewird an das angehängt. _app_ctx_stackWenn die Anforderung abgerissen wird, weiß sie, dass das AppContextgerade noch nicht angezeigt wird. Dasselbe passiert mit dem Testclient, der den Kontext beibehält, damit er überprüft werden kann.
theY4Kman
Der Bereich von g ist also pro Anforderung (Thread) und behält den Wert in der nachfolgenden Anforderung nicht bei.
Variable
83

Als Ergänzung zu den Informationen in diesem Thread: Ich war auch ein bisschen verwirrt über das Verhalten von flask.g, aber einige schnelle Tests haben mir geholfen, dies zu klären. Folgendes habe ich ausprobiert:

from flask import Flask, g
app = Flask(__name__)

with app.app_context():
    print('in app context, before first request context')
    print('setting g.foo to abc')
    g.foo = 'abc'
    print('g.foo should be abc, is: {0}'.format(g.foo))

    with app.test_request_context():
        print('in first request context')
        print('g.foo should be abc, is: {0}'.format(g.foo))
        print('setting g.foo to xyz')
        g.foo = 'xyz'
        print('g.foo should be xyz, is: {0}'.format(g.foo))

    print('in app context, after first request context')
    print('g.foo should be abc, is: {0}'.format(g.foo))

    with app.test_request_context():
        print('in second request context')
        print('g.foo should be abc, is: {0}'.format(g.foo))
        print('setting g.foo to pqr')
        g.foo = 'pqr'
        print('g.foo should be pqr, is: {0}'.format(g.foo))

    print('in app context, after second request context')
    print('g.foo should be abc, is: {0}'.format(g.foo))

Und hier ist die Ausgabe, die es gibt:

in app context, before first request context
setting g.foo to abc
g.foo should be abc, is: abc  

in first request context
g.foo should be abc, is: abc
setting g.foo to xyz
g.foo should be xyz, is: xyz  

in app context, after first request context
g.foo should be abc, is: xyz  

in second request context
g.foo should be abc, is: xyz
setting g.foo to pqr
g.foo should be pqr, is: pqr  

in app context, after second request context
g.foo should be abc, is: pqr

Wie der Y4Kman oben sagte: "Jede Anfrage drückt einen neuen Anwendungskontext". Und wie in den Flask-Dokumenten angegeben, wird der Anwendungskontext "nicht zwischen Anforderungen geteilt". Was jetzt nicht explizit angegeben wurde (obwohl ich denke, dass dies aus diesen Aussagen hervorgeht), und was meine Tests deutlich zeigen, ist, dass Sie niemals explizit mehrere Anforderungskontexte erstellen sollten , die in einem Anwendungskontext verschachtelt sind, weil flask.g(und co) dies nicht tun. Es gibt keine Magie, durch die es in den zwei verschiedenen "Kontextebenen" funktioniert, wobei verschiedene Zustände unabhängig voneinander auf Anwendungs- und Anforderungsebene existieren.

Die Realität ist, dass "Anwendungskontext" möglicherweise ein ziemlich irreführender Name ist, da app.app_context() es sich um einen Kontext pro Anforderung handelt , der genau dem "Anforderungskontext" entspricht . Stellen Sie sich das als "Anforderungskontext-Lite" vor, der nur erforderlich ist, wenn Sie einige der Variablen benötigen, für die normalerweise ein Anforderungskontext erforderlich ist, Sie jedoch keinen Zugriff auf ein Anforderungsobjekt benötigen (z. B. wenn Sie Batch-DB-Vorgänge in a ausführen Shell-Skript). Wenn Sie versuchen, den Anwendungskontext auf mehr als einen Anforderungskontext zu erweitern, fragen Sie nach Problemen. Anstelle meines obigen Tests sollten Sie stattdessen Code wie diesen mit Flask's Kontexten schreiben:

from flask import Flask, g
app = Flask(__name__)

with app.app_context():
    print('in app context, before first request context')
    print('setting g.foo to abc')
    g.foo = 'abc'
    print('g.foo should be abc, is: {0}'.format(g.foo))

with app.test_request_context():
    print('in first request context')
    print('g.foo should be None, is: {0}'.format(g.get('foo')))
    print('setting g.foo to xyz')
    g.foo = 'xyz'
    print('g.foo should be xyz, is: {0}'.format(g.foo))

with app.test_request_context():
    print('in second request context')
    print('g.foo should be None, is: {0}'.format(g.get('foo')))
    print('setting g.foo to pqr')
    g.foo = 'pqr'
    print('g.foo should be pqr, is: {0}'.format(g.foo))

Welches wird die erwarteten Ergebnisse geben:

in app context, before first request context
setting g.foo to abc
g.foo should be abc, is: abc  

in first request context
g.foo should be None, is: None
setting g.foo to xyz
g.foo should be xyz, is: xyz  

in second request context
g.foo should be None, is: None
setting g.foo to pqr
g.foo should be pqr, is: pqr
Jaza
quelle
7
Aufgrund des letzten Absatzes positiv bewertet, sind die Kontexte von flask zunächst ziemlich verwirrend. Aus dem Namen ergibt sich das Gefühl, dass der Anforderungskontext pro Anforderung ist und dass der App-Kontext auch nach einer Anforderung vorhanden ist oder nicht von seiner Lebensdauer beeinflusst wird.
Simanacci