flask请求上下文
先看一个例子:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import threading # local_values = threading.local() class Foo(object):
def __init__(self):
self.name = 0 local_values = Foo() def func(num):
local_values.name = num
import time
time.sleep(1)
print(local_values.name,threading.current_thread().name) for i in range(5):
th = threading.Thread(target=func, args=(i,), name='线程%s' % i)
th.start()
4 线程0
4 线程1
4 线程3
4 线程2
4 线程4
上述结果不是我们想要的,local_values.name的值被最后一个覆盖了.............................
flask的request和session设置方式比较新颖,如果没有这种方式,那么就只能通过参数的传递。
flask是如何做的呢?
1. 本地线程,保证即使是多个线程,自己的值也是互相隔离。
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import threading local_values = threading.local() def func(num):
local_values.name = num
import time
time.sleep(1)
print(local_values.name, threading.current_thread().name) for i in range(20):
th = threading.Thread(target=func, args=(i,), name='线程%s' % i)
th.start()
from _thread import get_ident
import threading def task(num):
print(get_ident()) for i in range(10):
th = threading.Thread(target=task,args=(i,),name='线程%s' % i)
th.start()
如上图所示,每个线程都有一个唯一标识
__setattr__、__getattr__的用法
class Foo(object):
def __init__(self):
pass def __setattr__(self, key, value):
print(key,value) def __getattr__(self, item):
print(item) obj = Foo() obj.xxx=123
obj.xxx
__setattr__ __getattr__
自定义Local
import threading
try:
from greenlet import getcurrent as get_ident # 协程
except ImportError:
try:
from thread import get_ident
except ImportError:
from _thread import get_ident # 线程 class Local(object): def __init__(self):
#递归执行__setattr__
# self.__storage__={}
# self.__ident_func__=get_ident
object.__setattr__(self, '__storage__', {})
object.__setattr__(self, '__ident_func__', get_ident) def __getattr__(self, name):
try:
return self.__storage__[self.__ident_func__()][name]
except KeyError:
raise AttributeError(name) def __setattr__(self, name, value):
ident = self.__ident_func__()
storage = self.__storage__
try:
storage[ident][name] = value
except KeyError:
storage[ident] = {name: value} def __delattr__(self, name):
try:
del self.__storage__[self.__ident_func__()][name]
except KeyError:
raise AttributeError(name) local_values = Local() def task(num):
local_values.name = num
import time
time.sleep(1)
print(local_values.name, threading.current_thread().name) for i in range(5):
th = threading.Thread(target=task, args=(i,),name='线程%s' % i)
th.start() 执行结果:
0 线程0
1 线程1
2 线程2
4 线程4
3 线程3
2. 上下文原理
#!/usr/bin/env python
# -*- coding:utf- -*-
from functools import partial
from flask.globals import LocalStack, LocalProxy ls = LocalStack() class RequestContext(object):
def __init__(self, environ):
self.request = environ def _lookup_req_object(name):
top = ls.top
if top is None:
raise RuntimeError(ls)
return getattr(top, name) session = LocalProxy(partial(_lookup_req_object, 'request')) ls.push(RequestContext('c1')) # 当请求进来时,放入
print(session) # 视图函数使用
print(session) # 视图函数使用
ls.pop() # 请求结束pop ls.push(RequestContext('c2'))
print(session) ls.push(RequestContext('c3'))
print(session)
3. Flask内部实现
#!/usr/bin/env python
# -*- coding:utf- -*- from greenlet import getcurrent as get_ident def release_local(local):
local.__release_local__() class Local(object):
__slots__ = ('__storage__', '__ident_func__') def __init__(self):
# self.__storage__ = {}
# self.__ident_func__ = get_ident
object.__setattr__(self, '__storage__', {})
object.__setattr__(self, '__ident_func__', get_ident) def __release_local__(self):
self.__storage__.pop(self.__ident_func__(), None) def __getattr__(self, name):
try:
return self.__storage__[self.__ident_func__()][name]
except KeyError:
raise AttributeError(name) def __setattr__(self, name, value):
ident = self.__ident_func__()
storage = self.__storage__
try:
storage[ident][name] = value
except KeyError:
storage[ident] = {name: value} def __delattr__(self, name):
try:
del self.__storage__[self.__ident_func__()][name]
except KeyError:
raise AttributeError(name) class LocalStack(object):
def __init__(self):
self._local = Local() def __release_local__(self):
self._local.__release_local__() def push(self, obj):
"""Pushes a new item to the stack"""
rv = getattr(self._local, 'stack', None)
if rv is None:
self._local.stack = rv = []
rv.append(obj)
return rv def pop(self):
"""Removes the topmost item from the stack, will return the
old value or `None` if the stack was already empty.
"""
stack = getattr(self._local, 'stack', None)
if stack is None:
return None
elif len(stack) == :
release_local(self._local)
return stack[-]
else:
return stack.pop() @property
def top(self):
"""The topmost item on the stack. If the stack is empty,
`None` is returned.
"""
try:
return self._local.stack[-]
except (AttributeError, IndexError):
return None stc = LocalStack() stc.push()
v = stc.pop() print(v)
------------------------------------------源码分析(重点):----------------------------------------------------------------------
首先看这段代码:
if __name__ == '__main__':
# 1.1
app.__call__
app.request_class
response_class = Response
app.run()
在上面提到了上下文的原理:
-----------------------LocalStak-----------------Local-----------------------------------
globals.py中有这么段代码,定义了request
# \flask\globals.py
# context locals
# 全局变量
_request_ctx_stack = LocalStack()
# _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'))
# Local类
class Local(object):
__slots__ = ('__storage__', '__ident_func__') def __init__(self):
object.__setattr__(self, '__storage__', {})
object.__setattr__(self, '__ident_func__', get_ident) def __getattr__(self, name):
try:
return self.__storage__[self.__ident_func__()][name]
except KeyError:
raise AttributeError(name) def __setattr__(self, name, value):
ident = self.__ident_func__()
storage = self.__storage__
"""
{
唯一标识(__ident_func__):{
stack:[ctx/requestcontext,]//栈
}
}
"""
try:
storage[ident][name] = value
except KeyError:
storage[ident] = {name: value}
# LocalStack类
def __init__(self):
self._local = Local() # LocalStack()就是我们之前写过的一个多线程数据相互隔离的类,每个线程都有一个唯一标识
# 每个线程都有一个独立的存储数据和访问数据的空间
# __storage__ __ident_func__ # \werkzeug\local.py
# Local类 每个线程唯一标识 可以存储数据
# LocalStack类 实例化Local类 # LocalStack类
def push(self, obj):
# 刚开始stack=[]
rv = getattr(self._local, 'stack', None)
if rv is None:
self._local.stack = rv = []
rv.append(obj)//执行Local的__setattr__方法
return rv def pop(self):
stack = getattr(self._local, 'stack', None)
if stack is None:
return None
elif len(stack) == 1:
release_local(self._local)
return stack[-1]
else:
return stack.pop()
以上都是启动会加载的文件 ,创建唯一标识,并给这个标识赋值 stack=[],stack是个栈,存储着每个用户请求的对象(数据)RequestContext对象(ctx)
{
唯一标识(__ident_func__):{
stack:[ctx/requestcontext,]//栈
}
}
--------------------------------接下来开始看看执行流程:---------(app)-----------------------------------------------
app.__call__
app.request_class
response_class = Response
app.run()
执行app.__call__方法
app.run() =》run_simple(host, port, self, **options) =》app.__call__
# \flask\app.py
# 主要从这里入手 分析代码
# Flask类(app)
app.py
def __call__(self, environ, start_response):
return self.wsgi_app(environ, start_response)
app.py
def wsgi_app(self, environ, start_response):
#将请求相关的数据environ封装到了RequestContext对象中
#再将对象封装到Local中(每个线程/每个协程独立空间存储)
#request_context对象中有如下:
#ctx.app 当前APP
#ctx.request Request对象(封装请求相关的东西)
#ctx.session
#RequestContext类 ctx.py
ctx = self.request_context(environ)
error = None
try:
try:
#RequestContext.push()->LocalStack.push
#将封装了请求相关的RequestContext对象中的对象添加到LocalStack.push(self)
#_request_ctx_stack = LocalStack()
"""
{
唯一标识(__ident_func__):{
stack:[ctx/requestcontext,]//栈
}
}
"""
# 将ctx通过LocalStack添加到Local中
# 此时ctx中的session中已经有值了
ctx.push()
response = self.full_dispatch_request()
except Exception as e:
error = e
response = self.handle_exception(e)
except:
error = sys.exc_info()[]
raise
return response(environ, start_response)
finally:
if self.should_ignore_error(error):
error = None
#RequestContext.pop()->LocalStack.pop(_request_ctx_stack.pop())
#_request_ctx_stack = LocalStack()
#最后将自己请求在Local中保存的数据清除
ctx.auto_pop(error)
ctx = self.request_context(environ) 记住了!!!
def request_context(self, environ):
#self=app
return RequestContext(self, environ)
ctx就是request_context对象
#request_context对象中有如下属性:
#ctx.app 当前APP
#ctx.request Request对象(封装请求相关的东西)
#ctx.session
ctx.push():
#执行RequestContext.push()->LocalStack.push
#将封装了请求相关的RequestContext对象中的对象添加到LocalStack.push(self)
"""
{
唯一标识(__ident_func__):{
stack:[ctx/requestcontext,]//栈
}
}
"""
--------------------------RequestContext------------ctx.py-------------------------------------
# \flask\ctx.py
# RequestContext类
def __init__(self, app, environ, request=None):
self.app = app
if request is None:
request = app.request_class(environ)
self.request = request
self.url_adapter = app.create_url_adapter(self.request)
self.flashes = None
self.session = None self._implicit_app_ctx_stack = [] self.preserved = False self._preserved_exc = None self._after_request_functions = [] self.match_request() request = app.request_class(environ) #ctx.push
def push(self): 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 = AppContext对象
# app_ctx.app = Flask对象
# app_ctx.g=对象(用于一个周期内要保存的值,相当于全局变量)
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) if hasattr(sys, 'exc_clear'):
sys.exc_clear() #selft 是RequestContext对象 也是app里面的ctx,包含了请求所有数据
#_request_ctx_stack = LocalStack()
#相当于执行LocalStack.push()
#self 就是RequestContext类
_request_ctx_stack.push() 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)
RequestContext对象中有request,session等对象,#ctx.push执行的就是RequestContext.push,其中有这么段代码
_request_ctx_stack.push()
#self 是RequestContext对象 也是app里面的ctx,包含了请求所有数据
#_request_ctx_stack = LocalStack()
#相当于执行LocalStack.push()
#self 就是RequestContext类
_request_ctx_stack.push()
组中执行的是Local中
storage[ident][name]
=
value
最终目的就是把RequestContext对象也就是ctx放到localStack中的唯一标识字典中的stack栈中
以上明白了吗?不明白可以去看看源码。。。。。 现在已经出现了
ctx=RequestContext app LocalStack Local 四个类
globals.py 刚开始启动加载的文件
到这里 请求上下文已经差不多了,每个请求都有一个独立数据存储空间,互补影响彼此。就是把request对象放到RequestContext.request属性里面,然后又把RequestContext对象放到每个线程(请求)或者是协程的一个唯一标识的stack栈中
"""
storage:{
唯一标识(__ident_func__):{
stack:[ctx/requestcontext,]//栈
}
}
"""
视图中执行print(request)发生了什么?
--------------------------request-------------------------------------------------
# \flask\globals.py
# 视图中执行print(request)发生了什么?
# 执行LocalProxy类中的__str__方法 # 在globals.py文件中 (最开始在加载的文件)
# context locals
_request_ctx_stack = LocalStack()
_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')) # 偏函数做了啥?
#partial(_lookup_req_object, 'request') # 函数_lookup_req_object 去LocalStack获取唯一标识栈里面的值(RequestContext对象)
def _lookup_req_object (name):
# _request_ctx_stack = LocalStack()
# 返回的其实就是刚开始塞到每个线程唯一标识里面的那个ctx/requestContext对象
top = _request_ctx_stack.top
if top is None:
raise RuntimeError(_request_ctx_err_msg)
# 到requestContext对象取request属性值 request对象
return getattr(top, name) # LocalProxy类中的__str__方法 最终就是执行_lookup_req_object函数而已
__setattr__ = lambda x, n, v: setattr(x._get_current_object(), n, v) 在LocalStack类中:
@property
def top(self):
"""The topmost item on the stack. If the stack is empty,
`None` is returned.
"""
try:
return self._local.stack[-]
except (AttributeError, IndexError):
return None def _get_current_object(self):
if not hasattr(self.__local, '__release_local__'):
# 执行_lookup_req_object函数
return self.__local()
try:
return getattr(self.__local, self.__name__)
except AttributeError:
raise RuntimeError('no object bound to %s' % self.__name__)
request = LocalProxy(partial(_lookup_req_object, 'request'))
这段代码要仔细看看了
print(request) => LocalProxy。__str__ => LocalProxy.__str__ = lambda x: str(x._get_current_object()) =>( _get_current_object 里面执行了 )self.__local()
LocalProxy.__local 就是传入的偏函数(
partial(_lookup_req_object, 'request')
)
最后执行self.__local() 相当于执行偏函数,返回request对象了
LocalProxy类中
def __init__(self, local, name=None):
#Local = partial(_lookup_req_object, 'request')
#_local = _LocalProxy__local私有字段访问
object.__setattr__(self, '_LocalProxy__local', local)
object.__setattr__(self, '__name__', name)
if callable(local) and not hasattr(local, '__release_local__'):
object.__setattr__(self, '__wrapped__', local) def _get_current_object(self):
if not hasattr(self.__local, '__release_local__'):
return self.__local()
try:
return getattr(self.__local, self.__name__)
except AttributeError:
_request_ctx_stack的应用
# _request_ctx_stack的应用
from flask.globals import _request_ctx_stack
from functools import partial def _lookup_req_object(name):
# name = request
# top= ctx
top = _request_ctx_stack.top
if top is None:
raise RuntimeError('不存在')
# return ctx.request
return getattr(top, name) class Foo(object):
def __init__(self):
self.xxx =
self.ooo = req = partial(_lookup_req_object,'xxx')
xxx = partial(_lookup_req_object,'ooo') # 当前求刚进来时
_request_ctx_stack.push(Foo()) # 使用
# obj = _request_ctx_stack.top
# obj.xxx
v1 = req()
print(v1)
v2 = xxx()
print(v2) # 请求终止,将local中的值移除
_request_ctx_stack.pop()
_request_ctx_stack
上下文
- threading.Local和Flask自定义Local对象
- 请求到来
- ctx = 封装RequestContext(request,session)
- ctx放到Local中
- 执行视图时
- 导入request
- print(request) --> LocalProxy对象的__str__
- request.method --> LocalProxy对象的__getattr__
- request + 1 --> LocalProxy对象的__add__
- 调用 _lookup_req_object函数:去local中将requestContext想获取到,再去requestContext中获取request或session
- 请求结束
- ctx.auto_pop()
- ctx从local中移除。
多app应用
from werkzeug.wsgi import DispatcherMiddleware
from werkzeug.serving import run_simple
from flask import Flask, current_app app1 = Flask('app01') app2 = Flask('app02') @app1.route('/index')
def index():
return "app01" @app2.route('/index2')
def index2():
return "app2" # http://www.oldboyedu.com/index
# http://www.oldboyedu.com/sec/index2
dm = DispatcherMiddleware(app1, {
'/sec': app2,
}) if __name__ == "__main__":
app2.__call__
run_simple('localhost', , dm)
离线脚本:
from flask import Flask,current_app,globals,_app_ctx_stack app1 = Flask('app01')
app1.debug = False # 用户/密码/邮箱
# app_ctx = AppContext(self):
# app_ctx.app
# app_ctx.g app2 = Flask('app02')
app2.debug = True # 用户/密码/邮箱
# app_ctx = AppContext(self):
# app_ctx.app
# app_ctx.g with app1.app_context():# __enter__方法 -> push -> app_ctx添加到_app_ctx_stack.local
# {<greenlet.greenlet object at 0x00000000036E2340>: {'stack': [<flask.ctx.AppContext object at 0x00000000037CA438>]}}
print(_app_ctx_stack._local.__storage__)
print(current_app.config['DEBUG']) with app2.app_context():
# {<greenlet.greenlet object at 0x00000000036E2340>: {'stack': [<flask.ctx.AppContext object at 0x00000000037CA438> ]}}
print(_app_ctx_stack._local.__storage__)
print(current_app.config['DEBUG']) print(current_app.config['DEBUG'])
"""
需求:不用数据库连接池,显示数据库连接
"""
class SQLHelper(object): def open(self):
pass def fetch(self,sql):
pass def close(self):
pass def __enter__(self):
self.open()
return self def __exit__(self, exc_type, exc_val, exc_tb):
self.close() # obj = SQLHelper()
# obj.open()
# obj.fetch('select ....')
# obj.close() with SQLHelper() as obj: # 自动调用类中的__enter__方法, obj就是__enter__返回值
obj.fetch('xxxx')
# 当执行完毕后,自动调用类 __exit__ 方法 # 以后如果遇到:
flask请求上下文的更多相关文章
- Flask(4)- flask请求上下文源码解读、http聊天室单聊/群聊(基于gevent-websocket)
一.flask请求上下文源码解读 通过上篇源码分析,我们知道了有请求发来的时候就执行了app(Flask的实例化对象)的__call__方法,而__call__方法返回了app的wsgi_app(en ...
- flask 请求上下文源码(转)
本篇阅读目录 一.flask请求上下文源码解读 二.http聊天室(单聊/群聊)- 基于gevent-websocket 回到顶部 转:https://www.cnblogs.com/li-li/p/ ...
- flask 请求上下文
一篇引用大牛的 https://www.cnblogs.com/zhaopanpan/p/9457343.html ### 线程安全 ```python# 线程不安全class Foo(object) ...
- Flask请求上下文源码讲解,简单的群聊单聊web
请求上下文流程图 群聊html代码 <!DOCTYPE html> <html lang="en"> <head> <meta chars ...
- flask请求上下文 (转)
本篇阅读目录 一.flask中的CBV 二.werkzeug + 上下文初步解读 三.偏函数和线程安全 回到顶部 一.flask中的CBV 对比django中的CBV,我们来看一下flask中的CBV ...
- flask请求上下文源码分析
一.什么是上下文 每一段程序都有很多外部变量,只有像add这种简单的函数才是没有外部变量的,一旦你的一段程序有了外部变量,这段程序就不完整了,不能独立运行,你为了使他们能运行,就要给所有的外部变量一个 ...
- Flask请求上下文request
- python 全栈开发,Day139(websocket原理,flask之请求上下文)
昨日内容回顾 flask和django对比 flask和django本质是一样的,都是web框架. 但是django自带了一些组件,flask虽然自带的组件比较少,但是它有很多的第三方插件. 那么在什 ...
- flask的请求上下文源码解读
一.flask请求上下文源码解读 通过上篇源码分析( ---Flask中的CBV和上下文管理--- ),我们知道了有请求发来的时候就执行了app(Flask的实例化对象)的__call__方法,而__ ...
随机推荐
- 关于什么是SpringMVC,和SpringMVC基于xml配置、注解配置、纯注解配置
首先我们先要了解一下,什么是SpringMVC? SpringMVC是Spring框架内置的MVC的实现.SpringMVC就是一个Spring内置的MVC子框架,也就是说SpringMVC的相关包都 ...
- nuxtjs中使用路由守卫
在vue中的路由守卫一般是来验证token失效什么的,当然也可以设置权限啦,在nuxtjs中如何使用路由守卫呢,话不多说,直接上代码 在plugins目录下简历route.js export defa ...
- mysql查看当前执行线程_关闭当前的某些线程 show processlist_kill
每个与mysqld的连接都在一个独立的线程里运行,您可以使用SHOW PROCESSLIST语句查看哪些线程正在运行,并使用KILL thread_id语句终止一个线程. 如果您拥有SUPER权限,您 ...
- poj1733(并查集+离散化)
题目大意:有一个长度为n的0,1字符串, 给m条信息,每条信息表示第x到第y个字符中间1的个数为偶数个或奇数个, 若这些信息中第k+1是第一次与前面的话矛盾, 输出k; 思路:x, y之间1的个数为偶 ...
- 记录Js 文本框验证 与 IE兼容性
最近的日常就是将测试小姐姐提交的bug进行修改,想来这种事情还是比较好开展的,毕竟此项目已上线一年多,现在只是一些前端的问题需要改正.实际上手的时候并不是这样,原项目是在谷歌上运行,后来由于要新增一个 ...
- Oarcle 入门之 order by 关键字
order by 关键字 作用:用于对查询结果进行排序 select * from emp where deptno = 20 order by sal asc /desc; 如何排序之升降问题 *用 ...
- 教你如何在win7中安装cygwin64
首先,说说我们为什么要安装cygwin吧,长期在win7下开发的人员可能不习惯使用unix系统,但由于工作问题,你又被逼要在unix环境下开发,那该如何是好啊?但现在你不用再纠结了,因为有cygwin ...
- 【 剑指Offer 1 】数据结构
数据结构是技术面试中的重点,总结以下几种常见的必须熟练掌握数据结构. 数组 字符串 链表 树 栈和队列 数组和字符串是两种最基本的数据结构,连续内存: 链表和树是面试中出现频率最高的: 栈与递归密切相 ...
- Not supported for DML operations
问题原因 缺失@Modifying注解 问题解决 在自定义的修改方法(delete.update)上面除了@Transactional注解和@Query还需要@Modifying注解 Bug重现 or ...
- jQuery validator plugin之Selector
原文 :unchecked Selector Selects all elements that are unchecked. jQuery( ":unchecked" ) Inv ...