Flask上下文管理机制流程(源码剖析)
Flask请求上下文管理
1 偏函数
partial 使用该方式可以生成一个新函数
from functools import partial def mod( n, m ):
return n % m mod_by_100 = partial( mod, 100 ) # 100传给n print mod( 100, 7 ) # 2
print mod_by_100( 7 ) # 2
2 线程安全
import time
from threading import local
class Foo(local):
# 继承local,保证线程安全,也保证了处理速度,threading.local()这个方法的特点用来保存一个全局变量,但是这个全局变量只有在当前线程才能访问,
num = 0
foo = Foo()
def add(i):
foo.num =i
time.sleep(0.5)
print(foo.num)
from threading import Thread
for i in range(20):
task = Thread(target=add,args=(i,))
task.start()
3 请求上下文
3.1 Flask请求上文
- 当请求进来时,app(), Flask实例化对象app执行__call__
def __call__(self, environ, start_response):
"""The WSGI server calls the Flask application object as the
WSGI application. This calls :meth:`wsgi_app` which can be
wrapped to applying middleware."""
return self.wsgi_app(environ, start_response)
执行wsgi_app 得到 一个 RequestContext的对象 ctx (封装了request以及session)
ctx = self.request_context(environ)
class RequestContext(object):
#此时的self是RequestContext对象 -->ctx 中封装了request/session
def __init__(self, app, environ, request=None):
self.app = app #app = Flask对象
if request is None:
#请求的原始信息通过request_class后此时request已经存在,request.methods等
request = app.request_class(environ)
self.request = request
self.url_adapter = app.create_url_adapter(self.request)
self.flashes = None
self.session = None
ctx 执行 ctx.push():
ctx = self.request_context(environ)
error = None
try:
try:
ctx.push()
response = self.full_dispatch_request()
except Exception as e:
error = e
response = self.handle_exception(e)
RequestContext对象的push方法
def push(self):
# _request_ctx_stack = LocalStack()一个LocalStack对象
# _request_ctx_stack._local = LocalStack()._loacl = {"__storage__":{},"__ident_func__":get_ident}
top = _request_ctx_stack.top
#top =None
if top is not None and top.preserved:
top.pop(top._preserved_exc)- _ request_ctx_stack是一个LocalStack对象 ,LocalStack()._local是一个Local对象 即Local()
class LocalStack(object):
def __init__(self):
self._local = Local()
#self._loacl = {"__storage__":{},"__ident_func__":get_ident}
- _request_ctx_stack中top方法,返回None (想哭,但是没有眼泪,这个方法,琢磨了半个小时)
Local对象经过初始化得到的字典值
class Local(object):
#限定键槽,当前只能由两个属性值__storage__,__ident_func__
__slots__ = ('__storage__', '__ident_func__') def __init__(self):
object.__setattr__(self, '__storage__', {})
object.__setattr__(self, '__ident_func__', get_ident) # {"__storage__":{},"__ident_func__":get_ident} #此时get_dient 是个没有执行的函数,内存地址
- _request_ctx_stack中top方法,返回None (第二次不会上当)
@property
def top(self):
"""The topmost item on the stack. If the stack is empty,
`None` is returned.
"""
try:
# self._local 即Local对象调用__getattr__方法
#在下文时候Local对象{"__storage__":{8080:{stack:rv=[ctx->request/session]}},"__ident_func__":get_ident}
# [ctx->request/session]
return self._local.stack[-1]
#得到ctx对象
except (AttributeError, IndexError):
return None
_request_ctx_stack 对象执行push方法
_request_ctx_stack.push(self) #当前的self为ctx
def push(self, obj):
#此时的self是LocalStack对象, obj为ctx
"""Pushes a new item to the stack"""
# self._local = {"__storage__":{},"__ident_func__":get_ident}
#找不到返回值是None
rv = getattr(self._local, 'stack', None)
if rv is None:
#由于.stack后面有等号,执行的时候Local()对象的__setattr__方法
#实际上是地址的赋值,此时stack和rv都指向空列表
self._local.stack = rv = []
#{"__storage__":{8080:{stack:rv=[]}},"__ident_func__":get_ident}
rv.append(obj)
# {"__storage__":{8080:{stack:rv=[ctx->request/session]}},"__ident_func__":get_ident}
# 应用上下文时候{"__storage__":{8080:{stack:rv=[app_ctx->(app/g)]}},"__ident_func__":get_ident}
return rv
#rv=[ctx->request/session]
def __setattr__(self, name, value):
#name=stack value=rv=[]
#self是Local对象 {"__storage__":{},"__ident_func__":get_ident}
ident = self.__ident_func__() #执行get_ident函数获取当前线程id 8080
storage = self.__storage__ #storge ={8080:{stack:rv=[]}}
try:
storage[ident][name] = value
except KeyError:
storage[ident] = {name: value} #storage={} # {"__storage__":{8080:{stack:rv=[]}},"__ident_func__":get_ident}
- 执行完push方法 请求上文结束:
#当请求进来,第一件事就是要把当前这个请求在我服务器上的线程开辟一个空间(线程对应的空间,必须含有stack对应一个列表存放ctx(request/session)
# {"__storage__":{8080:{stack:rv=[ctx->request/session]}},"__ident_func__":get_ident}
3.3.2Flask请求下文
导入request开始使用,在request中
#此时request是一个函数包裹一个偏函数 LocalProxy()是一个代理
#当前的request是一个LocalProxy() request.method 执行__getattr__方法
request = LocalProxy(
partial(_lookup_req_object, 'request') #return request对象
)
在偏函数中 将request传入到 _lookup_req_object中: 此时得到一个request对象
def _lookup_req_object(name):
# _request_ctx_stack是LocalStack对象
top = _request_ctx_stack.top
#下文[ctx->request/session]
if top is None:
raise RuntimeError(_request_ctx_err_msg)
#此时的name是request,从ctx对象中找出request对象
return getattr(top, name)
...
@property
def top(self):
"""The topmost item on the stack. If the stack is empty,
`None` is returned.
"""
try:
# self._local 即Local对象调用__getattr__方法
#在下文时候Local对象{"__storage__":{8080:{stack:rv=[ctx->request/session]}},"__ident_func__":get_ident}
# [ctx->request/session]
return self._local.stack[-1]
#得到ctx对象
except (AttributeError, IndexError):
return None
- 此时的top不是None已经存在值 (0.0)
partial(_lookup_req_object, 'request') 这一层执行完得到一个reauest对象,将偏函数传入到LocalProxy中
@implements_bool
class LocalProxy(object):
__slots__ = ('__local', '__dict__', '__name__', '__wrapped__') def __init__(self, local, name=None):
#local是request偏函数
object.__setattr__(self, '_LocalProxy__local', local) #__local = request偏函数
object.__setattr__(self, '__name__', name)
#当前偏函数可以执行而且判断loacl中是否有 __release_local__ ==>这句话成立
if callable(local) and not hasattr(local, '__release_local__'):
# "local" is a callable that is not an instance of Local or
# LocalManager: mark it as a wrapped function.
object.__setattr__(self, '__wrapped__', local) #__warpped__还是local偏函数
当前的request是一个LocalProxy() request.method 执行LocalProxy中的__getattr__方法
def __getattr__(self, name): # name是method(举例)
if name == '__members__':
return dir(self._get_current_object())
#此时self._get_current_object()是经过_local 执行后得到的request对象,从request对象中去取出method
return getattr(self._get_current_object(), name)
...
def _get_current_object(self):
#self._local是偏函数
if not hasattr(self.__local, '__release_local__'):
#执行偏函数,返回request对象
return self.__local()
try:
return getattr(self.__local, self.__name__)
except AttributeError:
raise RuntimeError('no object bound to %s' % self.__name__)
3.3.3小结
由此看来,falsk上下文管理可以分为三个阶段:
- 请求上文 ->
当请求进来,第一件事就是要把当前这个请求在服务器上的线程开辟一个空间(线程对应的空间,必须含有stack对应一个列表存放ctx(request/session),具体-->:将request,session封装在 RequestContext类中
app,g封装在AppContext类中,并通过LocalStack将requestcontext和appcontext放入Local类中
在local类中,以线程ID号作为key的字典, - 请求下文:
通过localproxy--->偏函数--->localstack--->local取值 - '请求响应时':-->要将上下文管理中的数据清除
先执行save.session()再各自执行pop(),将local中的数据清除
详细看源码
3.4 应用上下文
执行wsgi_app方法
#ctx为一个RequestContext的对象,参数为environ
ctx = self.request_context(environ)
error = None
try:
try:
ctx.push()
response = self.full_dispatch_request()
except Exception as e:
error = e
response = self.handle_exception(e)
执行push方法,_app_ctx_stack 同样是LocalStck对象,初始化时候top为None
def push(self):
app_ctx = _app_ctx_stack.top
#app_ctx = None
if app_ctx is None or app_ctx.app != self.app:
app_ctx = self.app.app_context() #app_context是AppContext对象 与RequestContenx一样,知识序列化出app和g
app_ctx.push()
# 应用上文时候{"__storage__":{8080:{stack:rv=[app_ctx->(app/g)]}},"__ident_func__":get_ident}
self._implicit_app_ctx_stack.append(app_ctx)
else:
self._implicit_app_ctx_stack.append(None)
- 执行app_ctx.push 进而 _app_ctx_stack.push
def push(self):
"""Binds the app context to the current context."""
self._refcnt += 1
if hasattr(sys, 'exc_clear'):
sys.exc_clear()
#将AppContext存在LocalStack对象中
_app_ctx_stack.push(self)
appcontext_pushed.send(self.app)
def push(self, obj):
#此时的self是LocalStack对象, obj为ctx
"""Pushes a new item to the stack"""
# self._local = {"__storage__":{},"__ident_func__":get_ident}
#找不到返回值是None
rv = getattr(self._local, 'stack', None)
if rv is None:
#由于.stack后面有等号,执行的时候Local()对象的__setattr__方法
#实际上是地址的赋值,此时stack和rv都指向改空列表
self._local.stack = rv = []
#{"__storage__":{8080:{stack:rv=[]}},"__ident_func__":get_ident}
rv.append(obj)
# {"__storage__":{8080:{stack:rv=[ctx->request/session]}},"__ident_func__":get_ident}
# 应用上下文时候{"__storage__":{8080:{stack:rv=[app_ctx->(app/g)]}},"__ident_func__":get_ident}
return rv
#rv=[ctx->request/session]
到此,push完毕,应用上文结束,应用下文在离线脚本时候使用,另外:在global.py中
def _find_app():
top = _app_ctx_stack.top #得到app_ctx(app / g)
if top is None:
raise RuntimeError(_app_ctx_err_msg)
return top.app #返回一个app即flask对象 只不过此时的flask对象 是公共的,与初始化的相同
# 但是是独立出来已经被配置好的Flask对象
# LocalStack是 针对当前这个线程对独立的Flask_app进行修改, 不影响现在运行的app =>离线脚本
#但是这个app 在请求结束后会从LocalStack中通过 __delattr__ 删除
# context locals
_request_ctx_stack = LocalStack() #LocalStark = self._loacl = {"__storage__":{},"__ident_func__":get_ident}
_app_ctx_stack = LocalStack()
current_app = LocalProxy(_find_app) # current_app可以点 .run | .route 等
Flask上下文管理机制流程(源码剖析)的更多相关文章
- Flask上下文管理机制
前引 在了解flask上下文管理机制之前,先来一波必知必会的知识点. 面向对象双下方法 首先,先来聊一聊面向对象中的一些特殊的双下划线方法,比如__call__.__getattr__系列.__get ...
- 【Cocos2d-x 3.x】内存管理机制与源码分析
侯捷先生说过这么一句话 : 源码之前,了无秘密. 要了解Cocos2d-x的内存管理机制,就得阅读源码. 接触Cocos2d-x时, Cocos2d-x的最新版本已经到了3.2的时代,在学习Coco ...
- Python内存管理机制-《源码解析》
Python内存管理机制 Python 内存管理分层架构 /* An object allocator for Python. Here is an introduction to the layer ...
- Struts2运行流程-源码剖析
本文为原创,如需转载,请标明出处:http://www.cnblogs.com/gudu1/p/7726172.html 首先说一下查看这些框架源码的感受,每一次深入探究 Spring.Struts ...
- Android的Context Manager(服务管理器)源码剖析-android学习之旅(99)
Context Manager介绍 Context Manager对应的进程是servicemanager进程,它先于Service Server和服务客户端运行,进入接收IPC数据的待机状态,处理来 ...
- flask你一定要知道的上下文管理机制
前引 在了解flask上下文管理机制之前,先来一波必知必会的知识点. 面向对象双下方法 首先,先来聊一聊面向对象中的一些特殊的双下划线方法,比如__call__.__getattr__系列.__get ...
- Flask的上下文管理机制
前引 在了解flask上下文管理机制之前,先来一波必知必会的知识点. 面向对象双下方法 首先,先来聊一聊面向对象中的一些特殊的双下划线方法,比如__call__.__getattr__系列.__get ...
- Redis源码剖析
Redis源码剖析和注释(一)---链表结构 Redis源码剖析和注释(二)--- 简单动态字符串 Redis源码剖析和注释(三)--- Redis 字典结构 Redis源码剖析和注释(四)--- 跳 ...
- Flask核心机制--上下文源码剖析
一.前言 了解过flask的python开发者想必都知道flask中核心机制莫过于上下文管理,当然学习flask如果不了解其中的处理流程,可能在很多问题上不能得到解决,当然我在写本篇文章之前也看到了很 ...
随机推荐
- 基于DP+位运算的RMQ算法
来源:http://blog.csdn.net/y990041769/article/details/38405063 RMQ算法,是一个快速求区间最值的离线算法,预处理时间复杂度O(n*log(n) ...
- codeforces E. Phone Talks(dp)
题目链接:http://codeforces.com/contest/158/problem/E 题意:给出一些电话,有打进来的时间和持续的时间,如果人在打电话,那么新打进来的电话入队,如果人没有打电 ...
- win、mac 设置 php上传文件大小限制
修改php.ini win平台WAMP修改 步骤 左键点击wamp 选择php 在弹出的窗口中选择php.ini 在打开的文件中进行修改(修改步骤如下) 修改完毕,保存并重启wamp mac MAM ...
- 每天学会一点点(JAVA基础)
1.什么是Java虚拟机?为什么Java被称作是“平台无关的编程语言”? 虚拟机是一个可以执行Java字节码的虚拟机进程.Java源文件被编译成能被Java虚拟机执行的字节码文件. Java被设计成允 ...
- (转载)非常完善的Log4net配置详细说明
一.前言 在项目中,对项目的日志收集是非常重要的,这里我就说说代码的异常日志收集.收集异常日志,有很多第三方成熟的框架,我这里就介绍一下我常用的Log4net. Log4Net介绍 Log4net 是 ...
- PythonI/O进阶学习笔记_5.python的set和dict
前言: 我一直觉得对我来说学习知识很忌讳不系统.本篇内容与上一篇 自定义序列类是有联系的. 上一篇比较通范的了解了序列类的一些协议和特性,并且有些list的内容.这篇更加具体到set和dict这两个序 ...
- jquery插件之poshytip
Poshy Tip 是一个强大的jQuery 工具提示插件,拥有不同的外观.作为 Form Tooltips使用时,可以自定义气泡出现的位置. 导入插件: <script type=" ...
- 排序入门练习题3 谁考了第k名 题解
题目出处:<信息学奥赛一本通>第二章 上机练习1 题目描述 在一次考试中,每个学生的成绩都不相同,现知道了每个学生的学号和成绩,求考第k名的学生的学号和成绩. 输入格式 输入的第一行包含两 ...
- 【学习笔记】python3核心技术与实践--开篇词
python的应用和流行程度: Python 可以运用在数据处理.Web 开发.人工智能等多个领域,它的语言简洁.开发效率高.可移植性强,并且可以和其他编程语言(比如 C++)轻松无缝衔接.现如今,不 ...
- 初学FPGA图像处理,开发板选择建议
我用的是ZYNQ7010的开发板,纯粹是入门学习,对于初学者,使用较多的xlinx入门级的开发板一般是zynq7000系列,淘宝上买的较好的是黑金和米联科,我买的就是黑金的,个人觉得教程很少,学习资料 ...