Flask启动必须调用
app.run()
对象实例化调用方法,执行类内部的__call__方法
def __call__(self, environ, start_response):
return self.wsgi_app(environ, start_response)
查看到Flask请求的整个流程概要wsgi_app类
def wsgi_app(self, environ, start_response):
#environ是个字典,把它包装成了request对象。
ctx = self.request_context(environ) # <<<---------------标记C
#ctx是Request_content对象,里面包含了当次请求的request,session;
#目前request是当前请求的request(method,args,forms)
error = None
try:
try:
ctx.push() # <<<---------- 以下所有代码皆在分析这条代码产生的逻辑
response = self.full_dispatch_request()
except Exception as e:
error = e
response = self.handle_exception(e)
except: # noqa: B001
error = sys.exc_info()[1]
raise
return response(environ, start_response)
finally:
if self.should_ignore_error(error):
error = None
ctx.auto_pop(error)
---------------------------------------------------------------------------ctx.push()产生的逻辑开始-----------------------------------------------------------------------
进入ctx对象
class Flask(_PackageBoundObject):
def request_context(self, environ):
return RequestContext(self, environ)
进入 RequestContext
类
class RequestContext(object):
def __init__(self, app, environ, request=None, session=None):
self.app = app
if request is None:
request = app.request_class(environ)
self.request = request
self.url_adapter = None
try:
self.url_adapter = app.create_url_adapter(self.request)
except HTTPException as e:
self.request.routing_exception = e
self.flashes = None
self.session = session
self._implicit_app_ctx_stack = []
self.preserved = False
self._preserved_exc = None
self._after_request_functions = []
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 = 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()
_request_ctx_stack.push(self) # <<<---------------标记A
if self.session is None:
session_interface = self.app.session_interface
#获得session属性
self.session = session_interface.open_session(self.app, self.request) # <<<---------------标记B
if self.session is None:
self.session = session_interface.make_null_session(self.app)
if self.url_adapter is not None:
self.match_request()
在RequestContext类
下有一个push
方法(标记A),作用是把发过来的session压入self.session
中(标记B),
在wsgi_app类
中有ctx = self.request_context(environ) :
,最终的结果返回给ctx (标记C)
因为ctx
是RequestContext
的实例化对象,所以ctx.push
本质上调用的是RequestContext
中的psuh
方法,那么进入_request_ctx_stack
_request_ctx_stack = LocalStack() # <<<---------------标记D
_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"))
原来_request_ctx_stack
是一个全局变量:LocalStack()
实例化的对象之一(标记D)
只要Flask一运行就有了 ,_request_ctx_stack.
的push
方法把self
传过去了,此时self
就是ctx
进入LocalStack
类,找到它的push
方法如下,其中self
代表的就是ctx
对象
class LocalStack(object):
def __init__(self):
self._local = Local()
def __release_local__(self):
self._local.__release_local__()
def _get__ident_func__(self):
return self._local.__ident_func__
def _set__ident_func__(self, value):
object.__setattr__(self._local, "__ident_func__", value)
__ident_func__ = property(_get__ident_func__, _set__ident_func__)
del _get__ident_func__, _set__ident_func__
def __call__(self):
def _lookup():
rv = self.top
if rv is None:
raise RuntimeError("object unbound")
return rv
return LocalProxy(_lookup)
def push(self, obj): # <<<---------------标记D
rv = getattr(self._local, "stack", None)
if rv is None:
self._local.stack = rv = []
rv.append(obj)
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()
@property
def top(self):
try:
return self._local.stack[-1]
except (AttributeError, IndexError):
return None
单独看LocalStack
类的push源码
def push(self, obj):
"""Pushes a new item to the stack"""
rv = getattr(self._local, "stack", None) #<<<--------标记E 一开始取值肯定取不到,原因即查看下面的`Local类`的实例化源码
if rv is None:
self._local.stack = rv = [] #多加了一次引用,不同线程可以操作自己 [] ,每次请求---`ctx`都独占一块内存区域
rv.append(obj)
return rv
补充:getattr
方法:getattr(object, name[, default])
其中 _local
肯定是ctx
对象生成的时候创建的,
进入看了下_local
就是LocalStack类
实例化时候生成的一个属性
class LocalStack(object):
def __init__(self):
self._local = Local()
这个属性就是Local类
的实例化对象,以下是Local类
的源码,这里就是给每一个请求分配一个id值的类
class Local(object):
__slots__ = ("__storage__", "__ident_func__")
def __init__(self):
object.__setattr__(self, "__storage__", {}) #<<<--------标记E,storage置空
object.__setattr__(self, "__ident_func__", get_ident)
def __iter__(self):
return iter(self.__storage__.items())
def __call__(self, proxy):
"""Create a proxy for a name."""
return LocalProxy(self, proxy)
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)
---------------------------------------------------------------------------ctx.push()产生的逻辑结束-----------------------------------------------------------------------
-------------------------------------------------------------------------ctx.auto_pop()产生的逻辑开始-------------------------------------------------------------------
进入ctx对象
class Flask(_PackageBoundObject):
def request_context(self, environ):
return RequestContext(self, environ)
进入 RequestContext
类
class RequestContext(object):
def __init__(self, app, environ, request=None, session=None):
self.app = app
if request is None:
request = app.request_class(environ)
self.request = request
self.url_adapter = None
try:
self.url_adapter = app.create_url_adapter(self.request)
except HTTPException as e:
self.request.routing_exception = e
self.flashes = None
self.session = session
self._implicit_app_ctx_stack = []
self.preserved = False
self._preserved_exc = None
self._after_request_functions = []
def auto_pop(self, exc):
if self.request.environ.get("flask._preserve_context") or (
exc is not None and self.app.preserve_context_on_exception
):
self.preserved = True
self._preserved_exc = exc
else:
self.pop(exc) #<<<--------标记F
def pop(self, exc=_sentinel):
app_ctx = self._implicit_app_ctx_stack.pop()
try:
clear_request = False
if not self._implicit_app_ctx_stack:
self.preserved = False
self._preserved_exc = None
if exc is _sentinel:
exc = sys.exc_info()[1]
self.app.do_teardown_request(exc)
if hasattr(sys, "exc_clear"):
sys.exc_clear()
request_close = getattr(self.request, "close", None)
if request_close is not None:
request_close()
clear_request = True
finally:
rv = _request_ctx_stack.pop() #<<<--------标记G
if clear_request:
rv.request.environ["werkzeug.request"] = None
if app_ctx is not None:
app_ctx.pop(exc)
assert rv is self, "Popped wrong request context. (%r instead of %r)" % (rv,self,)
其中auto_pop
方法最终调用了pop
方法(标记F)
在pop
方法中,又调用了_request_ctx_stack.pop()
,上文提到过_request_ctx_stack.push(self)
是一个全局变量:LocalStack()
实例化的对象之一
_request_ctx_stack = LocalStack() # <<<---------------标记D
_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"))
上文同样也分析过LocalStack
#########这里删除了部分源码############
class LocalStack(object):
def __init__(self):
self._local = Local()
def __call__(self):
def _lookup():
rv = self.top
if rv is None:
raise RuntimeError("object unbound")
return rv
return LocalProxy(_lookup)
def push(self, obj):
rv = getattr(self._local, "stack", None)
if rv is None:
self._local.stack = rv = []
rv.append(obj)
return rv
def pop(self):
stack = getattr(self._local, "stack", None) #<<<--------标记H
if stack is None:
return None
elif len(stack) == 1:
release_local(self._local) #<<<--------标记I
return stack[-1]
else:
return stack.pop() #<<<--------标记J
#########这里删除了部分源码############
标记H
值得形式就是stack = [ctx1,ctx2,...]
标记I
删除ctx
并且把删除的ctx
return返回
如果又多个值, 标记J
那么就从最后一个位置pop
掉一个值并且return返回
-------------------------------------------------------------------------ctx.auto_pop()产生的逻辑结束-------------------------------------------------------------------
---------------------------------------------------------------- self.full_dispatch_request()产生的逻辑开始--------------------------------------------------------------
def full_dispatch_request(self):
self.try_trigger_before_first_request_functions() #<---------标记K
try:
request_started.send(self)
rv = self.preprocess_request() #<---------标记L
if rv is None:
rv = self.dispatch_request() #<---------标记N
except Exception as e:
rv = self.handle_user_exception(e)
return self.finalize_request(rv) #<---------标记N
进入try_trigger_before_first_request_functions
标记K
def try_trigger_before_first_request_functions(self):
if self._got_first_request:
return
with self._before_request_lock:
if self._got_first_request:
return
for func in self.before_first_request_funcs: #请求扩展相关中的源码,只执行当此请求中的一次。
func()
self._got_first_request = True #设定为Trueyi'hou 其中的函数下次就不走了
查看请求扩展的装饰器@app.before_first_request
,进入代码。
def before_first_request(self, f):
self.before_first_request_funcs.append(f)
return f
把定义的函数追加到before_first_request_funcs
列表中了
进入标记L
,
def preprocess_request(self):
bp = _request_ctx_stack.top.request.blueprint
funcs = self.url_value_preprocessors.get(None, ())
if bp is not None and bp in self.url_value_preprocessors:
funcs = chain(funcs, self.url_value_preprocessors[bp])
for func in funcs:
func(request.endpoint, request.view_args)
funcs = self.before_request_funcs.get(None, ())
if bp is not None and bp in self.before_request_funcs:
funcs = chain(funcs, self.before_request_funcs[bp])
for func in funcs:
rv = func()
if rv is not None: #<<<------------------标记
return rv
标记M
一旦有返回结果就return rv ,在N
处进行真正的路由分发
def finalize_request(self, rv, from_error_handler=False):
response = self.make_response(rv) #返回response
try:
response = self.process_response(response)
request_finished.send(self, response=response)
except Exception:
if not from_error_handler:
raise
self.logger.exception(
"Request finalizing failed with an error while handling an error"
)
return response
---------------------------------------------------------------- self.full_dispatch_request()产生的逻辑结束--------------------------------------------------------------
-------------------------分析request、session打印出来的类一致,打印结果不一致,重写了__str__方法的探究逻辑结束-----------------------------
开始分析request
_request_ctx_stack = LocalStack() # <<<---------------标记D
_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"))
进入LocalProxy
,并查看其源码【补充:这里用到了代理模式】
#########这里删除了部分源码############
def __init__(self, local, name=None):
object.__setattr__(self, "_LocalProxy__local", local) # <---------------------标记K
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:
raise RuntimeError("no object bound to %s" % self.__name__)
@property
def __dict__(self):
try:
return self._get_current_object().__dict__
except RuntimeError:
raise AttributeError("__dict__")
def __repr__(self):
try:
obj = self._get_current_object()
except RuntimeError:
return "<%s unbound>" % self.__class__.__name__
return repr(obj)
def __getattr__(self, name):
if name == "__members__":
return dir(self._get_current_object())
return getattr(self._get_current_object(), name)
def __setitem__(self, key, value):
self._get_current_object()[key] = value
def __delitem__(self, key):
del self._get_current_object()[key]
__setattr__ = lambda x, n, v: setattr(x._get_current_object(), n, v)
__delattr__ = lambda x, n: delattr(x._get_current_object(), n)
__str__ = lambda x: str(x._get_current_object())
__lt__ = lambda x, o: x._get_current_object() < o
__le__ = lambda x, o: x._get_current_object() <= o
__eq__ = lambda x, o: x._get_current_object() == o
__ne__ = lambda x, o: x._get_current_object() != o
__gt__ = lambda x, o: x._get_current_object() > o
__ge__ = lambda x, o: x._get_current_object() >= o
#########这里删除了部分源码############
标记K
用到了偏函数
的概念,因为内部重写了__setattr__
方法,如果不这么做的话会出现递归。
#偏函数的第二个部分(可变参数),按原有函数的参数顺序进行补充,参数将作用在原函数上,最后偏函数返回一个新函数
from functools import partial
def test(a,b,c,d):
return a+b+c+d
tes=partial(test,1,2)
print(tes(3,4))
直接看到__str__或__repr__是怎么写的,看下源码
def __repr__(self):
try:
obj = self._get_current_object()
except RuntimeError:
return "<%s unbound>" % self.__class__.__name__
return repr(obj) # <---------------------标记O
查看_get_current_object
方法
def _get_current_object(self):
if not hasattr(self.__local, "__release_local__"):
return self.__local()
try:
return getattr(self.__local, self.__name__)
except AttributeError:
raise RuntimeError("no object bound to %s" % self.__name__)
def __init__(self, local, name=None):
object.__setattr__(self, "_LocalProxy__local", local) # <---------------------标记P
object.__setattr__(self, "__name__", name)
可以看到函数体内部对__local
进行了一次判定,这个__local
是一个隐藏属性,这个__local
就是类初始化的时候,那个偏函数传的_LocalProxy__local
,查看标记P
,注释:类外部调用隐藏属性时用_类名__属性名
此时我们看下偏函数内部的函数写了什么
globals.py
request = LocalProxy(partial(_lookup_req_object, "request"))
session = LocalProxy(partial(_lookup_req_object, "session"))
def _lookup_req_object(name):
top = _request_ctx_stack.top # <---------------------标记Q
if top is None:
raise RuntimeError(_request_ctx_err_msg)
return getattr(top, name)
查看以下top
做了什么事,返回了一开始放置的ctx对象
def top(self):
try:
return self._local.stack[-1] # <---------------------标记R
except (AttributeError, IndexError):
return None
标记K
返回了一个ctx
对象并且赋值给了标记Q,top
,并且在_lookup_req_object
中用getattr
取得ctx
–这里赋值给了top
的request对象。
那么这里就返回了一个request
对象,并且把request
对象以字符串形式返回了标记R
,所以print 打印request
时,我们看到的是request
对象的__str__
方法。
开始分析request.method与request.args等等
会调用对象内部的getattr
方法
class LocalProxy(object):
def __getattr__(self, name):
if name == "__members__":
return dir(self._get_current_object())
return getattr(self._get_current_object(), name)
上面的源码就是在request
对象里面找name
总结:
请求上下文执行流程(ctx):
-0 flask项目一启动,有6个全局变量
-_request_ctx_stack:LocalStack对象
-_app_ctx_stack :LocalStack对象
-request : LocalProxy对象
-session : LocalProxy对象
-1 请求来了 app.__call__()---->内部执行:self.wsgi_app(environ, start_response)
-2 wsgi_app()
-2.1 执行:ctx = self.request_context(environ):返回一个RequestContext对象,并且封装了request(当次请求的request对象),session
-2.2 执行: ctx.push():RequestContext对象的push方法
-2.2.1 push方法中中间位置有:_request_ctx_stack.push(self),self是ctx对象
-2.2.2 去_request_ctx_stack对象的类中找push方法(LocalStack中找push方法)
-2.2.3 push方法源码:
def push(self, obj):
#通过反射找self._local,在init实例化的时候生成的:self._local = Local()
#Local()flask封装的支持线程和协程的local对象
# 一开始取不到stack,返回None
rv = getattr(self._local, "stack", None)
if rv is None:
#走到这,self._local.stack=[],rv=[]
self._local.stack = rv = []
# 把ctx放到了列表中
#self._local={'线程id1':{'stack':[ctx,]},'线程id2':{'stack':[ctx,]},'线程id3':{'stack':[ctx,]}}
rv.append(obj)
return rv
-3 如果在视图函数中使用request对象,比如:print(request)
-3.1 会调用request对象的__str__方法,request类是:LocalProxy
-3.2 LocalProxy中的__str__方法:lambda x: str(x._get_current_object())
-3.2.1 内部执行self._get_current_object()
-3.2.2 _get_current_object()方法的源码如下:```
def _get_current_object(self):
if not hasattr(self.__local, "__release_local__"):
#self.__local() 在init的时候,实例化的,在init中:object.__setattr__(self, "_LocalProxy__local", local)
# 用了隐藏属性
#self.__local 实例化该类的时候传入的local(偏函数的内存地址:partial(_lookup_req_object, "request"))
#加括号返回,就会执行偏函数,也就是执行_lookup_req_object,不需要传参数了
#这个地方的返回值就是request对象(当此请求的request,没有乱)
return self.__local()
try:
return getattr(self.__local, self.__name__)
except AttributeError:
raise RuntimeError("no object bound to %s" % self.__name__)
~
-3.2.3 _lookup_req_object函数源码如下:
def _lookup_req_object(name):
#name是'request'字符串
#top方法是把第二步中放入的ctx取出来,因为都在一个线程内,当前取到的就是当次请求的ctx对象
top = _request_ctx_stack.top
if top is None:
raise RuntimeError(_request_ctx_err_msg)
#通过反射,去ctx中把request对象返回
return getattr(top, name)
-3.2.4 所以:print(request) 实质上是在打印当此请求的request对象的__str__
-4 如果在视图函数中使用request对象,比如:print(request.method):实质上是取到当次请求的reuquest对象的method属性
-5 最终,请求结束执行: ctx.auto_pop(error),把ctx移除掉
其他的东西:
-session:
-请求来了opensession
-ctx.push()---->也就是RequestContext类的push方法的最后的地方:
if self.session is None:
#self是ctx,ctx中有个app就是flask对象, self.app.session_interface也就是它:SecureCookieSessionInterface()
session_interface = self.app.session_interface
self.session = session_interface.open_session(self.app, self.request)
if self.session is None:
#经过上面还是None的话,生成了个空session
self.session = session_interface.make_null_session(self.app)
-请求走了savesession
-response = self.full_dispatch_request() 方法内部:执行了before_first_request,before_request,视图函数,after_request,savesession
-self.full_dispatch_request()---->执行:self.finalize_request(rv)-----》self.process_response(response)----》最后:self.session_interface.save_session(self, ctx.session, response)
-请求扩展相关
before_first_request,before_request,after_request依次执行
-flask有一个请求上下文,一个应用上下文
-ctx:
-是:RequestContext对象:封装了request和session
-调用了:_request_ctx_stack.push(self)就是把:ctx放到了那个位置
-app_ctx:
-是:AppContext(self) 对象:封装了当前的app和g
-调用 _app_ctx_stack.push(self) 就是把:app_ctx放到了那个位置
-g是个什么鬼?
专门用来存储用户信息的g对象,g的全称的为global
g对象在一次请求中的所有的代码的地方,都是可以使用的
-代理模式
-request和session就是代理对象,用的就是代理模式