Flask 请求上下文源码分析(生命周期)

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)

因为ctxRequestContext的实例化对象,所以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 并且把删除的ctxreturn返回
如果又多个值, 标记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就是代理对象,用的就是代理模式
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值