flask的上下文管理

上下文管理的几个阶段

复制代码
第一阶段:将ctx(request,session)放到“空调”上(Local对象)
           
第二阶段:视图函数导入:request/session 

第三阶段:请求处理完毕
                - 获取session并保存到cookie
                - 将ctx删除
复制代码

源码分析

首先当请求来时,我们会执行app.run方法,这个方法其实执行的是run_simple方法,而run_simple实际上执行的是app()

一个对象加括号执行的是__call__方法

这里的__call__实际上执行的是self.wsgi_app

在这个方法中首先定义了一个ctx,这是一个什么呢,我们来看看self.request_context(environ)

可以看到self.request_context(environ)返回的是一RequestContext类的对象,所以ctx是这个类的一个对象,我们再看看RequestContext类的内容

在这个类的init方法中我们定义了request和session,其中request=app.request_class,这个app.request_class也是个类

定义完ctx对象后我们要执行这个对象的push方法

在这个方法中我们看到有两个参数_app_ctx_stack和_request_ctx_stack,这两个参数是什么呢

可以看到他们都是LocalStack类的对象,并且都是flask的全局变量,也就是导入时就有的,所以在每一个请求中用到的都是同一个对象单例

在这个类中还实例了一个Local的对象,所以_app_ctx_stack和_request_ctx_stack对象各自都有一个_local对象

这个Local类的init方法中定义了一个__storage__的空字典,还定义了__ident_func__这个每个线程都不同的参数,这个__storage__的空字典就是用来存放每个请求过来时生成的ctx对象的,不同的请求,不同的线程通过__ident_func__区分

有了上面定义的_app_ctx_stack和_request_ctx_stack参数后,接着执行了他们的push方法

这里首先通过getattr获取self._local的stack属性,如果没有,则进行设置self._local.stack = rv = []

而通过.设置的过程其实就是执行self._local对象的__setattr__方法

在这个方法中根据不同的线程号,在self.__storage__字典中创建了不同的键值对,键就是每个请求线程对应的get_ident值,值为一个字典,字典的键为stark,值为包含每个请求对应的ctx对象的字典,这个ctx对象中还包含了request和session

执行完push后,又进行session操作,这里就不再讨论session操作,可以参考前面博客中关于session源码的解释,然后会执行我们的视图函数

当然在执行视图函数之前还会执行before_request

在视图函数中我们可以通过导入request和session等来进行使用,那么导入并使用的过程是怎样的呢,我们以request为例

可以看到我们导入的request其实是一个LocalProxy类的对象,实例化过程中传了一个偏函数_lookup_req_object为对象,这个函数的参数为request

 

在LocalProxy类实例化时,我们执行了object.__setattr__(self, '_LocalProxy__local', local),这其实是定义了这个类的一个隐藏属性,等同于__local=local(_lookup_req_object(request))

那么这个_lookup_req_object函数都做了什么呢

他其实是从_request_ctx_stack.top中获取name属性,这个name就是我们传的request

 

而这个top对应的其实就是我们当前线程的ctx对象,所以_lookup_req_object函数得到的就是ctx.request,也就是我们封装好的request

所以当我们执行request.method等指令时,其实执行的是LocalProxy类中的_getattr__方法

在这个方法中我们获取到到的是self._get_current_object()的name属性,name就是我们点后面的值(如method),而self._get_current_object()是什么呢

可以看到它返回的其实是self.__local()而我们上面提到了self.__local=_lookup_req_object函数,加括号执行就得到了我们的request对象,所以我们就得到了request对象的method方法

session和request同理,我们还注意到其实我们还可以导入current_app和g,他们都是在_app_ctx_stack对应的local中的

current_app导入的就是当前的app对象

而g是我们可以自己设置的,如当请求来时,我们可以先判断用户的权限,将相应的权限写入到g中,然后在视图函数中就可以直接从g中取到用户的权限了

 

执行完成视图函数后,我们接着执行后续的response操作,可以参考session的源码

最后我们要执行一个ctx.auto_pop(error)

这个操作做了什么呢

执行了self.pop也就是ctx.pop

其实就是将local字典中对应的ctx对象删除了,所以我们上面提到的除了session,其它的request,g等的生命周期其实就是一个请求的周期

 

 

原文地址:https://www.cnblogs.com/xyhh/p/10860400.html