十二 .Flask上下文 总结

一 . 上下文管理总结

from flask import Flask,request,session
app = Flask(__name__)
@app.route('/index')
def index():
    # 1. request是LocalProxy对象
    # 2. 对象中有method、执行__getattr__
    print(request.method)
    # request['method']
    # request + 1

    # 1. session是LocalProxy对象
    # 2. LocalProxy对象的__setitem__
    session['x'] = 123

    return "Index"


if __name__ == '__main__':
    app.run()
    # app.__call__
    # app.wsgi_app

"""
第一阶段:请求到来
    将request和Session相关数据封装到ctx=RequestContext对象中。
    再通过LocalStack将ctx添加到Local中。
    __storage__ = {
        1231:{'stack':[ctx(request,session)]}
    }
第二阶段:视图函数中获取request或session
    方式一:直接找LocalStack获取
            from flask.globals import _request_ctx_stack
            print(_request_ctx_stack.top.request.method)
            
    方式二:通过代理LocalProxy(哈哈)获取
            from flask import Flask,request
            print(request.method)
            
"""

https://www.cnblogs.com/Zzbj/p/10207128.html

1. 全局的变量

_request_ctx_stack = LocalStack()
_app_ctx_stack = LocalStack()
current_app = LocalProxy(_find_app)
#local就是我们的partial(_lookup_req_object, "request")
request = LocalProxy(partial(_lookup_req_object, "request"))
session = LocalProxy(partial(_lookup_req_object, "session"))
g = LocalProxy(partial(_lookup_app_object, "g"))

2. 项目请求流程

self.wsgi_app(environ, start_response)源码:
    def wsgi_app(self, environ, start_response):
        #ctx是ResquestContext的对象,里面request,session
        ctx = self.request_context(environ)
        error = None
        try:
            try:
                #就是ctx放到了Local对象
                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)

1. ctx = self.request_context(environ) : environ,请求相关的,ctx现在是包含request,session的RequestContext的对象,源码

1.1 RequestContext(self, environ) self ,是app对象 environ,请求相关的

1.2 RequestContext在实例化的时候的源码:
        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._after_request_functions = []
        # 这个RequestContext对象封装了,request 和seesoin

2. ctx.push()这个ctx是RequestContext,那就执行RequestContext.push方法

2.1 RequestContext.push()的源码
    def push(self):
        #_request_ctx_stack是localStack的对象
        #self是ctx,把self也就ctx放入到local对象里面
        _request_ctx_stack.push(self)
        if self.session is None:
            session_interface = self.app.session_interface
            self.session = session_interface.open_session(self.app, self.request)

            if self.session is None:
                self.session = session_interface.make_null_session(self.app)

        if self.url_adapter is not None:
            self.match_request()
    2.1.1 _request_ctx_stack.push(self)现在的self是ctx
    2.1.2 _request_ctx_stack是LocalStack()的对象
    2.1.3 LocalStack()的push把ctx传过来
    2.1.4 LocalStack()的push方法
        源码:
        #obj是ctx
        def push(self, obj):
            #obj是ctx,requestContext的对象
            rv = getattr(self._local, "stack", None)
            if rv is None:
                # self._local是Local()的对象
                # storage[“线程id或者协程id”][stack] = [ctx,]
                self._local.stack = rv = []
            rv.append(obj)
            return rv

# 最终也就是ctx.push()他的最终目的:把当前的ctx放入到Local()里面

3. response = self.full_dispatch_request() 源码:

 def full_dispatch_request(self):
        #这是服务器第一次请求时候执行的函数
        self.try_trigger_before_first_request_functions()
        try:
            request_started.send(self)
            #执行请求之前所有的函数,并且拿到请求之前的返回值
            rv = self.preprocess_request()
            if rv is None:
                #这个是真正视图函数,如果我的请求之前函数没有返回值才会执行
                rv = self.dispatch_request()
        except Exception as e:
            rv = self.handle_user_exception(e)
        return self.finalize_request(rv)
    
3.1 return self.finalize_request(rv)的源码:
    def finalize_request(self, rv, from_error_handler=False):
        response = self.make_response(rv)
        try:
            #请求之后的函数,after_request
            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

4. 我们的现在已经在2步的时候把我们request已经方法Locald对象中了,我们第三步的任意一个地方都能使用我们的request,session,拿他是怎么获取的?

4.1 我们在flask导入request,这个request是一个全局的变量,我们怎么通过request区分我当前的request对象(environ),我们发现request是LocalProxy的对象
4.2 当我们用全局的request.属性的时候,就会去找LocalProxy的对象,但是我们发现里面根本就没有
    那他一定执行LocalProxy对象的__getattr__方法
4.3 我们现在来看LocalProxy对象的__getattr__方法的源码:
    #name我们要获取属性名
    def __getattr__(self, name):
        if name == "__members__":
            return dir(self._get_current_object())
        #form
        #self._get_current_object()就是ctx里面的request,
        return getattr(self._get_current_object(), name)
    
    4.3.1 通过反射self._get_current_object()对象,来找我们属性,也就是name
       self._get_current_object()的源码:
            def _get_current_object(self):
                if not hasattr(self.__local, "__release_local__"):
                    return self.__local()
                try:
                    #self.__local就实例化传过来的偏函数,
                    return getattr(self.__local, self.__name__)
                except AttributeError:
                    raise RuntimeError("no object bound to %s" % self.__name__)

       4.3.1.1 return getattr(self.__local, self.__name__)那这里self.__local是谁def __init__(self, local, name=None):
                      object.__setattr__(self, "_LocalProxy__local", local)
                self.___local为local
                这个local为实例化的时候传的
                
            4.3.1.1.1 这个实例化的时候的操作
               request = LocalProxy(partial(_lookup_req_object, "request"))
               4.3.1.1的local就是 partial(_lookup_req_object, "request")的地址
            
            4.3.1.1.2 _lookup_req_object的源码:
                #调用的时候 partial(_lookup_req_object, "request")
                #现在的name就是"request"
                def _lookup_req_object(name):
                    # top是当前线程的ctx
                    top = _request_ctx_stack.top
                    if top is None:
                        raise RuntimeError(_request_ctx_err_msg)
                    #找top里面的request
                    # ctx找request
                    return getattr(top, name)
            4.3.1.1.3 我们来看这个_request_ctx_stack.top的top方法
                    def top(self):
                        try:
                            return self._local.stack[-1]
                        except (AttributeError, IndexError):
                            return None
                    # 我们发现这个self._local是Local()对象,这样就把ctx拿到了
原文地址:https://www.cnblogs.com/lovershowtime/p/11750174.html