# 应用上下文
在请求上下文的时候,我们将所有请求数据封装在ctx中,而下面我们将介绍应用上下文,依旧针对wsgi_app这个函数进行介绍
# 打开 wsgi_app 函数的源码
def wsgi_app(self, environ, start_response): ctx = self.request_context(environ) #请求上下进行封装,访问所有的封装的请求的属性 error = None try: try: ctx.push() #将ctx添加到请求数据中,除此之外还有添加应用app,以及session response = self.full_dispatch_request() except Exception as e: error = e response = self.handle_exception(e) except: # noqa: B001 error = sys.exc_info()[1] raise return response(environ, start_response) finally: if self.should_ignore_error(error): error = None ctx.auto_pop(error)
# 打开
ctx.push
函数源码def push(self): top = _request_ctx_stack.top # 通过self,在top属性中取出stack[-1],前提是有,我们就可以向取出来的ctx对象添加新属性,使用push if top is not None and top.preserved: # 看是否保留原有的属性 top.pop(top._preserved_exc) app_ctx = _app_ctx_stack.top if app_ctx is None or app_ctx.app != self.app: app_ctx = self.app.app_context() #创建AppContext(self)对象 app_ctx.push() #将得到的app_ctx对象添加到全局的 self._implicit_app_ctx_stack.append(app_ctx) else: self._implicit_app_ctx_stack.append(None) if hasattr(sys, "exc_clear"): sys.exc_clear() _request_ctx_stack.push(self) #这里进行的ctx的添加, . if self.session is None: session_interface = self.app.session_interface self.session = session_interface.open_session(self.app, self.request) if self.session is None: self.session = session_interface.make_null_session(self.app) if self.url_adapter is not None: self.match_request()
打开
app_context()
函数的源码1、该函数放回一个AppContext类对象,最后赋值给app_ctx return AppContext(self) #shlf就是app 2、打开AppContext类,执行初始化函数 def __init__(self, app): self.app = app #传入的self就是app self.url_adapter = app.create_url_adapter(None) self.g = app.app_ctx_globals_class() #去app中执行app_ctx_globals_class全局取数据,g就是一个空字典,可以通过它来传递参数 self._refcnt = 0 3、我们可以打开app_ctx_globals_class函数,里面返回了一个_AppCtxGlobals的类,其实就是一个字典的功能,
怎么将装饰器中的数据,传递给视图函数中?
from flask import request,Flask,session,g app = Flask('app01') # 不一定要传__name__ app.secret_key ='DSDSASD' @app.before_request def before(): request.a = 'xxxx' # 方式一:直接放在request中 session['x'] = 'sdfa' # 方式二: 放在session中 g.xxx= 'ok' #方式三:因为request中本来就有很多值了,可能会发生覆盖,所以可以使用g来携带参数 @app.route('/',methods=['GET']) def index(): print(request.a) print(session['x']) print(g.xxx) return 'index' if __name__ == '__main__': app.run()
app_ctx.push() 打开函数源码
1、使用app_push函数给 _app_ctx_stack 添加app对象 def push(self): #self是app_ctx """Binds the app context to the current context.""" self._refcnt += 1 if hasattr(sys, "exc_clear"): sys.exc_clear() _app_ctx_stack.push(self) #给全局_app_ctx_stack添加app_ctx appcontext_pushed.send(self.app) 2、点击_app_ctx_stack查看源码: _app_ctx_stack = LocalStack() #应用上下文,也就是说全局有两个Local _app_ctx_stack = LocalStack() 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")) 3、换句话说,ctx.push()执行之后会有两个local,大致的结构: _request_ctx_stack.__storage__ = { #请求上下文 唯一标识:{ 'stack':[ctx,] } } _app_ctx_stack.__storage__ = { #应用上下文 唯一标识:{ 'stack':[app_ctx,] } }
ctx.auto_pop(error)
将删除相应的ctx、app_ctx。
# 问题1:多线程是如何体现的?
当多个请求到来时,将自己的数据存储在,项目启动市创建的local数据中
# 问题2:为什么讲上下文管理?
1、通过请求进入,Flask会记录每个请求的携带数据,并以堆栈的方法保存在Local中,且有两个local类,分别保存request、app
2、关键:还记得上下文管理吗?
2.1:从所有的app.__call__中调用了wsgi_app函数:
def wsgi_app(self, environ, start_response):
ctx = self.request_context(environ) #在这里实例化了 RequestContext(self, environ) ,这里面会找到__enter__和__exit__函数,也就是说支持with app.request_context()
error = None
try:
try:
ctx.push()
response = self.full_dispatch_request()
except Exception as e:
error = e
response = self.handle_exception(e)
except: # noqa: B001
error = sys.exc_info()[1]
raise
return response(environ, start_response)
finally:
if self.should_ignore_error(error):
error = None
ctx.auto_pop(error)
2.2:同理,在ctx.push()中的app_ctx = self.app.app_context() 中的app_context函数返回的是一个
AppContext(self)类,里面也是实现了__enter__和__exit__函数,也就是说支持with app.app_context()
def __enter__(self):
self.push() #也就是说在app_context()、request_context()函数调用类的时候就会执行push函数
return self
def __exit__(self, exc_type, exc_value, tb):
self.pop(exc_value)
小提示:
from flask import Flask,_app_ctx_stack app = Flask('app01') with app.app_context(): print(_app_ctx_stack._local.__storage__) #{<greenlet.greenlet object at 0x000001D0CC4E3B90>: {'stack': [<flask.ctx.AppContext object at 0x000001D0CB52DD30>]}}
# 多APP应用
使用run_simple 结合 DispatcherMiddleware 类实现
from flask import Flask from werkzeug.wsgi import DispatcherMiddleware from werkzeug.serving import run_simple app = Flask('app01') app2 = Flask('app02') #生成两个app @app.route('/index',methods=['GET']) def home(): return 'index' @app2.route('/index2',methods=['GET']) def index(): return 'index2' app3 = DispatcherMiddleware(app,{ '/sec':app2, # 做了 分发,必须要加'/sec/index2' 才能触发app2 }) if __name__ == '__main__': run_simple('localhost',9000,app3)
# 离线脚本
使用 with 实现上下文的用法
from flask import Flask,_app_ctx_stack,current_app app = Flask('app01') app.debug = True with app.app_context(): print(_app_ctx_stack._local.__storage__) #{<greenlet.greenlet object at 0x000001D0CC4E3B90>: {'stack': [<flask.ctx.AppContext object at 0x000001D0CB52DD30>]}} print(current_app.config['DEBUG']) #True print(current_app) #<Flask 'app01'>
实际使用
from flask import Flask,_app_ctx_stack,current_app app = Flask('app01') app.debug = True app2 = Flask('app2') app2.debug = True with app.app_context(): #执行了app的push print(_app_ctx_stack._local.__storage__) #{<greenlet.greenlet object at 0x000001D0CC4E3B90>: {'stack': [<flask.ctx.AppContext object at 0x000001D0CB52DD30>]}} print(current_app.config['DEBUG']) #True print(current_app) #<Flask 'app01'> with app2.app_context(): # 会执行app2的push #{<greenlet.greenlet object at 0x00000142342D3B90>: {'stack': [<flask.ctx.AppContext object at 0x00000142344704E0>, <flask.ctx.AppContext object at 0x0000014234470748>]}} print(_app_ctx_stack._local.__storage__) print(current_app) #<Flask 'app2'>,此时stack中有两个app
← 信号 内置session理解 →