Python 烧瓶的用途是什么;什么是上下文堆栈?
我使用请求/应用程序上下文已经有一段时间了,但我没有完全理解它是如何工作的,也没有完全理解它为什么要这样设计。当涉及到请求或应用程序上下文时,“堆栈”的用途是什么?这两个堆栈是分开的,还是都是一个堆栈的一部分?是将请求上下文推送到堆栈上,还是堆栈本身?我是否能够将多个上下文推/弹出到彼此的顶部?如果是,我为什么要这样做 对于所有的问题,我很抱歉,但在阅读了有关请求上下文和应用程序上下文的文档后,我仍然感到困惑。多个应用程序 应用程序上下文(及其用途)确实令人困惑,直到您意识到Flask可以有多个应用程序。想象一下,您希望一个WSGI Python解释器运行多个Flask应用程序的情况。我们这里说的不是蓝图,而是完全不同的烧瓶应用 您可以将其设置为类似于以下示例: 请注意,有两个完全不同的Flask应用程序被创建为“前端”和“后端”。换句话说,Python 烧瓶的用途是什么;什么是上下文堆栈?,python,flask,Python,Flask,我使用请求/应用程序上下文已经有一段时间了,但我没有完全理解它是如何工作的,也没有完全理解它为什么要这样设计。当涉及到请求或应用程序上下文时,“堆栈”的用途是什么?这两个堆栈是分开的,还是都是一个堆栈的一部分?是将请求上下文推送到堆栈上,还是堆栈本身?我是否能够将多个上下文推/弹出到彼此的顶部?如果是,我为什么要这样做 对于所有的问题,我很抱歉,但在阅读了有关请求上下文和应用程序上下文的文档后,我仍然感到困惑。多个应用程序 应用程序上下文(及其用途)确实令人困惑,直到您意识到Flask可以有多个
Flask(…)
应用程序构造函数被调用了两次,创建了Flask应用程序的两个实例
上下文
使用Flask时,通常会使用全局变量访问各种功能。例如,您可能有读取
from flask import request
然后,在查看期间,您可以使用request
访问当前请求的信息。显然,request
不是一个正常的全局变量;实际上,它是一种价值。换句话说,在幕后有一种神奇的说法:“当我调用request.path
时,从当前请求的request
对象中获取path
属性。”两个不同的请求将对request.path
产生不同的结果
事实上,即使使用多个线程运行Flask,Flask也足够智能,可以隔离请求对象。这样,两个线程(每个线程处理一个不同的请求)就可以同时调用request.path
,并获得各自请求的正确信息
# this would happen some time near the end of request processing
release(local) # aka local.__release_local__()
把它放在一起
因此,我们已经看到Flask可以在同一个解释器中处理多个应用程序,而且由于Flask允许您使用“上下文本地”全局变量,因此必须有某种机制来确定“当前”请求是什么(以便执行诸如request.path
)之类的操作)
把这些想法放在一起,Flask必须有某种方法来确定“当前”应用程序是什么,这也应该是有意义的
您可能还有类似于以下内容的代码:
from flask import url_for
与我们的请求
示例一样,函数的url\u具有依赖于当前环境的逻辑。然而,在这种情况下,很明显,逻辑在很大程度上取决于哪个应用程序被视为“当前”应用程序。在上面显示的前端/后端示例中,“前端”和“后端”应用程序都可能有一个“/登录”路由,因此('/login')
的url_应该返回不同的内容,这取决于视图是处理前端还是后端应用程序的请求
要回答你的问题。。。
当涉及到请求或请求时,“堆栈”的用途是什么
应用程序上下文
从请求上下文文档:
因为请求上下文是作为堆栈在内部维护的
可以多次推动和弹出。这非常便于实现
比如内部重定向
换句话说,即使在这些“当前”请求或“当前”应用程序堆栈上通常会有0或1项,也可能会有更多项
给出的示例是让请求返回“内部重定向”的结果。假设一个用户请求a,但您希望返回到用户B。在大多数情况下,您向用户发出重定向,并将用户指向资源B,这意味着用户将运行第二个请求以获取B。处理此问题的一种稍微不同的方法是执行内部重定向,这意味着在处理a时,Flask将向自己发出一个新的资源B请求,并将第二个请求的结果用作用户原始请求的结果
这两个堆栈是分开的,还是都是一个堆栈的一部分
是的。然而,这是一个实现细节。更重要的不是有一个堆栈,而是在任何时候都可以获得“当前”应用程序或请求(堆栈顶部)
是将请求上下文推送到堆栈上,还是堆栈本身
“请求上下文”是“请求上下文堆栈”的一项。与“应用程序上下文”和“应用程序上下文堆栈”类似
我是否能够将多个上下文推/弹出到彼此的顶部?如果是,,
我为什么要这么做
在烧瓶应用程序中,通常不会这样做。您可能希望的一个示例是内部重定向(如上所述)。然而,即使在这种情况下,您也可能最终让Flask处理一个新请求,因此Flask将为您完成所有推送/弹出操作
但是,在某些情况下,您可能希望自己操作堆栈
在请求之外运行代码
人们面临的一个典型问题是,他们使用Flask SQLAlchemy扩展来设置SQL数据库和模型定义,代码如下所示
app = Flask(__name__)
db = SQLAlchemy() # Initialize the Flask-SQLAlchemy extension object
db.init_app(app)
然后他们在脚本中使用app
和db
值,该脚本应该从shell运行。例如,“setup_tables.py”脚本
from myapp import app, db
# Set up models
db.create_all()
在这种情况下,Flask SQLAlchemy扩展知道app
应用程序,但在create_all()
期间,它会抛出一个错误,抱怨没有应用程序
from myapp import app, db
# Set up models
with app.app_context():
db.create_all()
import unittest
from flask import request
class MyTest(unittest.TestCase):
def test_thing(self):
with app.test_request_context('/?next=http://example.com/') as ctx:
# You can now view attributes on request context stack by using `request`.
# Now the request context stack is empty
try:
from greenlet import get_ident
except ImportError:
from thread import get_ident
# globally
local = Local()
# ...
# on thread 1
local.first_name = 'John'
# ...
# on thread 2
local.first_name = 'Debbie'
class Local(object)
def __init__(self):
self.storage = {}
def __getattr__(self, name):
context_id = get_ident() # we get the current thread's or greenlet's id
contextual_storage = self.storage.setdefault(context_id, {})
try:
return contextual_storage[name]
except KeyError:
raise AttributeError(name)
def __setattr__(self, name, value):
context_id = get_ident()
contextual_storage = self.storage.setdefault(context_id, {})
contextual_storage[name] = value
def __release_local__(self):
context_id = get_ident()
self.storage.pop(context_id, None)
local = Local()
class LocalProxy(object):
def __init__(self, local, name):
# `local` here is either an actual `Local` object, that can be used
# to find the object of interest, here identified by `name`, or it's
# a callable that can resolve to that proxied object
self.local = local
# `name` is an identifier that will be passed to the local to find the
# object of interest.
self.name = name
def _get_current_object(self):
# if `self.local` is truly a `Local` it means that it implements
# the `__release_local__()` method which, as its name implies, is
# normally used to release the local. We simply look for it here
# to identify which is actually a Local and which is rather just
# a callable:
if hasattr(self.local, '__release_local__'):
try:
return getattr(self.local, self.name)
except AttributeError:
raise RuntimeError('no object bound to %s' % self.name)
# if self.local is not actually a Local it must be a callable that
# would resolve to the object of interest.
return self.local(self.name)
# Now for the LocalProxy to perform its intended duties i.e. proxying
# to an underlying object located somewhere in a Local, we turn all magic
# methods into proxies for the same methods in the object of interest.
@property
def __dict__(self):
try:
return self._get_current_object().__dict__
except RuntimeError:
raise AttributeError('__dict__')
def __repr__(self):
try:
return repr(self._get_current_object())
except RuntimeError:
return '<%s unbound>' % self.__class__.__name__
def __bool__(self):
try:
return bool(self._get_current_object())
except RuntimeError:
return False
# ... etc etc ...
def __getattr__(self, name):
if name == '__members__':
return dir(self._get_current_object())
return getattr(self._get_current_object(), name)
def __setitem__(self, key, value):
self._get_current_object()[key] = value
def __delitem__(self, key):
del self._get_current_object()[key]
# ... and so on ...
__setattr__ = lambda x, n, v: setattr(x._get_current_object(), n, v)
__delattr__ = lambda x, n: delattr(x._get_current_object(), n)
__str__ = lambda x: str(x._get_current_object())
__lt__ = lambda x, o: x._get_current_object() < o
__le__ = lambda x, o: x._get_current_object() <= o
__eq__ = lambda x, o: x._get_current_object() == o
# ... and so forth ...
# this would happen some time near application start-up
local = Local()
request = LocalProxy(local, 'request')
g = LocalProxy(local, 'g')
# this would happen early during processing of an http request
local.request = RequestContext(http_environment)
local.g = SomeGeneralPurposeContainer()
# this would happen some time near the end of request processing
release(local) # aka local.__release_local__()
class Local(object):
# ...
# ... all same stuff as before go here ...
# ...
def __call__(self, name):
return LocalProxy(self, name)
# now you can do
local = Local()
request = local('request')
g = local('g')
class LocalStack(object):
def __init__(self):
self.local = Local()
def push(self, obj):
"""Pushes a new item to the stack"""
rv = getattr(self.local, 'stack', None)
if rv is None:
self.local.stack = rv = []
rv.append(obj)
return rv
def pop(self):
"""Removes the topmost item from the stack, will return the
old value or `None` if the stack was already empty.
"""
stack = getattr(self.local, 'stack', None)
if stack is None:
return None
elif len(stack) == 1:
release_local(self.local) # this simply releases the local
return stack[-1]
else:
return stack.pop()
@property
def top(self):
"""The topmost item on the stack. If the stack is empty,
`None` is returned.
"""
try:
return self.local.stack[-1]
except (AttributeError, IndexError):
return None
_request_ctx_stack = LocalStack()
def _find_request():
top = _request_ctx_stack.top
if top is None:
raise RuntimeError('working outside of request context')
return top.request
request = LocalProxy(_find_request)
def _find_session():
top = _request_ctx_stack.top
if top is None:
raise RuntimeError('working outside of request context')
return top.session
session = LocalProxy(_find_session)
_app_ctx_stack = LocalStack()
def _find_g():
top = _app_ctx_stack.top
if top is None:
raise RuntimeError('working outside of application context')
return top.g
g = LocalProxy(_find_g)
def _find_app():
top = _app_ctx_stack.top
if top is None:
raise RuntimeError('working outside of application context')
return top.app
current_app = LocalProxy(_find_app)
app = Flask(*config, **kwconfig)
# ...
app(environ, start_response) # aka app.__call__(environ, start_response)
def Flask(object):
# ...
def __call__(self, environ, start_response):
return self.wsgi_app(environ, start_response)
def wsgi_app(self, environ, start_response):
ctx = RequestContext(self, environ)
ctx.push()
try:
# process the request here
# raise error if any
# return Response
finally:
ctx.pop()
# ...
class RequestContext(object):
def __init__(self, app, environ, request=None):
self.app = app
if request is None:
request = app.request_class(environ)
self.request = request
self.url_adapter = app.create_url_adapter(self.request)
self.session = self.app.open_session(self.request)
if self.session is None:
self.session = self.app.make_null_session()
self.flashes = None
def push(self):
_request_ctx_stack.push(self)
def pop(self):
_request_ctx_stack.pop()
class User(object):
def __init__(self):
self.userid = None
def get_user(_local):
try:
# get user object in current thread or greenlet
return _local.user
except AttributeError:
# if user object is not set in current thread ,set empty user object
_local.user = User()
return _local.user
usercontext = LocalProxy(partial(get_user, Local()))