回顾
在前一篇中,我们已经知道Flask是通过把用户的请求信息放入到线程栈中进行存储。例如你的线程id是9528,你进来后,生成一个stack,stack存放的就是9528的请求信息。你是9529、9531等依次对应,所有用户的请求信息都是这样存储。
# {9528:{stack :[ctx(request,session)]}} # 存放的是线程id为9528的请求信息
开始
接着前一篇继续,在push中,我们知道了Flask的上下文存储机制:
def push(self, obj): # obj= ctx = request_context=(request,session)
"""Pushes a new item to the stack"""
# 这个self往上找,self._local = Local=[{},get_ident]
rv = getattr(self._local, "stack", None)
if rv is None:
self._local.stack = rv = [] # {9528:{stack :[]}}
# _local对象.stack执行__setattr__方法
rv.append(obj)
# {9528:{stack :[ctx(request,session)]}}
return rv
往回翻到LocalStack
这里:
# context locals
_request_ctx_stack = LocalStack() # {9528:{stack :[ctx(request,session)]}}
# _request_ctx_stack 请求上下文栈,实例化LocalStack(),执行__init__方法
# _request_ctx_stack._local = Local=[{},get_ident]
#最后变为: [ {9528:{stack :[ctx(request,session)]}},get_ident]
知道了LocalStack
存储的值是[ {9528:{stack :[ctx(request,session)]}},get_ident]
这种形式的。再往下看:
_app_ctx_stack = LocalStack()
current_app = LocalProxy(_find_app)
request = LocalProxy(partial(_lookup_req_object, "request")) #partial 偏函数
session = LocalProxy(partial(_lookup_req_object, "session"))
g = LocalProxy(partial(_lookup_app_object, "g"))
点击进入到_lookup_req_object
中:
def _lookup_req_object(name): # name = request
top = _request_ctx_stack.top
# _request_ctx_stack._local = [ {9528:{stack :[ctx(request,session)]}},get_ident]
# 在实例内部别忘了("__storage__", "__ident_func__")与上面数组的小标是对应的
if top is None:
raise RuntimeError(_request_ctx_err_msg)
return getattr(top, name)
进入到top
中:
def top(self): # self._local = [ {9528:{stack :[ctx(request,session)]}},get_ident]
"""The topmost item on the stack. If the stack is empty,
`None` is returned.
"""
try:
return self._local.stack[-1]
# 对象.stack[-1],调用_local对象中的__getattr__方法
except (AttributeError, IndexError):
return None
进入到_local
对象中,找到__getattr__
方法:
def __getattr__(self, name): # name = stack
try:
# _request_ctx_stack._local = [ {9528:{stack :[ctx(request,session)]}},get_ident]
# 在实例内部别忘了("__storage__", "__ident_func__")与上面数组的小标是对应的
return self.__storage__[self.__ident_func__()][name]
# self.__ident_func__()获取线程id9528,name就是stack
# 拿到[ctx(request,session)] ,然后返回
except KeyError:
raise AttributeError(name)
回到top
这里:
def top(self): # self._local = [ {9528:{stack :[ctx(request,session)]}},get_ident]
"""The topmost item on the stack. If the stack is empty,
`None` is returned.
"""
try:
return self._local.stack[-1] # 返回 ctx (request,session)
# 对象.stack[-1],调用_local对象中的__getattr__方法
except (AttributeError, IndexError):
return None
再回到_lookup_req_object
这里:
def _lookup_req_object(name): # name = request
top = _request_ctx_stack.top # 拿到 ctx (request,session)
# _request_ctx_stack._local = [ {9528:{stack :[ctx(request,session)]}},get_ident]
# 在实例内部别忘了("__storage__", "__ident_func__")与上面数组的小标是对应的
if top is None:
raise RuntimeError(_request_ctx_err_msg)
return getattr(top, name) # # name = request
回到partial
这里:
_app_ctx_stack = LocalStack()
current_app = LocalProxy(_find_app)
request = LocalProxy(partial(_lookup_req_object, "request")) #partial 偏函数
# 取出request对象
session = LocalProxy(partial(_lookup_req_object, "session"))
g = LocalProxy(partial(_lookup_app_object, "g"))
进入到LocalProxy
中:
def __init__(self, local, name=None): # local = partial >可得> request
object.__setattr__(self, "_LocalProxy__local", local) # partial >>> request
# __setattr__为LocalProxy这个类添加一个__local属性
object.__setattr__(self, "__name__", name)
# local是partial偏函数,可以被执行,local中没有__release_local__这个属性
if callable(local) and not hasattr(local, "__release_local__"): # True
# "local" is a callable that is not an instance of Local or
# LocalManager: mark it as a wrapped function.
object.__setattr__(self, "__wrapped__", local) # partial >>> request
完了,到这里就结束了?别着急,还记得在Flask中可以用request.method
去判断前端传送数据所用的方法吧。.method
实质是调用了request
对象的__getattr__
方法,由前面可知,request
对象其实就是LocalProxy
对象,那我们进LocalProxy
对象去找找__getattr__
方法:
def __getattr__(self, name): # 例如 .method 则name = method
if name == "__members__":
return dir(self._get_current_object())
return getattr(self._get_current_object(), name) # name = method
进入到_get_current_object
中:
def _get_current_object(self):
"""Return the current object. This is useful if you want the real
object behind the proxy at a time for performance reasons or because
you want to pass the object into a different context.
"""
if not hasattr(self.__local, "__release_local__"): # True
return self.__local() # 往上翻发现,返回的就是request对象
try:
return getattr(self.__local, self.__name__)
except AttributeError:
raise RuntimeError("no object bound to %s" % self.__name__)
回到__getattr__
这里,一切都明白了:
def __getattr__(self, name): # 例如 .method 则name = method
if name == "__members__":
return dir(self._get_current_object())
return getattr(self._get_current_object(), name) # name = method
# self._get_current_object() 返回一个request对象
请求中的所有数据都是通过反射的方式,从request中获取的
总结
Flask上下文管理全流程解析: