Flask部分源码分析
目录
1 前戏
1.1 werkzeug介绍
# Werkzeug是一个WSGI工具包。它可以作为一个 Web 框架的底层库,因为它封装好了很多 Web 框架的东西,例如 Request,Response 等等。
# run_simple函数演示
# demo
from werkzeug.wrappers import Request, Response
@Request.application
def hello(request):
return Response('Hello World!')
if __name__ == '__main__':
from werkzeug.serving import run_simple
run_simple('localhost', 4000, hello)
# 解释:
run_simple()启动werkzeug服务,监听指定端口,请求来了则执行hello视图函数(对hello加括号执行)
1.2 偏函数
# partial()
# 例子:
fun = partial(_lookup_req_object, "request")
# 即提前给函数_lookup_req_object传递了参数request,下次fun()就能直接调用函数,并且带有参数request
1.3threadLocal
# 查看文档《python之多线程下的threadLocal》
1.4代理模式
# 概念自己百度
# 应用:源码中的LocalProxy()
2 开始分析flask源码
2.1 启动flask项目
# 步骤1:
启动flask项目,运行app.run()
# 步骤2:查看run(),及核心代码为:
def run():
try:
run_simple(t.cast(str, host), port, self, **options)
# 分析:
1.self在这里是flask的对象,即app
2.这一步相当于执行了app(),对象加括号调用相当于执行了类中的__call__方法
# 步骤3:查看flask的__call__方法
def __call__(self, environ: dict, start_response: t.Callable) -> t.Any:
return self.wsgi_app(environ, start_response)
# 步骤4:查看wsgi_app()
def wsgi_app(self, environ: dict, start_response: t.Callable) -> t.Any:
ctx = self.request_context(environ) # 产生RequestContext对象,看2.2
error: t.Optional[BaseException] = None
try:
try:
ctx.push() # 将ctx对象保存到local(),看2.3
response = self.full_dispatch_request() # 执行视图函数,返回response对象,看2.5
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) # 最后,pop删除ctx
2.2 产生RequestContext对象
# ctx就是RequestContext对象,其中包含request对象,session和flash
# 步骤1:查看request_context()
def request_context(self, environ: dict) -> RequestContext:
return RequestContext(self, environ)
# 步骤2:查看RequestContext()
class RequestContext:
def __init__():
self.app = app
self.request = request
self.flashes = None
self.session = session
# 分析:
ctx就等于类实例化得到RequestContext对象
2.3 将ctx保存到local()
# 将ctx对象保存到local(),以后访问request对象,就从local的ctx中拿,这样request才能保证线程安全。
# 步骤1:查看push(),核心代码:
def push(self) -> None:
_request_ctx_stack.push(self)
# 步骤2:查看_request_ctx_stack.push(self)
def push(self, obj: t.Any) -> t.List[t.Any]:
rv = getattr(self._local, "stack", []).copy()
rv.append(obj)
self._local.stack = rv
return rv # type: ignore
# 分析:
1._request_ctx_stack是LocalStack()类的对象
2.这里的self就是ctx
3.push的功能:将ctx添加到Local()中。local = {"stack":[ctx]}
2.4 视图函数中访问request
# 不同线程访问request不会乱,因为每次的request都是从Local()中获取的# 步骤1: 全局的request,实际上是LocalProxy()的对象,执行的是类的__str__方法。 request = LocalProxy(partial(_lookup_req_object, "request")) # 步骤2:最终会执行偏函数partial(_lookup_req_object, "request") def _lookup_req_object(name): top = _request_ctx_stack.top if top is None: raise RuntimeError(_request_ctx_err_msg) return getattr(top, name)# 分析: 1.name就是request(字符串) 2.top就是ctx,反射出request对象 # 步骤3:查看_request_ctx_stack.top @property def top(self) -> t.Any: try: return self._local.stack[-1] except (AttributeError, IndexError): return None# 分析: 1._request_ctx_stack是LocalStack()的对象,含有属性self._local = Local() 2.top是方法伪装成属性,返回Local().stack列表中的ctx
2.5 执行视图函数
# response = self.full_dispatch_request() 这一步开始执行视图函数# 步骤1:查看self.full_dispatch_request() def full_dispatch_request(self) -> Response: # 执行请求扩展before_first_request self.try_trigger_before_first_request_functions() try: request_started.send(self) # 发送信号 rv = self.preprocess_request() # 执行请求扩展before_request if rv is None: # 判断请求扩展中是否有返回值 rv = self.dispatch_request() except Exception as e: rv = self.handle_user_exception(e) return self.finalize_request(rv) # 真正的执行视图函数 # 步骤2:查看self.finalize_request(rv) def finalize_request(): response = self.make_response(rv) try: response = self.process_response(response) # 产生responsed对象,保存session request_finished.send(self, response=response) # 发送信号 except Exception: if not from_error_handler: raise self.logger.exception( "Request finalizing failed with an error while handling an error" ) return response