上下文的分类
1.Flask上下文分为application context和request context,即应用上下文和请求上下文,这两者都处于同一请求的局部中
上下文类的定义
/usr/lib/Python27/lib/site-packages/flask/ctx.py
class AppContext(object):
def __init__(self, app):
self.app = app
self.url_adapter = app.create_url_adapter(None)
self.g = app.app_ctx_globals_class()
# Like request context, app contexts can be pushed multiple times
# but there a basic "refcount" is enough to track them.
self._refcnt = 0
说明:AppContext类即为程序上下文,内部保存几个变量,app为当前程序实例,g用来保存每个请求中需要用到的请求内全局变量
/usr/lib/Python27/lib/site-packages/flask/ctx.py
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.flashes = None
self.session = None
# Request contexts can be pushed multiple times and interleaved with
# other request contexts. Now only if the last level is popped we
# get rid of them. Additionally if an application context is missing
# one is created implicitly so for each level we add this information
self._implicit_app_ctx_stack = []
说明:RequestContext即请求上下文,内部保存着几个变量,app为当前程序实例,request为请求对象,session为会话对象
上下文对象的作用域
1.Flask是多线程,线程有个叫ThreadLocal的类,用于实现线程隔离的类,而Werkzeug自己实现了它的线程隔离类,Werkzeug.local.Local,LocalStack就是Local实现的
/usr/lib/Python27/lib/site-packages/flask/globals.py
_request_ctx_stack = LocalStack()
_app_ctx_stack = LocalStack()
说明:LocalStack是Flask定义的线程隔离的栈(先进后出)存储对象,分别用来存储程序上下文和请求上下文,对于不同的线程,它们访问这两个对象看到的结果是不一样的,完全隔离,每个传给Flask对象的请求,都在不同的线程中处理,而且同一时刻每个线程只处理一个请求,所以对于每个请求来说,它们完全不用担心自己上下文中的数据被别的请求所修改
问题:Flask中的g,session,request,current_app是怎么做到同一个对象能在所有请求中使用而不会冲突哪?
current_app = LocalProxy(_find_app)
request = LocalProxy(partial(_lookup_req_object, 'request'))
session = LocalProxy(partial(_lookup_req_object, 'session'))
g = LocalProxy(partial(_lookup_app_object, 'g'))
说明:LocalProxy类的构造函数接受一个callable参数,如上传递一个偏函数,以g为例,当对g进行操作时,就会调用作为参数的偏函数,并发操作转换到偏函数返回的对象上
def _lookup_req_object(name):
top = _request_ctx_stack.top
if top is None:
raise RuntimeError('working outside of request context')
return getattr(top, name)
def _lookup_app_object(name):
top = _app_ctx_stack.top
if top is None:
raise RuntimeError('working outside of application context')
return getattr(top, name)
def _find_app():
top = _app_ctx_stack.top
if top is None:
raise RuntimeError('working outside of application context')
return top.app
说明:_app_ctx_stack和request_ctx_stack都是线程隔离的,所以流程是这样,访问g->从当前线程的程序上下文栈顶获取程序上下文->取出其中的g对象->进行操作,所以可以通过一个g对象让所有线程互不干扰的访问自己的g
上下文对象的推送
def push(self):
top = _request_ctx_stack.top
if top is not None and top.preserved:
top.pop(top._preserved_exc)
# Before we push the request context we have to ensure that there
# is an application context.
app_ctx = _app_ctx_stack.top
if app_ctx is None or app_ctx.app != self.app:
app_ctx = self.app.app_context()
app_ctx.push()
self._implicit_app_ctx_stack.append(app_ctx)
else:
self._implicit_app_ctx_stack.append(None)
_request_ctx_stack.push(self)
说明:构建Flask对象后并不会推送上下文,而在Flask对象调用run()作为WSGI应用启动后,每当有请求进入时,推送请求上下文之前,首先会检查栈顶如已存在应用上下文,如果存在,判断与请求上下文是否属于同一个应用,如果不存在就会推送一个当前应用的上下文,如果是单WSGI应用的程序,此判断毫无意义
def pop(self, exc=None):
app_ctx = self._implicit_app_ctx_stack.pop()
………………
………………
rv = _request_ctx_stack.pop()
# Get rid of the app as well if necessary.
if app_ctx is not None:
app_ctx.pop(exc)
说明:请求结束时,调用auto_pop函数,其中又调用自身pop函数,会把请求上下文和程序上下文都pop掉,所以可以得出结论,在单WSGI应用环境下,每个请求独立于线程上曾经的请求,独立与其它线上的请求
app_ctx = app.app_context()
try:
# 说明: 尝试从LocalStack获取当前线程对应的程序上下文current_app,发现为空抛出运行时错误
print current_app.name
except RuntimeError as e:
# 说明: 程序上下文没有压入LocalStack栈
print 'found errors: %s' % (e)
# 说明: 压入程序上下文到栈
app_ctx.push()
# 说明: 因为上面压入了栈所以可以访问到
print current_app.name
来源:oschina
链接:https://my.oschina.net/u/2612057/blog/693698