Flask 上下文管理基本流程
当浏览器发送请求到服务器时,在flask内部源码如下
# 第1步,封装session,再次封装请求数据
# ctx = RequestContext(self, environ) # self是app对象,environ请求是相关的原始数据
# ctx.request = Request(environ)
# ctx.session = None
# 第2步,将包含了request/session的ctx对象放到"空调"
{
1232:{ctx:ctx对象}
1231:{ctx:ctx对象}
1211:{ctx:ctx对象}
1111:{ctx:ctx对象}
1261:{ctx:ctx对象}
}
#第3步,接着执行视图函数
#第4步,请求结束,根据当前线程的唯一标记,将“空调”上的数据移除
有关上下文管理的预备知识
- 偏函数
import functools
def index(a1,a2):
return a1 + a2
# 原来的调用方式
# ret = index(1,23)
# print(ret)
# 偏函数,帮助开发者自动传递参数
new_func = functools.partial(index,666) #传入函数名,和预置另一个参数
ret = new_func(1)
print(ret)
- super关键字
"""
class Base(object):
def func(self):
print('Base.func')
class Foo(Base):
def func(self):
# 方式一:根据mro的顺序执行方法
# super(Foo,self).func()
# 方式二:主动执行Base类的方法
# Base.func(self)
print('Foo.func')
obj = Foo()
obj.func()
"""
####################################
class Base(object):
def func(self):
super(Base, self).func()
print('Base.func')
class Bar(object):
def func(self):
print('Bar.func')
class Foo(Base,Bar):
pass
# 示例一
obj = Foo()
obj.func()
print(Foo.__mro__)
# 示例二
# obj = Base()
# obj.func()
- 面向对象中特殊的方法
class Foo(object):
def __init__(self):
# self.storage = {} #该句会调用__setattr__方法,但self.storage还没创建,会报错
object.__setattr__(self,'storage',{}) #调用父类方法中的__setattr__方法
def __setattr__(self, key, value):
print(key,value,self.storage)
obj = Foo()
obj.xx = 123 #调用Foo类 __setattr__方法
- 实现栈结构
class Stack(object):
def __init__(self):
self.data = []
def push(self,val):
self.data.append(val)
def pop(self):
return self.data.pop()
def top(self):
return self.data[-1]
_stack = Stack()
_stack.push('佳俊')
_stack.push('咸鱼')
print(_stack.pop())
print(_stack.pop())
- local类,开辟内存空间
"""
{
1232:{k:v}
}
"""
try:
from greenlet import getcurrent as get_ident
except:
from threading import get_ident
"""
class Local(object):
def __init__(self):
object.__setattr__(self,'storage',{})
def __setattr__(self, key, value):
ident = get_ident()
if ident not in self.storage:
self.storage[ident] = {key:value}
else:
self.storage[ident][key] = value
def __getattr__(self, item):
ident = get_ident()
if ident in self.storage:
return self.storage[ident].get(item)
"""
#flask 源码部分也有local类
class Local(object):
__slots__ = ('__storage__', '__ident_func__')
def __init__(self):
# __storage__ = {1231:{'stack':[]}}
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)
obj = Local()
obj.stack = []
obj.stack.append('佳俊')
obj.stack.append('咸鱼')
print(obj.stack)
print(obj.stack.pop())
print(obj.stack)
- localstack类,帮助维护loacl对象中的列表,成栈结构
import functools
try:
from greenlet import getcurrent as get_ident
except:
from threading import get_ident
class Local(object):
__slots__ = ('__storage__', '__ident_func__')
def __init__(self):
# __storage__ = {1231:{'stack':[]}}
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):
# name=stack
# 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)
"""
__storage__ = {
12312: {stack:[ctx(session/request) ,]}
}
"""
# obj = Local()
# obj.stack = []
# obj.stack.append('佳俊')
# obj.stack.append('咸鱼')
# print(obj.stack)
# print(obj.stack.pop())
# print(obj.stack)
class LocalStack(object):
def __init__(self):
self._local = Local()
def push(self,value):
rv = getattr(self._local, 'stack', None) # self._local.stack =>local.getattr
if rv is None:
self._local.stack = rv = [] # self._local.stack =>local.setattr
rv.append(value) # self._local.stack.append(666)
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) == 1:
return stack[-1]
else:
return stack.pop()
def top(self):
try:
return self._local.stack[-1]
except (AttributeError, IndexError):
return None
class RequestContext(object):
def __init__(self):
self.request = "xx"
self.session = 'oo'
_request_ctx_stack = LocalStack()
_request_ctx_stack.push(RequestContext())
def _lookup_req_object(arg):
ctx = _request_ctx_stack.top()
return getattr(ctx,arg) # ctx.request / ctx.session
request = functools.partial(_lookup_req_object,'request')
session = functools.partial(_lookup_req_object,'session')
print(request())
print(session())
- slots用法,限制对象访问变量
class Foo(object):
__slots__ = ('name',) #限制类的对象,只能外部调用name变量
def __init__(self):
self.name = 'alex'
# self.age = 18
obj = Foo()
print(obj.name)
# print(obj.age) #会报错
flask 上下文管理流程
先看一个简单的flask程序
from flask import Flask
app = Flask(__name__)
@app.route('/index')
def index():
print("index")
return '...'
if __name__ == "__main__":
app.run()
(1)上下文管理:request
执行app.run()
后,会执行app.__call__
方法,此时又会调用app.wsgi_app
,大致调用流程如下
#第一步
wsgi
#第二步
ctx = ReuqestContext(session,request) #将请求数据和session封装在ctx对象中
ctx.push() #将ctx对象加入到__storage__字典中
#第三步
LocalStack 把ctx对象添加到local中
#第四步
Local里存放的数据为字典类型
__storage__={
1321:{stack:[ctx,]}
}
(2)上下文管理:session
大致步骤与上下文管理request相同,只有获取session并给session赋值有区别
第一步:
wsgi
第二步:
ctx = ReuqestContext(session=None,request)
ctx.push()
第三步:
LocalStack,把ctx对象添加到local中
第四步:
Local,为字典中的元素开辟独立空间
__storage__={
1321:{stack:[ctx,]}
}
第五步:
获取ctx中的session,给session赋值(从cookie中读取数据) =>通过调用 open_session给session赋值
上下文管理总结
- 请求到来之后wsgi会触发
__call__
方法,由__call__
方法再次调用wsgi_app
方法 - 在wsgi_app方法中:
-
首先将
请求相关数据+空session
封装到一个RequestContext
对象中,即:ctx
。 -
将
ctx
交给LocalStack
对象,再由LocalStack
将ctx
添加到Local
中,Local
结构如下:
storage = {
1231:{stack:[ctx,] }
} -
根据请求中的cookie中提取名称为
sessionid
对应的值,对cookie进行解密+反序列化,再次赋值给ctx
中的session
-
执行视图函数
-
把
session
中的数据再次写入到cookie中。 -
将
ctx
删除
-
- 结果返回给用户浏览器
- 断开socket连接