from flask import Flask app = Flask(__name__) @app.route("/") def hello(): return "Hello World" if __name__ == "__main__": app.run()
当程序运行时,执行app.run(),会先执行Flask.__call__方法
def __call__(self, environ, start_response): """Shortcut for :attr:`wsgi_app`.""" return self.wsgi_app(environ, start_response)
call方法中调用Flask.wsgi_app方法
def wsgi_app(self, environ, start_response): ctx = self.request_context(environ) # 获取environ中的信息,封装在request和session中 ctx.push() # 放在Local中的__storage__字典中,字典格式为{"stack":[ctx, ]} 此处stack中存放的为列表形式==栈,详细信息查看文章《Flask多app应用之“栈”详解》 error = None try: try: response = self.full_dispatch_request() # 请求 except Exception as e: error = e response = self.handle_exception(e) # 报错 except: error = sys.exc_info()[1] raise return response(environ, start_response) # response finally: if self.should_ignore_error(error): error = None ctx.auto_pop(error) # 请求结束,删除__storage__中信息
深入ctx = request_context(environ)
def request_context(self, environ): return RequestContext(self, environ) # self为Flask对象=app,environ为请求信息
所以此处ctx为RequestContext对象!
接着我们来看RequestContext中都封装了些什么:
深入Request Context(self, environ)
源码可见,RequestContext中封装了我们用到的reqeust,session,so:request = ctx.request , session = ctx.reqeust
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 # request字段 self.url_adapter = app.create_url_adapter(self.request) self.flashes = None self.session = None # session字段 self._implicit_app_ctx_stack = [] self.preserved = False self._preserved_exc = None self._after_request_functions = [] self.match_request()
1.1深入ctx.push()
def push(self): # push方法为RuquestContext类的方法 top = _request_ctx_stack.top 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对象,应用上下文 app_ctx.push() 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) # 请求上下文, self.session = self.app.open_session(self.request) if self.session is None: self.session = self.app.make_null_session()
def app_context(self): return AppContext(self)
继续深入AppContext(),里面封装了一些参数
def __init__(self, app): self.app = app # app对象 self.url_adapter = app.create_url_adapter(None) self.g = app.app_ctx_globals_class() # g,记住里面有个一个g字段,后期我们会用到 self._refcnt = 0
下面继续我们的流程:
深入app_ctx.push()
def push(self): # push为AppContext()类的方法 self._refcnt += 1 if hasattr(sys, 'exc_clear'): sys.exc_clear() _app_ctx_stack.push(self) #我们需要深入的方法,self为app_ctx appcontext_pushed.send(self.app
深入_app_ctx_stack.push(self)
_app_ctx_stack = LocalStack()
_app_ctx_stack为:LocalStack的对象,这里为全局变量,下面我们还会用到,push为LocalStack的方法
我们先来看一下LocalStack的构造方法
def __init__(self): self._local = Local() # 这个Local为Flask上下文管理的核心技术,详细请看《Flask上下文源码分析之thredading.local拓展》
我么来看下LocalStack.push做了什么
def push(self, obj): rv = getattr(self._local, 'stack', None) if rv is None: self._local.stack = rv = [] # 相当于 rv = [] self._local.stack = rv,给rv赋值的同时,会给self._local.stack赋值 rv.append(obj) # self._local.stack = [obj,] return rv
self._local.stack 相当于执行了Local中的__setattr__方法
def __getattr__(self, name): try: return self.__storage__[self.__ident_func__()][name] # __storage__ = {"唯一标识":{"stack": [app_ctx]}} except KeyError: raise AttributeError(name)
现在我们开始使用以上提到的g
from flask import Flask,request,g app = Flask(__name__) @app.before_request def before(): g.permission_code_list = ['list','add'] # 设置参数 @app.route('/',methods=['GET',"POST"]) def index(): print(g.permission_code_list) # 获取参数 return "index" if __name__ == '__main__': app.run()
上面是一个小例子,我们先来看看这个g是什么,为什么就可以这样取到值
g = LocalProxy(partial(_lookup_app_object, 'g'))
g是LocalProxy类的对象,partial为偏函数,示例
from functools import partial def foo(name): print(name) partial(foo, 'i am pyton') # 执行foo,并把参数传进去
so:这个偏函数执行了_lokkup_app_object,参数为g,我们现在深入_locakup_app_object
def _lookup_app_object(name): top = _app_ctx_stack.top # 这个_app_ctx_stack又是LocalStack对象, if top is None: raise RuntimeError(_app_ctx_err_msg) return getattr(top, name) # getattr(app_ctx, "g")
深入top = _app_ctx_stack.top
def top(self): try: return self._local.stack[-1] # 这里相当于执行了Local中的__getattr__方法, 拿到__storage__["唯一id"]["stack"] = [app_ctx, ],[-1]取列表最后一个值 except (AttributeError, IndexError): return None
def __getattr__(self, name): try: return self.__storage__[self.__ident_func__()][name] #__storage__["唯一id"]["stack"] = [app_ctx, ] except KeyError: raise AttributeError(name)
通过源码分析到,top,就是app_ctx对象,app_ctx为AppContext对象,里面封装的参数我们上面有讲解
return getattr(top, name) # getattr(app_ctx, "g")
======================华丽的分割线======================
请求上下文开始>>>>>>>
接着1.1深入ctx.push()开始
深入_request_ctx_stack.push(self)
_request_ctx_stack同样为LocalStack对象,push为LocalStack类的方法,self为ctx,ctx中封装了reqeust,session等
push方法上面已经讲解过,最终的操作为,把ctx放在了Local中的字典__staorage__中,示例:
__staorage__ = { "唯一标识":{ ”stask“:[ctx] } }
request, session应用示例
from flask import g, Flask, request, session app = Flask(__name__) @app.route("/") def hello(): print(request.method) return "Hello Word"
request源码
request, session 同g一样,同样是LocalProxy对象,偏函数同样是获取ctx对象中的request,session
赋值执行LocalProxy的__setattr__,取值执行__getattr__
请求上下文结束>>>>>>>>>>>>>>>>
请求上下文流程之结尾
ctx.auto_pop(error)
深入auto_pop,auto_pop最终又调用了自己的pop方法
def auto_pop(self, exc): if self.request.environ.get('flask._preserve_context') or \ (exc is not None and self.app.preserve_context_on_exception): self.preserved = True self._preserved_exc = exc else: self.pop(exc) # exec
def pop(self, exc=_sentinel): app_ctx = self._implicit_app_ctx_stack.pop() try: clear_request = False if not self._implicit_app_ctx_stack: self.preserved = False self._preserved_exc = None if exc is _sentinel: exc = sys.exc_info()[1] self.app.do_teardown_request(exc) if hasattr(sys, 'exc_clear'): sys.exc_clear() request_close = getattr(self.request, 'close', None) if request_close is not None: request_close() clear_request = True finally: rv = _request_ctx_stack.pop() # 清空应用上下文 {"唯一id":{"task":[ctx]}},由于是字典类型,所以pop # get rid of circular dependencies at the end of the request # so that we don't require the GC to be active. if clear_request: rv.request.environ['werkzeug.request'] = None # Get rid of the app as well if necessary. if app_ctx is not None: app_ctx.pop(exc) # 清空应用上下文 {"唯一id":{"task":[app_ctx]}},由于是字典类型,所以pop assert rv is self, 'Popped wrong request context. ' \ '(%r instead of %r)' % (rv, self)
_request_ctx_stack 为LocalStack对象,LocalStack方法pop
def pop(self): stack = getattr(self._local, 'stack', None) if stack is None: return None elif len(stack) == 1: # 一次请求只有一个,所以一般情况下这里面只有一个 release_local(self._local) # 深入release_local return stack[-1] # 返回最后一个 else: return stack.pop() # 清空全部,下次stack变成None
release_local(self._local)最终执行了Local类中的方法
def __release_local__(self): self.__storage__.pop(self.__ident_func__(), None) # 清空当前请求相关的信息
over~