flask源码剖析
这段时间想重新写个自己的博客系统,又正好在看一些框架源码,然后就想要不顺便写个小框架吧,既然想写框架,要不再顺便写个orm吧,再写个小的异步Server吧。。事实证明饭要一口一口吃
先梳理一下flask工作的整个流程吧。
首先flask是符合wsgi协议的,那么也就是说,flask要实现一个可以callable的object给web server。
那么什么是wsgi? 简而言之,wsgi就是把框架和web Server协同工作的一个协议,规范。流程大概是:当有请求来的时候, web Server调用一个callable object(函数,类,实现__call__的实例等等), 传入(environ, start_response), 其中environ是一个字典,包含了请求相关的所有信息,比如请求路径,参数;start_response是一个函数。在这个callable object中会调用start_response,返回http状态码,headers等信息给web server, callable object本身则会返回具体的内容给web server, 返回的object必须是可迭代的。当有callable object本身返回的内容时候, 就会把start_response返回的内容和callable object返回的内容写入缓冲区并且刷新。
那么flask 给web server的callable object是什么?
Class Flask:
...
def __call__(self, environ, start_response):
"""Shortcut for :attr:`wsgi_app`."""
return self.wsgi_app(environ, start_response)
可以看到,是一个实现了__call__的实例,web server 调用Flask的实例,传入environ和start_response,那么这个self.wsgi_app是什么东西?
def wsgi_app(self, environ, start_response):
"""The actual WSGI application. This is not implemented in
`__call__` so that middlewares can be applied without losing a
reference to the class. So instead of doing this:: app = MyMiddleware(app) It's a better idea to do this instead:: app.wsgi_app = MyMiddleware(app.wsgi_app) Then you still have the original application object around and
can continue to call methods on it. .. versionchanged:: 0.7
The behavior of the before and after request callbacks was changed
under error conditions and a new callback was added that will
always execute at the end of the request, independent on if an
error ocurred or not. See :ref:`callbacks-and-errors`. :param environ: a WSGI environment
:param start_response: a callable accepting a status code,
a list of headers and an optional
exception context to start the response
"""
with self.request_context(environ):
try:
response = self.full_dispatch_request()
except Exception, e:
response = self.make_response(self.handle_exception(e))
return response(environ, start_response)
在文档里面第一句已经说明了“The actual WSGI application...”这个才是真正的callable object。那么这个真正的callable object会做什么事情?
首先,会打开self.request_context(envion),
class RequestContext(object): def __init__(self, app, environ):
self.app = app
self.request = app.request_class(environ)
self.url_adapter = app.create_url_adapter(self.request)
self.g = app.request_globals_class()
self.flashes = None
self.session = None self.preserved = False
self._after_request_functions = [] self.match_request() blueprint = self.request.blueprint
if blueprint is not None:
# better safe than sorry, we don't want to break code that
# already worked
bp = app.blueprints.get(blueprint)
if bp is not None and blueprint_is_module(bp):
self.request._is_old_module = True def match_request(self):
try:
url_rule, self.request.view_args = \
self.url_adapter.match(return_rule=True)
self.request.url_rule = url_rule
except HTTPException, e:
self.request.routing_exception = e def push(self):
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)
self.session = self.app.open_session(self.request)
if self.session is None:
self.session = self.app.make_null_session() def pop(self, exc=None):
app_ctx = self._implicit_app_ctx_stack.pop() clear_request = False
if not self._implicit_app_ctx_stack:
self.preserved = False
if exc is None:
exc = sys.exc_info()[1]
self.app.do_teardown_request(exc)
clear_request = True rv = _request_ctx_stack.pop()
assert rv is self, 'Popped wrong request context. (%r instead of %r)' \
% (rv, self) # 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) def __enter__(self):
self.push()
return self def __exit__(self, exc_type, exc_value, tb):
if self.request.environ.get('flask._preserve_context') or \
(tb is not None and self.app.preserve_context_on_exception):
self.preserved = True
else:
self.pop(exc_value)
从__enter__可以看到,首先会把当前请求推入栈,这个是什么栈?有什么用呢?
简而言之,这个栈就是flask根据每次请求时候,为每个请求所创建的线程/协程的id所创建的一个自己的threadlocal,在这个threadlocal中所实现的一个栈结构,会把不同的请求隔离开来,每次请求时候把当前请求所在的app,request推入栈,就可以在全局可用了。
把current_app,request推入栈之后,就根据请求的方法,路径等参数分发请求,找到用@app.route('...')注册的函数,生成response。
但是这里有个问题,就是根据wsgi协议,返回的结果应该是个可迭代的结果,而且这时候start_response还没执行,状态码和其他头部信息还都没有。这个就是make_response的事情了,
def full_dispatch_request(self):
self.try_trigger_before_first_request_functions()
try:
request_started.send(self)
rv = self.preprocess_request()
if rv is None:
rv = self.dispatch_request()
except Exception, e:
rv = self.handle_user_exception(e)
response = self.make_response(rv)
response = self.process_response(response)
request_finished.send(self, response=response)
return response
make_response会把返回结果包装一下。
def make_response(self, rv):
if isinstance(rv, basestring):
rv = self.response_class(rv, headers=headers, status=status)
headers = status = None
else:
rv = self.response_class.force_type(rv, request.environ) if status is not None:
if isinstance(status, basestring):
rv.status = status
else:
rv.status_code = status
if headers:
rv.headers.extend(headers) return rv
make_response会利用class Response(BaseResponse)把view function返回的结果包装一下,
class Response(ResponseBase):
default_mimetype = 'text/html' class BaseResponse(object):
automatically_set_content_length = True def __init__(self, response=None, status=None, headers=None,
.... @classmethod
def force_type(cls, response, environ=None):
... @classmethod
def from_app(cls, app, environ, buffered=False):
... def _get_status_code(self):
return self._status_code def __call__(self, environ, start_response): app_iter, status, headers = self.get_wsgi_response(environ)
start_response(status, headers)
return app_iter
当执行Response的实例时候,在__call__里面会执行start_response, 同时返回一个可迭代的对象
这就是flask的执行逻辑
参考资料:
PEP 333 -- Python Web Server Gateway Interface v1.0
flask源码剖析的更多相关文章
- flask源码剖析系列(系列目录)
flask源码剖析系列(系列目录) 01 flask源码剖析之werkzurg 了解wsgi 02 flask源码剖析之flask快速使用 03 flask源码剖析之threading.local和高 ...
- 08 Flask源码剖析之flask拓展点
08 Flask源码剖析之flask拓展点 1. 信号(源码) 信号,是在flask框架中为我们预留的钩子,让我们可以进行一些自定义操作. pip3 install blinker 2. 根据flas ...
- flask 源码剖析
flask 上下文管理源码流程及涉及的部分技术点 [flask源码梳理]之一 偏函数_mro [flask源码梳理]之二 面向对象中__setattr__ [flask源码梳理]之三 Local ...
- Flask源码剖析详解
1. 前言 本文将基于flask 0.1版本(git checkout 8605cc3)来分析flask的实现,试图理清flask中的一些概念,加深读者对flask的理解,提高对flask的认识.从而 ...
- 04 flask源码剖析之LocalStack和Local对象实现栈的管理
04 LocalStack和Local对象实现栈的管理 目录 04 LocalStack和Local对象实现栈的管理 1.源码入口 1. flask源码关于local的实现 2. flask源码关于l ...
- 05 flask源码剖析之配置加载
05 Flask源码之:配置加载 目录 05 Flask源码之:配置加载 1.加载配置文件 2.app.config源码分析 3.from_object源码分析 4. 总结 1.加载配置文件 from ...
- 06 flask源码剖析之路由加载
06 Flask源码之:路由加载 目录 06 Flask源码之:路由加载 1.示例代码 2.路由加载源码分析 1.示例代码 from flask import Flask app = Flask(__ ...
- 07 flask源码剖析之用户请求过来流程
07 Flask源码之:用户请求过来流程 目录 07 Flask源码之:用户请求过来流程 1.创建ctx = RequestContext对象 2. 创建app_ctx = AppContext对象 ...
- flask源码剖析--请求流程
想了解这篇里面的内容,请先去了解我另外一篇博客Flask上下文 在了解flask之前,我们需要了解两个小知识点 偏函数 import functools def func(a1,a2): print( ...
随机推荐
- FPN 学习笔记
通常,利用网络对物体进行检测时,浅层网络分辨率高,学到的是图片的细节特征,深层网络,分辨率低,学到的更多的是语义特征. 1).通常的CNN使用如下图中显示的网络,使用最后一层特征图进行预测 例如VGG ...
- 【转】CentOS 7.X 系统安装及优化
[转]CentOS 7.X 系统安装及优化 centos的演变 启动流程sysvinit 串行启动:一次一个,一个一个启动 并行启动:全部的一起启动 init优点 运行非常良好.主要依赖于shell脚 ...
- C++ 字符串的编码
转载链接:https://www.cnblogs.com/akb48/p/5439154.html windows平台 char 表示单字符,占用一个字节 wchar_t 表示宽字符,占用两个字节 L ...
- py-faster-rcnn + opencv3.0.0 + ubuntu16.04配置(CPU模式)
最近开始做行人检测,因此开始接触faster-rcnn,这里贴上配置教程(亲测可行),不过是基于cpu的,蓝瘦... 参考博客:http://www.tuicool.com/articles/nYJr ...
- 梯度优化算法总结以及solver及train.prototxt中相关参数解释
参考链接:http://sebastianruder.com/optimizing-gradient-descent/ 如果熟悉英文的话,强烈推荐阅读原文,毕竟翻译过程中因为个人理解有限,可能会有谬误 ...
- UML和模式应用5:细化阶段(6)---操作契约
1.前言 操作契约使用前置和后置条件,描述领域模型里对象的详细变化,作为系统操作的结果. 操作契约可以作为有用的OOA相关的制品. 操作契约可以视为UP用例模型的一部分,它是对用例之处的系统操作的效用 ...
- 对WinMain程序入口函数返回值为msg.wParam的几点理解
原文地址:http://blog.csdn.net/setflvet/article/details/6983224 1.在WinMain主函数中,最后的返回值是msg.wParam,这个参数是传递给 ...
- oracle 视图 参数
创建包: create or replace package p_view_param is function set_param(num number) return number; fu ...
- Expm 1_3 数组中逆序对个数问题
有一个数的序列A[1].A[2] .A[3] .…… .A[n],若i<j,并且A[i]>A[j],则称A[i]与A[j]构成了一个逆序对,设计算法求数列A中逆序对的个数. package ...
- bootgrid 刷新保持当前排序
1. 前言 主要是利用了HTHNL5的localStorage技术和用ajax传输一个数组到后台并进行判断.这篇文章是解决一个小需求而来的,主要是用来记录. 2. 代码 JavaScript: var ...